common/bucket_log/provider.rs
1use std::fmt::{Debug, Display};
2
3use async_trait::async_trait;
4use uuid::Uuid;
5
6use crate::linked_data::Link;
7
8// TODO (amiller68): it might be easier to design this to work
9// with dependency injection over a generic type
10
11#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
12pub enum BucketLogError<T> {
13 /// The bucket log is empty
14 #[error("unhandled bucket log provider error: {0}")]
15 Provider(#[from] T),
16 /// The bucket log is empty
17 #[error("head not found at height {0}")]
18 HeadNotFound(u64),
19 /// An append causes a conflict with the current of the
20 /// log i.e. same link at the same height
21 #[error("conflict with current log entry")]
22 Conflict,
23 /// An append does not implement a valid link structure
24 /// st the previous link pointed at by the new log does
25 /// not exist in the log at the expected height --
26 /// current, previous, height
27 #[error("invalid append: {0}, {1}, {2}")]
28 InvalidAppend(Link, Link, u64),
29}
30
31#[async_trait]
32pub trait BucketLogProvider: Send + Sync + std::fmt::Debug + Clone + 'static {
33 type Error: Display + Debug;
34
35 async fn exists(&self, id: Uuid) -> Result<bool, BucketLogError<Self::Error>>;
36
37 /// Get the possible heads for a bucket
38 /// based on passed height
39 ///
40 /// # Arguments
41 /// * `id` - The UUID of the bucket
42 /// * `height` - The height to query the candidate heads for
43 ///
44 /// # Returns
45 /// * `Ok(Vec<Link>)` - The candidate heads for the bucket
46 /// * `Err(Self::Error)` - An error occurred while fetching the candidate heads
47 async fn heads(&self, id: Uuid, height: u64) -> Result<Vec<Link>, BucketLogError<Self::Error>>;
48
49 // NOTE (amiller68): maybe name is more of a
50 // implementation detail or product concern,
51 // but maybe its not such thing to mandate a
52 // cache for.
53 /// Append a version of the bucket to the log
54 ///
55 /// # Arguments
56 /// * `id` - The UUID of the bucket
57 /// * `name` - The friendly name for the bucket
58 /// * `current` - The current link of the record
59 /// * `previous` - The previous link of the record
60 /// * `height` - The reported depth of the bucket version within the chain
61 ///
62 /// Should fail with the following errors to be considered
63 /// correct:
64 /// * `Err(BucketLogError::Conflict)` - The append causes a conflict with the current log
65 /// * `Err(BucketLogError::InvalidHeight)` - The height is not greater than the previous height
66 async fn append(
67 &self,
68 id: Uuid,
69 name: String,
70 current: Link,
71 // NOTE (amiller68): this should *only*
72 // be null for the genesis of a bucket
73 previous: Option<Link>,
74 height: u64,
75 ) -> Result<(), BucketLogError<Self::Error>>;
76
77 /// Return the greatest height of the bucket version within the chain
78 ///
79 /// # Arguments
80 /// * `id` - The UUID of the bucket
81 ///
82 /// # Returns
83 /// * `Result<u64, BucketLogError<Self::Error>>` - The height of the bucket version within the chain
84 ///
85 /// NOTE: while this returns a BucketLogError, it should only ever return a BucketLogError::NotFound
86 /// or ProviderError
87 async fn height(&self, id: Uuid) -> Result<u64, BucketLogError<Self::Error>>;
88
89 /// Check if a link exists within a bucket
90 ///
91 /// # Arguments
92 /// * `id` - The UUID of the bucket
93 /// * `link` - The link to check for existence as current
94 ///
95 /// # Returns
96 /// * `Result<Vec<u64>, BucketLogError<Self::Error>>`
97 /// The heights at which the link exists within the bucket
98 async fn has(&self, id: Uuid, link: Link) -> Result<Vec<u64>, BucketLogError<Self::Error>>;
99
100 /// Get the peers canonical head based on its log entries
101 async fn head(
102 &self,
103 id: Uuid,
104 height: Option<u64>,
105 ) -> Result<(Link, u64), BucketLogError<Self::Error>> {
106 let height = height.unwrap_or(self.height(id).await?);
107 let heads = self.heads(id, height).await?;
108 Ok((
109 heads
110 .into_iter()
111 .max()
112 .ok_or(BucketLogError::HeadNotFound(height))?,
113 height,
114 ))
115 }
116
117 /// List all bucket IDs that have log entries
118 ///
119 /// # Returns
120 /// * `Ok(Vec<Uuid>)` - The list of bucket IDs
121 /// * `Err(BucketLogError)` - An error occurred while fetching bucket IDs
122 async fn list_buckets(&self) -> Result<Vec<Uuid>, BucketLogError<Self::Error>>;
123}