snix_castore/directoryservice/mod.rs
1use crate::composition::{Registry, ServiceBuilder};
2use crate::{B3Digest, Directory, Error};
3
4use auto_impl::auto_impl;
5use futures::stream::BoxStream;
6use tonic::async_trait;
7mod combinators;
8mod directory_graph;
9mod from_addr;
10mod grpc;
11mod memory;
12mod object_store;
13mod order_validator;
14mod redb;
15mod simple_putter;
16#[cfg(test)]
17pub mod tests;
18mod traverse;
19mod utils;
20
21pub use self::combinators::{Cache, CacheConfig};
22pub use self::directory_graph::{DirectoryGraph, ValidatedDirectoryGraph};
23pub use self::from_addr::from_addr;
24pub use self::grpc::{GRPCDirectoryService, GRPCDirectoryServiceConfig};
25pub use self::memory::{MemoryDirectoryService, MemoryDirectoryServiceConfig};
26pub use self::object_store::{ObjectStoreDirectoryService, ObjectStoreDirectoryServiceConfig};
27pub use self::order_validator::{LeavesToRootValidator, OrderValidator, RootToLeavesValidator};
28pub use self::redb::{RedbDirectoryService, RedbDirectoryServiceConfig};
29pub use self::simple_putter::SimplePutter;
30pub use self::traverse::descend_to;
31pub use self::utils::traverse_directory;
32
33#[cfg(feature = "cloud")]
34mod bigtable;
35
36#[cfg(feature = "cloud")]
37pub use self::bigtable::{BigtableDirectoryService, BigtableParameters};
38
39/// The base trait all Directory services need to implement.
40/// This is a simple get and put of [Directory], returning their
41/// digest.
42#[async_trait]
43#[auto_impl(&, &mut, Arc, Box)]
44pub trait DirectoryService: Send + Sync {
45 /// Looks up a single Directory message by its digest.
46 /// The returned Directory message *must* be valid.
47 /// In case the directory is not found, Ok(None) is returned.
48 ///
49 /// It is okay for certain implementations to only allow retrieval of
50 /// Directory digests that are at the "root", aka the last element that's
51 /// sent to a DirectoryPutter. This makes sense for implementations bundling
52 /// closures of directories together in batches.
53 async fn get(&self, digest: &B3Digest) -> Result<Option<Directory>, Error>;
54 /// Uploads a single Directory message, and returns the calculated
55 /// digest, or an error. An error *must* also be returned if the message is
56 /// not valid.
57 async fn put(&self, directory: Directory) -> Result<B3Digest, Error>;
58
59 /// Looks up a closure of [Directory].
60 /// Ideally this would be a `impl Stream<Item = Result<Directory, Error>>`,
61 /// and we'd be able to add a default implementation for it here, but
62 /// we can't have that yet.
63 ///
64 /// This returns a pinned, boxed stream. The pinning allows for it to be polled easily,
65 /// and the box allows different underlying stream implementations to be returned since
66 /// Rust doesn't support this as a generic in traits yet. This is the same thing that
67 /// [async_trait] generates, but for streams instead of futures.
68 ///
69 /// The individually returned Directory messages *must* be valid.
70 /// Directories are sent in an order from the root to the leaves, so that
71 /// the receiving side can validate each message to be a connected to the root
72 /// that has initially been requested.
73 ///
74 /// In case the directory can not be found, this should return an empty stream.
75 fn get_recursive(
76 &self,
77 root_directory_digest: &B3Digest,
78 ) -> BoxStream<'static, Result<Directory, Error>>;
79
80 /// Allows persisting a closure of [Directory], which is a graph of
81 /// connected Directory messages.
82 fn put_multiple_start(&self) -> Box<dyn DirectoryPutter + '_>;
83}
84
85/// Provides a handle to put a closure of connected [Directory] elements.
86///
87/// The consumer can periodically call [DirectoryPutter::put], starting from the
88/// leaves. Once the root is reached, [DirectoryPutter::close] can be called to
89/// retrieve the root digest (or an error).
90///
91/// DirectoryPutters might be created without a single [DirectoryPutter::put],
92/// and then dropped without calling [DirectoryPutter::close],
93/// for example when ingesting a path that ends up not pointing to a directory,
94/// but a single file or symlink.
95#[async_trait]
96pub trait DirectoryPutter: Send {
97 /// Put a individual [Directory] into the store.
98 /// Error semantics and behaviour is up to the specific implementation of
99 /// this trait.
100 /// Due to bursting, the returned error might refer to an object previously
101 /// sent via `put`.
102 async fn put(&mut self, directory: Directory) -> Result<(), Error>;
103
104 /// Close the stream, and wait for any errors.
105 /// If there's been any invalid Directory message uploaded, and error *must*
106 /// be returned.
107 async fn close(&mut self) -> Result<B3Digest, Error>;
108}
109
110/// Registers the builtin DirectoryService implementations with the registry
111pub(crate) fn register_directory_services(reg: &mut Registry) {
112 reg.register::<Box<dyn ServiceBuilder<Output = dyn DirectoryService>>, super::directoryservice::ObjectStoreDirectoryServiceConfig>("objectstore");
113 reg.register::<Box<dyn ServiceBuilder<Output = dyn DirectoryService>>, super::directoryservice::MemoryDirectoryServiceConfig>("memory");
114 reg.register::<Box<dyn ServiceBuilder<Output = dyn DirectoryService>>, super::directoryservice::CacheConfig>("cache");
115 reg.register::<Box<dyn ServiceBuilder<Output = dyn DirectoryService>>, super::directoryservice::GRPCDirectoryServiceConfig>("grpc");
116 reg.register::<Box<dyn ServiceBuilder<Output = dyn DirectoryService>>, super::directoryservice::RedbDirectoryServiceConfig>("redb");
117 #[cfg(feature = "cloud")]
118 {
119 reg.register::<Box<dyn ServiceBuilder<Output = dyn DirectoryService>>, super::directoryservice::BigtableParameters>("bigtable");
120 }
121}