1use dynamic::{MsgPack, MsgUnpack};
2use rand::random_range;
3use scc::HashMap;
4use smol_str::SmolStr;
5
6use anyhow::{Result, anyhow};
7
8use super::sync_await;
9use crate::directory;
10#[cfg(feature = "iroh")]
11use crate::iroh::IrohClient;
12use crate::node::Node;
13use fjall::{KeyspaceCreateOptions, OptimisticTxDatabase, OptimisticTxKeyspace};
14use redis::AsyncCommands;
15use redis::Commands;
16use std::sync::Arc;
17
18use rslock::LockManager;
19
20pub enum Mount<T> {
21 Memory(Arc<HashMap<SmolStr, Node<T>>>),
22 Redis {
23 client: redis::Client,
24 rl: LockManager,
25 },
26 Fjall {
27 values: OptimisticTxKeyspace,
28 write_lock: Arc<std::sync::Mutex<()>>,
29 },
30 #[cfg(feature = "iroh")]
31 Iroh {
32 client: IrohClient,
33 write_lock: Arc<std::sync::Mutex<()>>,
34 },
35}
36
37impl<T: std::fmt::Debug + MsgPack + MsgUnpack + Default + Send> Mount<T> {
38 pub fn memory() -> Self {
39 Self::Memory(Arc::new(HashMap::new()))
40 }
41
42 pub fn redis(url: &str) -> Result<Self> {
43 let client = redis::Client::open(url)?;
44 let mut conn = client.get_connection()?;
45 directory::rebuild_once(&mut conn)?;
46 let rl = LockManager::new(vec![url]);
47 Ok(Self::Redis { client, rl })
48 }
49
50 pub fn fjall(data_dir: &str) -> Result<Self> {
51 let db = OptimisticTxDatabase::builder(data_dir).open()?;
52 let values = db.keyspace("root", KeyspaceCreateOptions::default)?;
53 Ok(Self::Fjall { values, write_lock: Arc::new(std::sync::Mutex::new(())) })
54 }
55
56 #[cfg(feature = "iroh")]
57 pub fn iroh(node_id: &str) -> Result<Self> {
58 let remote = node_id.parse()?;
59 Ok(Self::Iroh { client: IrohClient::new(remote)?, write_lock: Arc::new(std::sync::Mutex::new(())) })
60 }
61
62 pub fn add(&self, name: &str, value: T) -> bool {
63 match self {
64 Self::Memory(m) => {
65 m.upsert_sync(name.into(), Node::Object(value));
66 true
67 }
68 Self::Redis { client, rl: _ } => {
69 let mut buf = Vec::new();
70 value.encode(&mut buf);
71 let Ok(mut conn) = client.get_connection() else {
72 return false;
73 };
74 conn.set::<&str, Vec<u8>, ()>(name, buf).is_ok() && directory::add_path(&mut conn, name).is_ok()
75 }
76 Self::Fjall { values, write_lock } => {
77 let mut buf = Vec::new();
78 value.encode(&mut buf);
79 let Ok(_guard) = write_lock.lock() else {
80 return false;
81 };
82 fjall_clear_node(values, name).is_ok() && values.insert(fjall_object_key(name), buf).is_ok()
83 }
84 #[cfg(feature = "iroh")]
85 Self::Iroh { client, write_lock } => {
86 let mut buf = Vec::new();
87 value.encode(&mut buf);
88 let Ok(_guard) = write_lock.lock() else {
89 return false;
90 };
91 client.put_bytes(name, bytes::Bytes::from(buf)).is_ok()
92 }
93 }
94 }
95
96 pub fn contains(&self, name: &str) -> bool {
97 match self {
98 Self::Memory(m) => m.contains_sync(name),
99 Self::Redis { client, rl: _ } => client.get_connection().and_then(|mut conn| conn.exists::<&str, bool>(name)).unwrap_or(false),
100 Self::Fjall { values, .. } => values.contains_key(fjall_object_key(name)).unwrap_or(false) || values.contains_key(fjall_type_key(name)).unwrap_or(false),
101 #[cfg(feature = "iroh")]
102 Self::Iroh { client, .. } => client.contains(name),
103 }
104 }
105
106 pub fn get_mut<R: Send + 'static, F: FnMut(&mut T) -> R>(&self, name: &str, mut f: F) -> Result<R>
107 where
108 F: Send + 'static,
109 {
110 match self {
111 Self::Memory(m) => m
112 .update_sync(name, |_, v| match v {
113 Node::Object(v) => Some(f(v)),
114 _ => None,
115 })
116 .flatten()
117 .ok_or(anyhow!("{} 不存在", name)),
118 Self::Redis { client, rl } => {
119 let name = String::from(name);
120 let rl = rl.clone();
121 let client = client.clone();
122 sync_await!(async move {
123 loop {
124 let time_out = random_range(0..1000); if let Ok(lock) = rl.lock(name.as_str(), std::time::Duration::from_millis(time_out)).await {
126 let mut conn = client.get_multiplexed_async_connection().await?;
127 let mut buf: Vec<u8> = conn.get(name.as_str()).await?;
128 let (mut v, _) = T::decode(buf.as_slice())?;
129 let r = f(&mut v);
130 buf.clear();
131 v.encode(&mut buf);
132 conn.set::<&str, Vec<u8>, ()>(name.as_str(), buf).await?;
133 rl.unlock(&lock).await;
134 break Ok(r);
135 }
136 }
137 })
138 }
139 Self::Fjall { values, write_lock } => {
140 let _guard = write_lock.lock().map_err(|e| anyhow!("无法获取 fjall 写锁: {}", e))?;
141 let mut v = fjall_get_object::<T>(values, name)?;
142 let r = f(&mut v);
143 fjall_insert_object(values, name, &v)?;
144 Ok(r)
145 }
146 #[cfg(feature = "iroh")]
147 Self::Iroh { client, write_lock } => {
148 let _guard = write_lock.lock().map_err(|e| anyhow!("无法获取 iroh 写锁: {}", e))?;
149 let bytes = client.get_bytes(name)?;
150 let (mut v, _) = T::decode(bytes.as_ref())?;
151 let r = f(&mut v);
152 let mut buf = Vec::new();
153 v.encode(&mut buf);
154 client.put_bytes(name, bytes::Bytes::from(buf))?;
155 Ok(r)
156 }
157 }
158 }
159
160 pub fn get<R, F: FnOnce(&T) -> R>(&self, name: &str, f: F) -> Result<R> {
161 match self {
162 Self::Memory(m) => m
163 .read_sync(name, |_, v| match v {
164 Node::Object(v) => Some(f(v)),
165 _ => None,
166 })
167 .flatten()
168 .ok_or(anyhow!("{} 不存在", name)),
169 Self::Redis { client, rl: _ } => {
170 let mut conn = client.get_connection()?;
171 let buf: Vec<u8> = conn.get(name)?;
172 let (v, _) = T::decode(buf.as_slice())?;
173 Ok(f(&v))
174 }
175 Self::Fjall { values, .. } => fjall_get_object(values, name).map(|v| f(&v)),
176 #[cfg(feature = "iroh")]
177 Self::Iroh { client, .. } => {
178 let bytes = client.get_bytes(name)?;
179 let (v, _) = T::decode(bytes.as_ref())?;
180 Ok(f(&v))
181 }
182 }
183 }
184
185 pub fn get_key_mut<'a, R: Send + 'static, F: FnOnce(&mut T) -> R>(&'a self, name: &'a str, key: &'a str, f: F) -> Result<R>
186 where
187 F: Send + 'static,
188 {
189 match self {
190 Self::Memory(m) => m
191 .update_sync(name, |_, v| match v {
192 Node::Map(m) => m.update_sync(key, |_, v| f(v)),
193 _ => None,
194 })
195 .flatten()
196 .ok_or(anyhow!("{} 不存在", name)),
197 Self::Redis { client, rl } => {
198 let name = String::from(name);
199 let key = String::from(key);
200 let rl = rl.clone();
201 let client = client.clone();
202 sync_await!(async move {
203 loop {
204 let time_out = random_range(0..1000); let lock_name = format!("{}::{}", name, key); if let Ok(lock) = rl.lock(lock_name.as_str(), std::time::Duration::from_millis(time_out)).await {
207 let mut conn = client.get_multiplexed_async_connection().await?;
208 let mut buf: Vec<u8> = conn.hget(name.as_str(), key.as_str()).await?;
209 let (mut v, _) = T::decode(buf.as_slice())?;
210 let r = f(&mut v);
211 buf.clear();
212 v.encode(&mut buf);
213 conn.hset::<&str, &str, Vec<u8>, ()>(name.as_str(), key.as_str(), buf).await?;
214 rl.unlock(&lock).await;
215 break Ok(r);
216 }
217 }
218 })
219 }
220 Self::Fjall { values, write_lock } => {
221 let _guard = write_lock.lock().map_err(|e| anyhow!("无法获取 fjall 写锁: {}", e))?;
222 let mut v = fjall_get_map_item::<T>(values, name, key)?;
223 let r = f(&mut v);
224 fjall_insert_map_item(values, name, key, &v)?;
225 Ok(r)
226 }
227 #[cfg(feature = "iroh")]
228 Self::Iroh { .. } => Err(anyhow!("iroh mount 暂不支持 map key 更新")),
229 }
230 }
231
232 pub fn dir(&self, name: &str) -> Result<Vec<SmolStr>> {
233 let prefix = if name.is_empty() || name.ends_with('/') { name.to_string() } else { format!("{name}/") };
234 if let Self::Redis { client, rl: _ } = self {
235 let mut conn = client.get_connection()?;
236 return Ok(Self::dir_entries_from_children(&prefix, directory::children(&mut conn, name)?));
237 }
238
239 let raw = self.dir_raw(&prefix)?;
240 Ok(Self::dir_entries_from_raw(&prefix, raw))
241 }
242
243 fn dir_entries_from_children(prefix: &str, children: Vec<SmolStr>) -> Vec<SmolStr> {
244 children.into_iter().map(|child| if prefix.is_empty() { child } else { format!("{prefix}{child}").into() }).collect()
245 }
246
247 fn dir_entries_from_raw(prefix: &str, raw: Vec<SmolStr>) -> Vec<SmolStr> {
248 let mut seen = std::collections::HashSet::new();
249 let mut names = Vec::new();
250 for key in raw {
251 let Some(rest) = key.strip_prefix(&prefix) else {
252 continue;
253 };
254 let end = rest.find('/').unwrap_or(rest.len());
255 let entry = &rest[..end];
256 if !entry.is_empty() && seen.insert(entry.to_string()) {
257 names.push(format!("{prefix}{entry}").into());
258 }
259 }
260 names
261 }
262
263 pub fn dir_raw(&self, name: &str) -> Result<Vec<SmolStr>> {
264 let mut names = Vec::new();
265 match self {
266 Self::Memory(m) => {
267 m.iter_sync(|key, _| {
268 if key.starts_with(name) {
269 names.push(key.clone())
270 }
271 true
272 });
273 }
274 Self::Redis { client, rl: _ } => {
275 let mut conn = client.get_connection()?;
276 let prefix = if name.is_empty() || name.ends_with('/') { name.to_string() } else { format!("{name}/") };
277 names.append(&mut Self::dir_entries_from_children(&prefix, directory::children(&mut conn, name)?));
278 }
279 Self::Fjall { values, .. } => {
280 names.append(&mut fjall_paths_with_prefix(values, name)?);
281 }
282 #[cfg(feature = "iroh")]
283 Self::Iroh { client, .. } => {
284 names.append(&mut client.dir_raw(name)?);
285 }
286 }
287 Ok(names)
288 }
289
290 pub fn len(&self, name: &str) -> Result<usize> {
291 match self {
292 Self::Memory(m) => m.read_sync(name, |_, v| v.len()).ok_or(anyhow!("{} 不是列表", name)),
293 Self::Redis { client, rl: _ } => {
294 let mut conn = client.get_connection()?;
295 let ty: String = conn.key_type(name)?;
296 match ty.as_str() {
297 "list" => Ok(conn.llen(name)?),
298 "hash" => Ok(conn.hlen(name)?),
299 _ => Ok(1),
300 }
301 }
302 Self::Fjall { values, .. } => match fjall_node_type(values, name)? {
303 Some(FjallNodeType::List) => Ok(fjall_count_prefix(values, fjall_list_prefix(name))?),
304 Some(FjallNodeType::Map) => Ok(fjall_count_prefix(values, fjall_map_prefix(name))?),
305 None if values.contains_key(fjall_object_key(name))? => Ok(1),
306 None => Err(anyhow!("{} 不存在", name)),
307 },
308 #[cfg(feature = "iroh")]
309 Self::Iroh { client, .. } => {
310 if client.contains(name) {
311 Ok(1)
312 } else {
313 Err(anyhow!("{} 不存在", name))
314 }
315 }
316 }
317 }
318
319 pub fn remove(&self, name: &str) -> Result<T> {
320 match self {
321 Self::Memory(m) => m.remove_sync(name).and_then(|(_, v)| v.into_object()).ok_or(anyhow!("{} 不存在", name)),
322 Self::Redis { client, rl: _ } => {
323 let mut conn = client.get_connection()?;
324 let buf: Vec<u8> = conn.get(name)?;
325 let (v, _) = T::decode(buf.as_slice())?;
326 let removed: usize = conn.del(name)?;
327 if removed != 0 {
328 directory::remove_path(&mut conn, name)?;
329 }
330 Ok(v)
331 }
332 Self::Fjall { values, .. } => {
333 let v = fjall_get_object(values, name)?;
334 values.remove(fjall_object_key(name))?;
335 Ok(v)
336 }
337 #[cfg(feature = "iroh")]
338 Self::Iroh { client, .. } => {
339 let bytes = client.get_bytes(name)?;
340 let (v, _) = T::decode(bytes.as_ref())?;
341 client.delete(name)?;
342 Ok(v)
343 }
344 }
345 }
346
347 pub fn add_list(&self, name: &str) {
348 match self {
349 Self::Memory(m) => {
350 m.upsert_sync(name.into(), Node::<T>::list());
351 } Self::Redis { client: _, rl: _ } => {}
353 Self::Fjall { values, write_lock } => {
354 if let Ok(_guard) = write_lock.lock() {
355 let _ = fjall_clear_node(values, name).and_then(|_| fjall_set_node_type(values, name, FjallNodeType::List));
356 }
357 }
358 #[cfg(feature = "iroh")]
359 Self::Iroh { .. } => {}
360 }
361 }
362
363 pub fn add_map(&self, name: &str) {
364 match self {
365 Self::Memory(m) => {
366 m.upsert_sync(name.into(), Node::<T>::map());
367 } Self::Redis { client: _, rl: _ } => {}
369 Self::Fjall { values, write_lock } => {
370 if let Ok(_guard) = write_lock.lock() {
371 let _ = fjall_clear_node(values, name).and_then(|_| fjall_set_node_type(values, name, FjallNodeType::Map));
372 }
373 }
374 #[cfg(feature = "iroh")]
375 Self::Iroh { .. } => {}
376 }
377 }
378
379 pub fn push(&self, name: &str, value: T) -> Result<usize> {
380 match self {
381 Self::Memory(m) => m.update_sync(name, |_, v| v.push(value)).flatten().ok_or(anyhow!("push {} 失败", name)),
382 Self::Redis { client, rl: _ } => {
383 let mut conn = client.get_connection()?;
384 let mut buf = Vec::new();
385 value.encode(&mut buf);
386 let len = conn.rpush(name, buf)?;
387 directory::add_path(&mut conn, name)?;
388 Ok(len)
389 }
390 Self::Fjall { values, write_lock } => {
391 let _guard = write_lock.lock().map_err(|e| anyhow!("无法获取 fjall 写锁: {}", e))?;
392 match fjall_node_type(values, name)? {
393 Some(FjallNodeType::List) => {}
394 Some(FjallNodeType::Map) => return Err(anyhow!("push {} 失败", name)),
395 None => fjall_set_node_type(values, name, FjallNodeType::List)?,
396 }
397 let idx = fjall_next_list_idx(values, name)?;
398 fjall_insert_list_item(values, name, idx, &value)?;
399 Ok(idx)
400 }
401 #[cfg(feature = "iroh")]
402 Self::Iroh { .. } => Err(anyhow!("iroh mount 暂不支持 list push")),
403 }
404 }
405
406 pub fn get_idx<R, F: FnOnce(&T) -> R>(&self, name: &str, idx: usize, f: F) -> Result<R> {
407 match self {
408 Self::Memory(m) => m.read_sync(name, |_, v| v.get_idx(idx, f)).flatten().ok_or(anyhow!("get_idx {} 失败", name)),
409 Self::Redis { client, rl: _ } => {
410 let mut conn = client.get_connection()?;
411 let buf: Vec<u8> = conn.lindex(name, idx as isize)?;
412 let (v, _) = T::decode(buf.as_slice())?;
413 Ok(f(&v))
414 }
415 Self::Fjall { values, .. } => fjall_get_list_item(values, name, idx).map(|v| f(&v)),
416 #[cfg(feature = "iroh")]
417 Self::Iroh { .. } => Err(anyhow!("iroh mount 暂不支持 list get_idx")),
418 }
419 }
420
421 pub fn get_idx_mut<R, F: FnMut(&mut T) -> R>(&self, name: &str, idx: usize, mut f: F) -> Result<R> {
422 match self {
423 Self::Memory(m) => m.update_sync(name, |_, v| v.get_idx_mut(idx, f)).flatten().ok_or(anyhow!("get_idx {} 失败", name)),
424 Self::Redis { client, rl: _ } => {
425 let mut conn = client.get_connection()?;
426 let buf: Vec<u8> = conn.lindex(name, idx as isize)?;
427 let (mut v, _) = T::decode(buf.as_slice())?;
428 Ok(f(&mut v))
429 }
430 Self::Fjall { values, write_lock } => {
431 let _guard = write_lock.lock().map_err(|e| anyhow!("无法获取 fjall 写锁: {}", e))?;
432 let mut v = fjall_get_list_item::<T>(values, name, idx)?;
433 let r = f(&mut v);
434 fjall_insert_list_item(values, name, idx, &v)?;
435 Ok(r)
436 }
437 #[cfg(feature = "iroh")]
438 Self::Iroh { .. } => Err(anyhow!("iroh mount 暂不支持 list get_idx_mut")),
439 }
440 }
441
442 pub fn remove_idx(&self, name: &str, idx: usize) -> Result<T> {
443 match self {
444 Self::Memory(m) => m.update_sync(name, |_, v| v.remove_idx(idx)).flatten().ok_or(anyhow!("remove_idx {} 失败", name)),
445 Self::Redis { client, rl: _ } => {
446 let mut conn = client.get_connection()?;
447 let buf: Vec<u8> = conn.lindex(name, idx as isize)?;
448 let v = T::decode(buf.as_slice()).map(|(v, _)| v).unwrap_or(T::default());
449 let _: () = conn.lset(name, idx as isize, Vec::new())?;
450 Ok(v)
451 }
452 Self::Fjall { values, .. } => {
453 let key = fjall_list_item_key(name, idx);
454 let buf = values.get(&key)?.ok_or(anyhow!("remove_idx {} 失败", name))?;
455 let (v, _) = T::decode(buf.as_ref())?;
456 values.remove(key)?;
457 Ok(v)
458 }
459 #[cfg(feature = "iroh")]
460 Self::Iroh { .. } => Err(anyhow!("iroh mount 暂不支持 list remove_idx")),
461 }
462 }
463
464 pub fn insert(&self, name: &str, key: &str, value: T) -> Option<T> {
465 match self {
466 Self::Memory(m) => m.update_sync(name, |_, v| v.insert(key.into(), value)).flatten(),
467 Self::Redis { client, rl: _ } => {
468 if let Ok(mut conn) = client.get_connection() {
469 let mut buf = Vec::new();
470 value.encode(&mut buf);
471 if conn.hset::<&str, &str, Vec<u8>, ()>(name, key, buf).is_ok() {
472 let _ = directory::add_path(&mut conn, name);
473 }
474 }
475 None
476 }
477 Self::Fjall { values, write_lock } => {
478 let _guard = write_lock.lock().ok()?;
479 match fjall_node_type(values, name).ok()? {
480 Some(FjallNodeType::Map) => {}
481 Some(FjallNodeType::List) => return None,
482 None => fjall_set_node_type(values, name, FjallNodeType::Map).ok()?,
483 }
484 let old = fjall_get_map_item(values, name, key).ok();
485 fjall_insert_map_item(values, name, key, &value).ok()?;
486 old
487 }
488 #[cfg(feature = "iroh")]
489 Self::Iroh { .. } => None,
490 }
491 }
492
493 pub fn get_key<R, F: FnOnce(&T) -> R>(&self, name: &str, key: &str, f: F) -> Result<R> {
494 match self {
495 Self::Memory(m) => m.read_sync(name, |_, v| v.get_key(key, f)).flatten().ok_or(anyhow!("get_key {} 失败", name)),
496 Self::Redis { client, rl: _ } => {
497 let mut conn = client.get_connection()?;
498 let buf: Vec<u8> = conn.hget(name, key)?;
499 let (v, _) = T::decode(buf.as_slice())?;
500 Ok(f(&v))
501 }
502 Self::Fjall { values, .. } => fjall_get_map_item(values, name, key).map(|v| f(&v)),
503 #[cfg(feature = "iroh")]
504 Self::Iroh { .. } => Err(anyhow!("iroh mount 暂不支持 map get_key")),
505 }
506 }
507
508 pub fn keys(&self, name: &str) -> Result<Vec<SmolStr>> {
509 match self {
510 Self::Memory(m) => m.read_sync(name, |_, v| v.keys()).flatten().ok_or(anyhow!("get_key {} 失败", name)),
511 Self::Redis { client, rl: _ } => {
512 let mut conn = client.get_connection()?;
513 let keys: Vec<String> = conn.hkeys(name).unwrap_or(Vec::new());
514 Ok(keys.into_iter().map(|k| k.into()).collect())
515 }
516 Self::Fjall { values, .. } => fjall_map_keys(values, name),
517 #[cfg(feature = "iroh")]
518 Self::Iroh { .. } => Err(anyhow!("iroh mount 暂不支持 map keys")),
519 }
520 }
521
522 pub fn remove_key(&self, name: &str, key: &str) -> Result<T> {
523 match self {
524 Self::Memory(m) => m.update_sync(name, |_, v| v.remove_key(key)).flatten().ok_or(anyhow!("remove_key {} 失败", name)),
525 Self::Redis { client, rl: _ } => {
526 let mut conn = client.get_connection()?;
527 let buf: Vec<u8> = conn.hget(name, key)?;
528 let v = T::decode(buf.as_slice())?.0;
529 let _: usize = conn.hdel(name, key)?;
530 if !conn.exists::<_, bool>(name)? {
531 directory::remove_path(&mut conn, name)?;
532 }
533 Ok(v)
534 }
535 Self::Fjall { values, .. } => {
536 let item_key = fjall_map_item_key(name, key);
537 let buf = values.get(&item_key)?.ok_or(anyhow!("remove_key {} 失败", name))?;
538 let (v, _) = T::decode(buf.as_ref())?;
539 values.remove(item_key)?;
540 Ok(v)
541 }
542 #[cfg(feature = "iroh")]
543 Self::Iroh { .. } => Err(anyhow!("iroh mount 暂不支持 map remove_key")),
544 }
545 }
546}
547
548#[derive(Clone, Copy)]
549enum FjallNodeType {
550 List,
551 Map,
552}
553
554const FJALL_OBJECT_PREFIX: u8 = b'o';
555const FJALL_TYPE_PREFIX: u8 = b't';
556const FJALL_LIST_PREFIX: u8 = b'l';
557const FJALL_LIST_COUNTER_PREFIX: u8 = b'c';
558const FJALL_MAP_PREFIX: u8 = b'm';
559const FJALL_SEPARATOR: u8 = 0;
560
561fn fjall_key(prefix: u8, name: &str) -> Vec<u8> {
562 let mut key = Vec::with_capacity(2 + name.len());
563 key.push(prefix);
564 key.push(FJALL_SEPARATOR);
565 key.extend_from_slice(name.as_bytes());
566 key
567}
568
569fn fjall_object_key(name: &str) -> Vec<u8> {
570 fjall_key(FJALL_OBJECT_PREFIX, name)
571}
572
573fn fjall_type_key(name: &str) -> Vec<u8> {
574 fjall_key(FJALL_TYPE_PREFIX, name)
575}
576
577fn fjall_list_counter_key(name: &str) -> Vec<u8> {
578 fjall_key(FJALL_LIST_COUNTER_PREFIX, name)
579}
580
581fn fjall_item_prefix(prefix: u8, name: &str) -> Vec<u8> {
582 let mut key = fjall_key(prefix, name);
583 key.push(FJALL_SEPARATOR);
584 key
585}
586
587fn fjall_list_prefix(name: &str) -> Vec<u8> {
588 fjall_item_prefix(FJALL_LIST_PREFIX, name)
589}
590
591fn fjall_map_prefix(name: &str) -> Vec<u8> {
592 fjall_item_prefix(FJALL_MAP_PREFIX, name)
593}
594
595fn fjall_list_item_key(name: &str, idx: usize) -> Vec<u8> {
596 let mut key = fjall_list_prefix(name);
597 key.extend_from_slice(&(idx as u64).to_be_bytes());
598 key
599}
600
601fn fjall_map_item_key(name: &str, key: &str) -> Vec<u8> {
602 let mut item_key = fjall_map_prefix(name);
603 item_key.extend_from_slice(key.as_bytes());
604 item_key
605}
606
607fn fjall_decode_value<T: MsgUnpack>(buf: &[u8]) -> Result<T> {
608 T::decode(buf).map(|(value, _)| value)
609}
610
611fn fjall_encode_value<T: MsgPack>(value: &T) -> Vec<u8> {
612 let mut buf = Vec::new();
613 value.encode(&mut buf);
614 buf
615}
616
617fn fjall_get_object<T: MsgUnpack>(values: &OptimisticTxKeyspace, name: &str) -> Result<T> {
618 let buf = values.get(fjall_object_key(name))?.ok_or(anyhow!("{} 不存在", name))?;
619 fjall_decode_value(buf.as_ref())
620}
621
622fn fjall_insert_object<T: MsgPack>(values: &OptimisticTxKeyspace, name: &str, value: &T) -> Result<()> {
623 Ok(values.insert(fjall_object_key(name), fjall_encode_value(value))?)
624}
625
626fn fjall_get_list_item<T: MsgUnpack>(values: &OptimisticTxKeyspace, name: &str, idx: usize) -> Result<T> {
627 let buf = values.get(fjall_list_item_key(name, idx))?.ok_or(anyhow!("get_idx {} 失败", name))?;
628 fjall_decode_value(buf.as_ref())
629}
630
631fn fjall_insert_list_item<T: MsgPack>(values: &OptimisticTxKeyspace, name: &str, idx: usize, value: &T) -> Result<()> {
632 Ok(values.insert(fjall_list_item_key(name, idx), fjall_encode_value(value))?)
633}
634
635fn fjall_get_map_item<T: MsgUnpack>(values: &OptimisticTxKeyspace, name: &str, key: &str) -> Result<T> {
636 let buf = values.get(fjall_map_item_key(name, key))?.ok_or(anyhow!("get_key {} 失败", name))?;
637 fjall_decode_value(buf.as_ref())
638}
639
640fn fjall_insert_map_item<T: MsgPack>(values: &OptimisticTxKeyspace, name: &str, key: &str, value: &T) -> Result<()> {
641 Ok(values.insert(fjall_map_item_key(name, key), fjall_encode_value(value))?)
642}
643
644fn fjall_node_type(values: &OptimisticTxKeyspace, name: &str) -> Result<Option<FjallNodeType>> {
645 Ok(values.get(fjall_type_key(name))?.and_then(|value| match value.as_ref() {
646 b"L" => Some(FjallNodeType::List),
647 b"M" => Some(FjallNodeType::Map),
648 _ => None,
649 }))
650}
651
652fn fjall_set_node_type(values: &OptimisticTxKeyspace, name: &str, node_type: FjallNodeType) -> Result<()> {
653 let value = match node_type {
654 FjallNodeType::List => b"L".as_slice(),
655 FjallNodeType::Map => b"M".as_slice(),
656 };
657 Ok(values.insert(fjall_type_key(name), value)?)
658}
659
660fn fjall_clear_node(values: &OptimisticTxKeyspace, name: &str) -> Result<()> {
661 values.remove(fjall_object_key(name))?;
662 values.remove(fjall_type_key(name))?;
663 values.remove(fjall_list_counter_key(name))?;
664 fjall_remove_prefix(values, fjall_list_prefix(name))?;
665 fjall_remove_prefix(values, fjall_map_prefix(name))?;
666 Ok(())
667}
668
669fn fjall_remove_prefix(values: &OptimisticTxKeyspace, prefix: Vec<u8>) -> Result<()> {
670 let mut keys = Vec::new();
671 for item in values.inner().prefix(prefix) {
672 keys.push(item.key()?);
673 }
674 for key in keys {
675 values.remove(key)?;
676 }
677 Ok(())
678}
679
680fn fjall_next_list_idx(values: &OptimisticTxKeyspace, name: &str) -> Result<usize> {
681 let key = fjall_list_counter_key(name);
682 let current = values.get(&key)?.and_then(|buf| buf.as_ref().try_into().ok().map(u64::from_be_bytes)).unwrap_or(0);
683 values.insert(key, (current + 1).to_be_bytes())?;
684 Ok(current as usize)
685}
686
687fn fjall_count_prefix(values: &OptimisticTxKeyspace, prefix: Vec<u8>) -> Result<usize> {
688 let mut count = 0;
689 for item in values.inner().prefix(prefix) {
690 item.key()?;
691 count += 1;
692 }
693 Ok(count)
694}
695
696fn fjall_paths_with_prefix(values: &OptimisticTxKeyspace, prefix: &str) -> Result<Vec<SmolStr>> {
697 let mut names = Vec::new();
698 names.extend(fjall_paths_for_key_prefix(values, FJALL_OBJECT_PREFIX, prefix)?);
699 names.extend(fjall_paths_for_key_prefix(values, FJALL_TYPE_PREFIX, prefix)?);
700 names.sort();
701 names.dedup();
702 Ok(names)
703}
704
705fn fjall_paths_for_key_prefix(values: &OptimisticTxKeyspace, key_prefix: u8, path_prefix: &str) -> Result<Vec<SmolStr>> {
706 let mut scan_prefix = fjall_key(key_prefix, path_prefix);
707 if path_prefix.is_empty() {
708 scan_prefix.truncate(2);
709 }
710 let mut names = Vec::new();
711 for item in values.inner().prefix(scan_prefix) {
712 let key = item.key()?;
713 let Some(path) = key.get(2..) else {
714 continue;
715 };
716 if let Ok(path) = std::str::from_utf8(path) {
717 names.push(path.into());
718 }
719 }
720 Ok(names)
721}
722
723fn fjall_map_keys(values: &OptimisticTxKeyspace, name: &str) -> Result<Vec<SmolStr>> {
724 let prefix = fjall_map_prefix(name);
725 let mut keys = Vec::new();
726 for item in values.inner().prefix(&prefix) {
727 let key = item.key()?;
728 let Some(map_key) = key.get(prefix.len()..) else {
729 continue;
730 };
731 if let Ok(map_key) = std::str::from_utf8(map_key) {
732 keys.push(map_key.into());
733 }
734 }
735 Ok(keys)
736}
737
738use std::sync::RwLock;
739
740#[derive(Debug)]
741pub struct Root<T> {
742 mounts: Arc<RwLock<Vec<(SmolStr, Mount<T>)>>>,
743}
744
745impl<T: std::fmt::Debug> std::fmt::Debug for Mount<T> {
746 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
747 match self {
748 Self::Memory(_) => write!(f, "Mount::Memory"),
749 Self::Redis { client: _, rl: _ } => write!(f, "Mount::Redis"),
750 Self::Fjall { .. } => write!(f, "Mount::Fjall"),
751 #[cfg(feature = "iroh")]
752 Self::Iroh { .. } => write!(f, "Mount::Iroh"),
753 }
754 }
755}
756
757impl<T> Clone for Mount<T> {
758 fn clone(&self) -> Self {
759 match self {
760 Self::Memory(m) => Self::Memory(m.clone()),
761 Self::Redis { client, rl } => Self::Redis { client: client.clone(), rl: rl.clone() },
762 Self::Fjall { values, write_lock } => Self::Fjall { values: values.clone(), write_lock: write_lock.clone() },
763 #[cfg(feature = "iroh")]
764 Self::Iroh { client, write_lock } => Self::Iroh { client: client.clone(), write_lock: write_lock.clone() },
765 }
766 }
767}
768
769impl<T: std::fmt::Debug + MsgPack + MsgUnpack + Default + Send> Root<T> {
770 pub fn new() -> Self {
771 Self { mounts: Arc::new(RwLock::new(vec![("local".into(), Mount::<T>::memory())])) }
772 }
773
774 pub fn mount_memory(&self, name: &str) -> bool {
775 let mounts = self.mounts.write();
776 match mounts {
777 Ok(mut mounts) => {
778 if mounts.iter().any(|(n, _)| n == name) {
779 return false;
780 }
781 mounts.push((name.into(), Mount::<T>::memory()));
782 true
783 }
784 Err(_) => false,
785 }
786 }
787
788 pub fn mount_redis(&self, name: &str, url: &str) -> Result<bool> {
789 let mounts = self.mounts.write();
790 match mounts {
791 Ok(mut mounts) => {
792 if mounts.iter().any(|(n, _)| n == name) {
793 return Ok(false);
794 }
795 mounts.push((name.into(), Mount::<T>::redis(url)?));
796 Ok(true)
797 }
798 Err(e) => Err(anyhow!("无法获取写锁: {}", e)),
799 }
800 }
801
802 pub fn mount_fjall(&self, name: &str, data_dir: &str) -> Result<bool> {
803 let mounts = self.mounts.write();
804 match mounts {
805 Ok(mut mounts) => {
806 if mounts.iter().any(|(n, _)| n == name) {
807 return Ok(false);
808 }
809 mounts.push((name.into(), Mount::<T>::fjall(data_dir)?));
810 Ok(true)
811 }
812 Err(e) => Err(anyhow!("无法获取写锁: {}", e)),
813 }
814 }
815
816 #[cfg(feature = "iroh")]
817 pub fn mount_iroh(&self, name: &str, node_id: &str) -> Result<bool> {
818 let mounts = self.mounts.write();
819 match mounts {
820 Ok(mut mounts) => {
821 if mounts.iter().any(|(n, _)| n == name) {
822 return Ok(false);
823 }
824 mounts.push((name.into(), Mount::<T>::iroh(node_id)?));
825 Ok(true)
826 }
827 Err(e) => Err(anyhow!("无法获取写锁: {}", e)),
828 }
829 }
830
831 pub fn get_mount<'a>(&self, name: &'a str) -> Result<(Mount<T>, &'a str)> {
832 let (mount, name) = name.split_once('/').ok_or(anyhow!("{} 没有 root 路径", name))?;
833 let mounts = self.mounts.read().map_err(|e| anyhow!("无法获取读锁: {}", e))?;
834 let m = mounts.iter().find_map(|m| if m.0 == mount { Some(m.1.clone()) } else { None }).ok_or(anyhow!("没有找到 {}", name))?;
835 Ok((m, name))
836 }
837}
838
839#[cfg(test)]
840mod tests {
841 use super::*;
842 use dynamic::Dynamic;
843
844 #[test]
845 fn dir_returns_only_immediate_children() {
846 let root = Root::<Dynamic>::new();
847 let (mount, name) = root.get_mount("local/test/dir/a").unwrap();
848 assert!(mount.add(name, 1.into()));
849 let (mount, name) = root.get_mount("local/test/dir/sub/item").unwrap();
850 assert!(mount.add(name, 2.into()));
851 let (mount, name) = root.get_mount("local/test/dir2/x").unwrap();
852 assert!(mount.add(name, 3.into()));
853
854 let (mount, name) = root.get_mount("local/test/dir").unwrap();
855 let mut entries = mount.dir(name).unwrap();
856 entries.sort();
857
858 assert_eq!(entries, vec![SmolStr::new("test/dir/a"), SmolStr::new("test/dir/sub")]);
859 }
860
861 #[test]
862 fn dir_accepts_trailing_slash() {
863 let root = Root::<Dynamic>::new();
864 let (mount, name) = root.get_mount("local/test/slash/a").unwrap();
865 assert!(mount.add(name, 1.into()));
866
867 let (mount, name) = root.get_mount("local/test/slash/").unwrap();
868 assert_eq!(mount.dir(name).unwrap(), vec![SmolStr::new("test/slash/a")]);
869 }
870
871 #[test]
872 fn fjall_mount_persists_values_and_dirs() {
873 let data_dir = std::env::temp_dir().join(format!("zust-root-fjall-{}", uuid::Uuid::new_v4()));
874 let data_dir_str = data_dir.to_str().unwrap();
875
876 {
877 let root = Root::<Dynamic>::new();
878 assert!(root.mount_fjall("fjall", data_dir_str).unwrap());
879 let (mount, name) = root.get_mount("fjall/test/kv/a").unwrap();
880 assert!(mount.add(name, 42.into()));
881 let (mount, name) = root.get_mount("fjall/test/kv/b").unwrap();
882 assert!(mount.add(name, "persisted".into()));
883 let (mount, name) = root.get_mount("fjall/test/list").unwrap();
884 mount.add_list(name);
885 assert_eq!(mount.push(name, 7.into()).unwrap(), 0);
886 let (mount, name) = root.get_mount("fjall/test/map").unwrap();
887 mount.add_map(name);
888 mount.insert(name, "answer", 42.into());
889 }
890
891 {
892 let root = Root::<Dynamic>::new();
893 assert!(root.mount_fjall("fjall", data_dir_str).unwrap());
894 let (mount, name) = root.get_mount("fjall/test/kv/a").unwrap();
895 assert_eq!(mount.get(name, |v| v.as_int()).unwrap(), Some(42));
896 let (mount, name) = root.get_mount("fjall/test").unwrap();
897 let mut entries = mount.dir(name).unwrap();
898 entries.sort();
899 assert_eq!(entries, vec![SmolStr::new("test/kv"), SmolStr::new("test/list"), SmolStr::new("test/map")]);
900 let (mount, name) = root.get_mount("fjall/test/list").unwrap();
901 assert_eq!(mount.get_idx(name, 0, |v| v.as_int()).unwrap(), Some(7));
902 let (mount, name) = root.get_mount("fjall/test/map").unwrap();
903 assert_eq!(mount.get_key(name, "answer", |v| v.as_int()).unwrap(), Some(42));
904 }
905
906 let _ = std::fs::remove_dir_all(data_dir);
907 }
908}