Skip to content

Commit 1cb1d42

Browse files
committed
+
1 parent c8a3350 commit 1cb1d42

File tree

1 file changed

+254
-0
lines changed

1 file changed

+254
-0
lines changed

_posts/2025-05-25-sled_template.md

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
---
2+
layout: post
3+
title: "Rust sled template"
4+
summary: "러스트 임베디드 데이터베이스 sled 템플릿"
5+
author: eveheeero
6+
date: '2025-05-25 04:26:00 +0900'
7+
category: ['etc', 'rust']
8+
tags: rust
9+
thumbnail: /assets/img/posts/default.png
10+
keywords: rust, sled, template
11+
usemathjax: false
12+
permalink: /blog/rust-sled-template/
13+
---
14+
15+
16+
## 배경
17+
18+
사이드 프로젝트 중 sled 관련 공통 모듈을 만들어 공유하게 되었습니다.
19+
20+
## 요약
21+
22+
- `str``Uuid`를 통해 키 이용 가능
23+
24+
## 소스 코드
25+
26+
```rust
27+
// serde = { version = "1.0.152", features = ["derive"] }
28+
// serde_json = "1.0.91"
29+
// sled = "0.34.7"
30+
// flate2 = "1.1.1"
31+
// uuid = { version = "1.17.0", features = ["fast-rng", "v4"]
32+
use serde::{Serialize, de::DeserializeOwned};
33+
use sled::IVec;
34+
use std::{io::Write, sync::LazyLock};
35+
use uuid::Uuid;
36+
37+
static DB: LazyLock<sled::Db> = LazyLock::new(|| sled::open("db").unwrap());
38+
39+
pub struct DbRoot;
40+
41+
pub struct DbTree {
42+
pub inner: sled::Tree,
43+
}
44+
45+
pub trait DbReturnTrait: DbGet + DbRaw {
46+
fn set<K: TreeKey, I: Serialize + ?Sized, R: DeserializeOwned>(
47+
&self,
48+
key: K,
49+
value: &I,
50+
) -> Result<Option<R>, sled::Error> {
51+
let result = self.set_raw(key, value)?;
52+
Ok(result.map(|x| inflate_deserialize(&x)))
53+
}
54+
fn remove<K: TreeKey, R: DeserializeOwned>(&self, key: K) -> Result<Option<R>, sled::Error> {
55+
let result = self.remove_raw(key)?;
56+
Ok(result.map(|x| inflate_deserialize(&x)))
57+
}
58+
}
59+
pub trait DbTrait: DbGet + DbRaw {
60+
fn set<K: TreeKey, I: Serialize + ?Sized>(
61+
&self,
62+
key: K,
63+
value: &I,
64+
) -> Result<Option<()>, sled::Error> {
65+
let result = self.set_raw(key, value)?;
66+
Ok(result.map(|_| ()))
67+
}
68+
fn new<I: Serialize + ?Sized>(&self, value: &I) -> Result<Uuid, sled::Error> {
69+
let key = Uuid::new_v4();
70+
self.set(key, value)?;
71+
Ok(key)
72+
}
73+
fn remove<K: TreeKey>(&self, key: K) -> Result<Option<()>, sled::Error> {
74+
let result = self.remove_raw(key)?;
75+
Ok(result.map(|_| ()))
76+
}
77+
}
78+
pub trait DbRaw {
79+
fn set_raw<K: TreeKey, I: Serialize + ?Sized>(
80+
&self,
81+
key: K,
82+
value: &I,
83+
) -> Result<Option<IVec>, sled::Error>;
84+
fn remove_raw<K: TreeKey>(&self, key: K) -> Result<Option<IVec>, sled::Error>;
85+
}
86+
pub trait DbGet {
87+
fn get<K: TreeKey, R: DeserializeOwned>(&self, key: K) -> Result<Option<R>, sled::Error>;
88+
}
89+
90+
impl DbRoot {
91+
pub fn get_tree<K: TreeKey>(&self, name: K) -> DbTree {
92+
DbTree {
93+
inner: DB.open_tree(name.key()).unwrap(),
94+
}
95+
}
96+
pub fn drop_tree<K: TreeKey>(&self, name: K) -> Result<bool, sled::Error> {
97+
DB.drop_tree(name.key())
98+
}
99+
pub fn flush(&self) -> Result<(), sled::Error> {
100+
DB.flush().map(|_| ())
101+
}
102+
}
103+
104+
impl DbRaw for DbRoot {
105+
fn set_raw<K: TreeKey, I: Serialize + ?Sized>(
106+
&self,
107+
key: K,
108+
value: &I,
109+
) -> Result<Option<IVec>, sled::Error> {
110+
let compressed_data = deflate_serialize(value);
111+
DB.insert(key.key(), compressed_data)
112+
}
113+
fn remove_raw<K: TreeKey>(&self, key: K) -> Result<Option<IVec>, sled::Error> {
114+
DB.remove(key.key())
115+
}
116+
}
117+
impl DbGet for DbRoot {
118+
fn get<K: TreeKey, R: DeserializeOwned>(&self, key: K) -> Result<Option<R>, sled::Error> {
119+
DB.get(key.key()).map(|x| {
120+
x.map(|v| inflate(&v))
121+
.map(|x| serde_json::from_slice(&x).unwrap())
122+
})
123+
}
124+
}
125+
126+
impl DbTree {
127+
pub fn name(&self) -> String {
128+
str::from_utf8(&self.inner.name()).unwrap().to_string()
129+
}
130+
pub fn flush(&self) -> Result<(), sled::Error> {
131+
self.inner.flush().map(|_| ())
132+
}
133+
}
134+
impl DbRaw for DbTree {
135+
fn set_raw<K: TreeKey, I: Serialize + ?Sized>(
136+
&self,
137+
key: K,
138+
value: &I,
139+
) -> Result<Option<IVec>, sled::Error> {
140+
let compressed_data = deflate_serialize(value);
141+
self.inner.insert(key.key(), compressed_data)
142+
}
143+
fn remove_raw<K: TreeKey>(&self, key: K) -> Result<Option<IVec>, sled::Error> {
144+
self.inner.remove(key.key())
145+
}
146+
}
147+
impl DbGet for DbTree {
148+
fn get<K: TreeKey, R: DeserializeOwned>(&self, key: K) -> Result<Option<R>, sled::Error> {
149+
self.inner.get(key.key()).map(|x| {
150+
x.map(|v| inflate(&v))
151+
.map(|x| serde_json::from_slice(&x).unwrap())
152+
})
153+
}
154+
}
155+
impl DbTrait for DbRoot {}
156+
impl DbReturnTrait for DbRoot {}
157+
impl DbTrait for DbTree {}
158+
impl DbReturnTrait for DbTree {}
159+
160+
fn inflate_deserialize<T: DeserializeOwned>(data: &[u8]) -> T {
161+
let decompressed_data = inflate(data);
162+
serde_json::from_slice(&decompressed_data).unwrap()
163+
}
164+
fn deflate_serialize<T: Serialize + ?Sized>(value: &T) -> Vec<u8> {
165+
let data = serde_json::to_vec(value).unwrap();
166+
deflate(&data)
167+
}
168+
fn deflate(data: &[u8]) -> Vec<u8> {
169+
use flate2::Compression;
170+
use flate2::write::DeflateEncoder;
171+
let mut encoder = DeflateEncoder::new(Vec::new(), Compression::default());
172+
encoder.write_all(data).unwrap();
173+
encoder.finish().unwrap()
174+
}
175+
176+
fn inflate(data: &[u8]) -> Vec<u8> {
177+
use flate2::read::DeflateDecoder;
178+
use std::io::Read;
179+
let mut decoder = DeflateDecoder::new(data);
180+
let mut out = Vec::new();
181+
decoder.read_to_end(&mut out).unwrap();
182+
out
183+
}
184+
185+
#[test]
186+
fn duplicate_test() {
187+
let _ = DbTrait::set(&DbRoot, "tree", "asdf").unwrap();
188+
let _ = DbRoot.get_tree("tree");
189+
let _ = DbRoot.get_tree("tree");
190+
}
191+
192+
#[test]
193+
fn duplicate_drop() {
194+
let _ = DbRoot.drop_tree("tree");
195+
let _ = DbTrait::remove(&DbRoot, "tree");
196+
}
197+
198+
#[test]
199+
fn inspect() {
200+
println!("Master: {}", str::from_utf8(&DB.name()).unwrap());
201+
DB.iter().for_each(|item| {
202+
if let Ok((key, value)) = item {
203+
println!(
204+
"Key: {}, Value: {}",
205+
str::from_utf8(&key).unwrap(),
206+
str::from_utf8(&inflate(&value)).unwrap()
207+
);
208+
}
209+
});
210+
let tree_names = DB.tree_names();
211+
tree_names.iter().for_each(|name| {
212+
println!("Tree: {}", str::from_utf8(name).unwrap());
213+
if let Ok(tree) = DB.open_tree(name) {
214+
tree.iter().for_each(|item| {
215+
if let Ok((key, value)) = item {
216+
println!(
217+
" Key: {}, Value: {}",
218+
str::from_utf8(&key).unwrap(),
219+
str::from_utf8(&inflate(&value)).unwrap()
220+
);
221+
}
222+
});
223+
}
224+
println!();
225+
});
226+
}
227+
228+
pub trait TreeKey {
229+
fn key(&self) -> impl AsRef<[u8]>;
230+
}
231+
impl TreeKey for &str {
232+
fn key(&self) -> impl AsRef<[u8]> {
233+
self
234+
}
235+
}
236+
impl TreeKey for String {
237+
fn key(&self) -> impl AsRef<[u8]> {
238+
self
239+
}
240+
}
241+
impl TreeKey for Uuid {
242+
fn key(&self) -> impl AsRef<[u8]> {
243+
self.to_string()
244+
}
245+
}
246+
```
247+
248+
## 작성자의 글
249+
250+
- .
251+
252+
## 참조
253+
254+
- .

0 commit comments

Comments
 (0)