use super::DirectoryPutter; use super::DirectoryService; use super::{Directory, DirectoryGraph, LeavesToRootValidator}; use crate::B3Digest; use crate::Error; use tonic::async_trait; use tracing::instrument; use tracing::warn; /// This is an implementation of DirectoryPutter that simply /// inserts individual Directory messages one by one, on close, after /// they successfully validated. pub struct SimplePutter<DS: DirectoryService> { directory_service: DS, directory_validator: Option<DirectoryGraph<LeavesToRootValidator>>, } impl<DS: DirectoryService> SimplePutter<DS> { pub fn new(directory_service: DS) -> Self { Self { directory_service, directory_validator: Some(Default::default()), } } } #[async_trait] impl<DS: DirectoryService + 'static> DirectoryPutter for SimplePutter<DS> { #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest()), err)] async fn put(&mut self, directory: Directory) -> Result<(), Error> { match self.directory_validator { None => return Err(Error::StorageError("already closed".to_string())), Some(ref mut validator) => { validator .add(directory) .map_err(|e| Error::StorageError(e.to_string()))?; } } Ok(()) } #[instrument(level = "trace", skip_all, ret, err)] async fn close(&mut self) -> Result<B3Digest, Error> { match self.directory_validator.take() { None => Err(Error::InvalidRequest("already closed".to_string())), Some(validator) => { // retrieve the validated directories. let directories = validator .validate() .map_err(|e| Error::StorageError(e.to_string()))? .drain_leaves_to_root() .collect::<Vec<_>>(); // Get the root digest, which is at the end (cf. insertion order) let root_digest = directories .last() .ok_or_else(|| Error::InvalidRequest("got no directories".to_string()))? .digest(); // call an individual put for each directory and await the insertion. for directory in directories { let exp_digest = directory.digest(); let actual_digest = self.directory_service.put(directory).await?; // ensure the digest the backend told us matches our expectations. if exp_digest != actual_digest { warn!(directory.digest_expected=%exp_digest, directory.digest_actual=%actual_digest, "unexpected digest"); return Err(Error::StorageError( "got unexpected digest from backend during put".into(), )); } } Ok(root_digest) } } } }