willow_data_model/entry/
builder.rs

1use anyhash::Hasher;
2use ufotofu::prelude::*;
3
4use crate::prelude::*;
5
6/// A builder for [`Entry`].
7///
8/// See [`Entry::builder`] and [`Entry::prefilled_builder`].
9pub struct EntryBuilder<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> {
10    namespace_id: Option<N>,
11    subspace_id: Option<S>,
12    path: Option<Path<MCL, MCC, MPL>>,
13    timestamp: Option<Timestamp>,
14    payload_length: Option<u64>,
15    payload_digest: Option<PD>,
16}
17
18impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
19    EntryBuilder<MCL, MCC, MPL, N, S, PD>
20{
21    pub(crate) fn create_empty() -> Self {
22        Self {
23            namespace_id: None,
24            subspace_id: None,
25            path: None,
26            timestamp: None,
27            payload_length: None,
28            payload_digest: None,
29        }
30    }
31
32    /// Sets the [namespace_id](https://willowprotocol.org/specs/data-model/index.html#entry_namespace_id) of the entry being built.
33    pub fn namespace_id(&mut self, value: N) -> &mut Self {
34        let new = self;
35        new.namespace_id = Some(value);
36        new
37    }
38
39    /// Sets the [subspace_id](https://willowprotocol.org/specs/data-model/index.html#entry_subspace_id) of the entry being built.
40    pub fn subspace_id(&mut self, value: S) -> &mut Self {
41        let new = self;
42        new.subspace_id = Some(value);
43        new
44    }
45
46    /// Sets the [path](https://willowprotocol.org/specs/data-model/index.html#entry_path) of the entry being built.
47    pub fn path(&mut self, value: Path<MCL, MCC, MPL>) -> &mut Self {
48        let new = self;
49        new.path = Some(value);
50        new
51    }
52
53    /// Sets the [timestamp](https://willowprotocol.org/specs/data-model/index.html#entry_timestamp) of the entry being built.
54    pub fn timestamp<T: Into<Timestamp>>(&mut self, value: T) -> &mut Self {
55        let new = self;
56        new.timestamp = Some(value.into());
57        new
58    }
59
60    /// Sets the [payload_length](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) of the entry being built.
61    pub fn payload_length(&mut self, value: u64) -> &mut Self {
62        let new = self;
63        new.payload_length = Some(value);
64        new
65    }
66
67    /// Sets the [payload_digest](https://willowprotocol.org/specs/data-model/index.html#entry_payload_digest) of the entry being built.
68    pub fn payload_digest(&mut self, value: PD) -> &mut Self {
69        let new = self;
70        new.payload_digest = Some(value);
71        new
72    }
73
74    /// Sets the [payload_length](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) and [payload_digest](https://willowprotocol.org/specs/data-model/index.html#entry_payload_digest) of the entry being built to those of the given [Payload](https://willowprotocol.org/specs/data-model/index.html#Payload).
75    ///
76    /// The type parameter `H` is the type of the [`Hasher`] which hashes the payload into a payload digest (of type `Digest: Into<PD>`). Its [`Default`] impl provides the initial state of the hasher.
77    pub fn payload<Payload: AsRef<[u8]>, H, Digest>(&mut self, payload: Payload) -> &mut Self
78    where
79        H: Default + Hasher<Digest>,
80        Digest: Into<PD>,
81    {
82        let new = self;
83
84        let mut hasher = H::default();
85        hasher.write(payload.as_ref());
86
87        new.payload_digest = Some(hasher.finish().into());
88        new.payload_length = Some(payload.as_ref().len() as u64);
89
90        new
91    }
92
93    /// Sets the [payload_length](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) and [payload_digest](https://willowprotocol.org/specs/data-model/index.html#entry_payload_digest) of the entry being built to those of the payload given by the producer.
94    ///
95    /// The type parameter `H` is the type of the [`Hasher`] which hashes the payload into a payload digest (of type `Digest: Into<PD>`). Its [`Default`] impl provides the initial state of the hasher.
96    pub async fn payload_async<P, H, Digest>(
97        &mut self,
98        payload_producer: &mut P,
99    ) -> Result<&mut Self, P::Error>
100    where
101        H: Default + Hasher<Digest>,
102        Digest: Into<PD>,
103        P: BulkProducer<Item = u8, Final = ()>,
104    {
105        let new = self;
106
107        let mut hasher = H::default();
108        let mut payload_len = 0;
109
110        loop {
111            match payload_producer
112                .expose_items_sync(|partial_payload| {
113                    hasher.write(partial_payload);
114                    payload_len += partial_payload.len();
115                    (partial_payload.len(), ())
116                })
117                .await?
118            {
119                Either::Left(_) => {}
120                Either::Right(_) => {
121                    new.payload_digest = Some(hasher.finish().into());
122                    new.payload_length = Some(payload_len as u64);
123                    return Ok(new);
124                }
125            }
126        }
127    }
128
129    /// Sets the [timestamp](https://willowprotocol.org/specs/data-model/index.html#entry_timestamp) of the entry being built to the current time.
130    #[cfg(feature = "std")]
131    pub fn now(&mut self) -> Result<&mut Self, HifitimeError> {
132        let new = self;
133
134        new.timestamp = Some(Timestamp::now()?);
135
136        Ok(new)
137    }
138
139    /// Builds the [`Entry`].
140    ///
141    /// Calling this method multiple times will cause a panic.
142    pub fn build(&mut self) -> Result<Entry<MCL, MCC, MPL, N, S, PD>, EntryBuilderError> {
143        Ok(Entry {
144            namespace_id: self
145                .namespace_id
146                .take()
147                .ok_or(EntryBuilderError::MissingNamespaceId)?,
148            subspace_id: self
149                .subspace_id
150                .take()
151                .ok_or(EntryBuilderError::MissingSubespaceId)?,
152            path: self.path.take().ok_or(EntryBuilderError::MissingPath)?,
153            timestamp: self
154                .timestamp
155                .take()
156                .ok_or(EntryBuilderError::MissingTimestamp)?,
157            payload_length: self
158                .payload_length
159                .take()
160                .ok_or(EntryBuilderError::MissingPayloadLength)?,
161            payload_digest: self
162                .payload_digest
163                .take()
164                .ok_or(EntryBuilderError::MissingPayloadDigest)?,
165        })
166    }
167}
168
169/// Everything that can go wrong when trying to build an [`Entry`].
170#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
171pub enum EntryBuilderError {
172    /// The builder was not configured with a namespace id.
173    MissingNamespaceId,
174    /// The builder was not configured with a subspace id.
175    MissingSubespaceId,
176    /// The builder was not configured with a path.
177    MissingPath,
178    /// The builder was not configured with a timestamp.
179    MissingTimestamp,
180    /// The builder was not configured with a payload length.
181    MissingPayloadLength,
182    /// The builder was not configured with a payload digest.
183    MissingPayloadDigest,
184}