snix_castore/directoryservice/
simple_putter.rs

1use super::DirectoryPutter;
2use super::DirectoryService;
3use super::{Directory, DirectoryGraph, LeavesToRootValidator};
4use crate::B3Digest;
5use crate::Error;
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    directory_validator: Option<DirectoryGraph<LeavesToRootValidator>>,
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            directory_validator: Some(Default::default()),
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<(), Error> {
35        match self.directory_validator {
36            None => return Err(Error::StorageError("already closed".to_string())),
37            Some(ref mut validator) => {
38                validator
39                    .add(directory)
40                    .map_err(|e| Error::StorageError(e.to_string()))?;
41            }
42        }
43
44        Ok(())
45    }
46
47    #[instrument(level = "trace", skip_all, ret, err)]
48    async fn close(&mut self) -> Result<B3Digest, Error> {
49        match self.directory_validator.take() {
50            None => Err(Error::InvalidRequest("already closed".to_string())),
51            Some(validator) => {
52                // retrieve the validated directories.
53                let directories = validator
54                    .validate()
55                    .map_err(|e| Error::StorageError(e.to_string()))?
56                    .drain_leaves_to_root()
57                    .collect::<Vec<_>>();
58
59                // Get the root digest, which is at the end (cf. insertion order)
60                let root_digest = directories
61                    .last()
62                    .ok_or_else(|| Error::InvalidRequest("got no directories".to_string()))?
63                    .digest();
64
65                // call an individual put for each directory and await the insertion.
66                for directory in directories {
67                    let exp_digest = directory.digest();
68                    let actual_digest = self.directory_service.put(directory).await?;
69
70                    // ensure the digest the backend told us matches our expectations.
71                    if exp_digest != actual_digest {
72                        warn!(directory.digest_expected=%exp_digest, directory.digest_actual=%actual_digest, "unexpected digest");
73                        return Err(Error::StorageError(
74                            "got unexpected digest from backend during put".into(),
75                        ));
76                    }
77                }
78
79                Ok(root_digest)
80            }
81        }
82    }
83}