infinitree/
backends.rs

1//! Backends work with underlying persistence layers.
2//!
3//! The `infinitree` crate only contains the bare minimum to implement
4//! a filesystem-based tree.
5//!
6//! For more interesting backends, such as S3 and LRU caches, please
7//! use the
8//! [`infinitree-backends`](https://docs.rs/infinitree-backends)
9//! crate.
10use crate::object::{ObjectId, ReadObject, WriteObject};
11use std::{io, sync::Arc};
12
13mod directory;
14pub use directory::Directory;
15
16#[derive(thiserror::Error, Debug)]
17pub enum BackendError {
18    #[error("IO error: {source}")]
19    Io {
20        #[from]
21        source: io::Error,
22    },
23    #[error("No object found")]
24    NotFound { id: ObjectId },
25    #[error("Can't create object")]
26    Create,
27    #[error("Backend Error: {source}")]
28    Generic {
29        #[from]
30        source: anyhow::Error,
31    },
32}
33
34pub type Result<T> = std::result::Result<T, BackendError>;
35
36pub trait Backend: Send + Sync {
37    fn write_object(&self, object: &WriteObject) -> Result<()>;
38    fn read_object(&self, id: &ObjectId) -> Result<Arc<ReadObject>>;
39
40    fn preload(&self, _objects: &[ObjectId]) -> Result<()> {
41        Ok(())
42    }
43
44    fn delete(&self, _objects: &[ObjectId]) -> Result<()> {
45        Ok(())
46    }
47
48    fn sync(&self) -> Result<()> {
49        Ok(())
50    }
51
52    fn read_fresh(&self, id: &ObjectId) -> Result<Arc<ReadObject>> {
53        self.read_object(id)
54    }
55
56    fn keep_warm(&self, _objects: &[ObjectId]) -> Result<()> {
57        Ok(())
58    }
59}
60
61#[cfg(any(test, bench, feature = "test"))]
62pub mod test {
63    use super::*;
64    use std::{collections::HashMap, sync::Mutex};
65
66    #[derive(Clone, Default)]
67    pub struct InMemoryBackend(Arc<Mutex<HashMap<ObjectId, Arc<ReadObject>>>>);
68
69    impl InMemoryBackend {
70        pub fn new() -> Self {
71            InMemoryBackend::default()
72        }
73
74        pub fn shared() -> Arc<Self> {
75            Arc::new(InMemoryBackend::default())
76        }
77    }
78
79    impl Backend for InMemoryBackend {
80        fn write_object(&self, object: &WriteObject) -> Result<()> {
81            self.0
82                .lock()
83                .unwrap()
84                .insert(*object.id(), Arc::new(object.into()));
85            Ok(())
86        }
87
88        fn read_object(&self, id: &ObjectId) -> Result<Arc<ReadObject>> {
89            self.0
90                .lock()
91                .unwrap()
92                .get(id)
93                .ok_or(BackendError::NotFound { id: *id })
94                .map(Arc::clone)
95        }
96    }
97
98    #[derive(Clone, Default)]
99    pub struct NullBackend(Arc<Mutex<usize>>);
100
101    #[allow(clippy::len_without_is_empty)]
102    impl NullBackend {
103        pub fn len(&self) -> usize {
104            *self.0.lock().unwrap()
105        }
106    }
107
108    impl Backend for NullBackend {
109        fn write_object(&self, _object: &WriteObject) -> Result<()> {
110            *self.0.lock().unwrap() += 1;
111            Ok(())
112        }
113
114        fn read_object(&self, _id: &ObjectId) -> Result<Arc<ReadObject>> {
115            unimplemented!();
116        }
117    }
118}