Skip to main content

root/
mount.rs

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); //等待随机时间
125                        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); //等待随机时间
205                        let lock_name = format!("{}::{}", name, key); //为这个 name 里面的 key 单独上锁
206                        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            } // 强制插入 肯定成功
352            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            } // 强制插入 肯定成功
368            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}