Skip to main content

snix_castore/directoryservice/
simple_putter.rs

1use super::Directory;
2use super::DirectoryPutter;
3use super::DirectoryService;
4use crate::B3Digest;
5use crate::directoryservice::directory_graph::DirectoryGraphBuilder;
6use tonic::async_trait;
7use tracing::instrument;
8use tracing::warn;
9
10/// This is an implementation of DirectoryPutter that simply
11/// inserts individual Directory messages one by one, on close, after
12/// they successfully validated.
13pub struct SimplePutter<'a, DS> {
14    directory_service: &'a DS,
15
16    builder: Option<DirectoryGraphBuilder>,
17}
18
19impl<'a, DS> SimplePutter<'a, DS>
20where
21    DS: DirectoryService,
22{
23    pub fn new(directory_service: &'a DS) -> Self {
24        Self {
25            directory_service,
26            builder: Some(DirectoryGraphBuilder::new_leaves_to_root()),
27        }
28    }
29}
30
31#[async_trait]
32impl<DS: DirectoryService + 'static> DirectoryPutter for SimplePutter<'_, DS> {
33    #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest()), err)]
34    async fn put(&mut self, directory: Directory) -> Result<(), super::Error> {
35        let builder = self.builder.as_mut().ok_or_else(|| Error::AlreadyClosed)?;
36
37        builder
38            .try_insert(directory)
39            .map_err(Error::OrderingError)?;
40
41        Ok(())
42    }
43
44    #[instrument(level = "trace", skip_all, ret, err)]
45    async fn close(&mut self) -> Result<B3Digest, super::Error> {
46        let builder = self.builder.take().ok_or_else(|| Error::AlreadyClosed)?;
47
48        // Retrieve the validated directories.
49        let directory_graph = builder.build().map_err(Error::OrderingError)?;
50        let root_digest = directory_graph.root().digest();
51
52        for directory in directory_graph.drain_leaves_to_root() {
53            let exp_digest = directory.digest();
54            let actual_digest = self.directory_service.put(directory).await?;
55
56            // ensure the digest the backend told us matches our expectations.
57            if exp_digest != actual_digest {
58                warn!(directory.digest_expected=%exp_digest, directory.digest_actual=%actual_digest, "unexpected digest");
59                Err(Error::UnexpectedDigest {
60                    expected: exp_digest,
61                    actual: actual_digest,
62                })?;
63            }
64        }
65
66        Ok(root_digest)
67    }
68}
69
70#[derive(thiserror::Error, Debug)]
71pub enum Error {
72    #[error("DirectoryGraphBuilder already closed")]
73    AlreadyClosed,
74    #[error("got unexpected digest from backend, expected {expected}, actual {actual}")]
75    UnexpectedDigest {
76        expected: B3Digest,
77        actual: B3Digest,
78    },
79    #[error("failure during graph validation")]
80    OrderingError(#[from] super::OrderingError),
81}
82
83impl From<Error> for super::Error {
84    fn from(value: Error) -> Self {
85        Self(Box::new(value))
86    }
87}