Skip to main content

coven_format/
value.rs

1//! Wire encodings for the identities their owning crates define, and SQLite values.
2
3use crate::error::{bound, require, Error, Rule};
4use crate::wire::{get_blob, put_blob, wire_struct, Decoder, Encoder, Wire};
5use coven_crypto::{SealingKey, SigningKey};
6use coven_foundation::id_source::{CircleId, DeviceId, InviteId, KeyId, MemberId, StoreId};
7pub use coven_merge::Slot;
8use coven_merge::{Audience, RowId, Timestamp, WriteId};
9use uuid::Uuid;
10
11macro_rules! byte_id {
12    ($($ty:ty),*) => { $(
13        impl Wire for $ty {
14            fn put(&self, out: &mut Encoder) -> Result<(), Error> { self.0.put(out) }
15            fn get(input: &mut Decoder<'_>) -> Result<Self, Error> { Ok(Self(Wire::get(input)?)) }
16        }
17    )* };
18}
19byte_id!(DeviceId);
20macro_rules! uuid_id {
21    ($($ty:ty),*) => { $(
22        impl Wire for $ty {
23            fn put(&self, out: &mut Encoder) -> Result<(), Error> { self.0.as_bytes().put(out) }
24            fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
25                Ok(Self(Uuid::from_bytes(Wire::get(input)?)))
26            }
27        }
28    )* };
29}
30uuid_id!(StoreId, CircleId, InviteId, KeyId, MemberId);
31
32impl Wire for SigningKey {
33    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
34        self.as_bytes().put(out)
35    }
36    #[expect(
37        clippy::map_err_ignore,
38        reason = "coven-crypto refuses a signing key only as invalid or weak"
39    )]
40    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
41        Self::from_bytes(Wire::get(input)?).map_err(|_| Error::InvalidSigningKey)
42    }
43}
44wire_struct!(coven_crypto::MemberPublicKeys, signing, sealing);
45impl Wire for SealingKey {
46    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
47        self.as_bytes().put(out)
48    }
49    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
50        Ok(Self::from_bytes(Wire::get(input)?))
51    }
52}
53impl Wire for Timestamp {
54    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
55        out.bytes(&self.milliseconds().to_be_bytes()[2..])?;
56        self.counter().put(out)?;
57        self.device().put(out)
58    }
59    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
60        let mut ms = [0; 8];
61        ms[2..].copy_from_slice(input.take(6)?);
62        Self::new(u64::from_be_bytes(ms), Wire::get(input)?, Wire::get(input)?)
63            .map_err(Error::Merge)
64    }
65}
66impl Wire for Audience {
67    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
68        match self {
69            Self::Store => 0u8.put(out),
70            Self::Circle(id) => {
71                1u8.put(out)?;
72                id.put(out)
73            }
74        }
75    }
76    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
77        match u8::get(input)? {
78            0 => Ok(Self::Store),
79            1 => Ok(Self::Circle(Wire::get(input)?)),
80            tag => Err(Error::UnknownTag { field: "audience", tag }),
81        }
82    }
83}
84wire_struct!(WriteId, device, number);
85impl Wire for Slot {
86    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
87        self.get().put(out)
88    }
89    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
90        Self::new(u64::get(input)?)
91            .ok_or(Error::Invalid { field: "positive number", rule: Rule::Required })
92    }
93}
94
95/// A provider storage time: Unix seconds and a nanosecond fraction (ยง20.5).
96/// It keeps every returned digit on every platform, unlike `SystemTime`,
97/// which counts 100 ns on Windows; ordering is time order.
98#[derive(
99    Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
100)]
101#[serde(try_from = "StoredStorageTime")]
102pub struct StorageTime {
103    seconds: u64,
104    nanos: u32,
105}
106
107/// A serialized `StorageTime`, checked on the way in.
108#[derive(serde::Deserialize)]
109struct StoredStorageTime {
110    seconds: u64,
111    nanos: u32,
112}
113
114impl TryFrom<StoredStorageTime> for StorageTime {
115    type Error = Error;
116    fn try_from(stored: StoredStorageTime) -> Result<Self, Error> {
117        Self::new(stored.seconds, stored.nanos)
118    }
119}
120
121impl StorageTime {
122    /// The Unix epoch, the earliest storage time.
123    pub const UNIX_EPOCH: Self = Self { seconds: 0, nanos: 0 };
124
125    /// A sample whose fraction is below one second.
126    pub fn new(seconds: u64, nanos: u32) -> Result<Self, Error> {
127        require(nanos < 1_000_000_000, "storage time", Rule::Nanoseconds)?;
128        Ok(Self { seconds, nanos })
129    }
130
131    /// The exact provider time, refusing one before the Unix epoch.
132    #[expect(
133        clippy::map_err_ignore,
134        reason = "a time before the epoch is the Range rule; how far before is not part of it"
135    )]
136    pub fn from_system_time(time: std::time::SystemTime) -> Result<Self, Error> {
137        let since = time
138            .duration_since(std::time::UNIX_EPOCH)
139            .map_err(|_| Error::Invalid { field: "storage time", rule: Rule::Range })?;
140        Self::new(since.as_secs(), since.subsec_nanos())
141    }
142
143    /// The time `since` the Unix epoch.
144    pub fn since_epoch(since: std::time::Duration) -> Self {
145        Self { seconds: since.as_secs(), nanos: since.subsec_nanos() }
146    }
147
148    /// The exact time elapsed since the Unix epoch.
149    pub fn elapsed_since_epoch(self) -> std::time::Duration {
150        std::time::Duration::new(self.seconds, self.nanos)
151    }
152
153    /// This time plus `duration`, or `None` past the representable range.
154    pub fn checked_add(self, duration: std::time::Duration) -> Option<Self> {
155        self.elapsed_since_epoch().checked_add(duration).map(Self::since_epoch)
156    }
157
158    /// This time minus `duration`, or `None` before the Unix epoch.
159    pub fn checked_sub(self, duration: std::time::Duration) -> Option<Self> {
160        self.elapsed_since_epoch().checked_sub(duration).map(Self::since_epoch)
161    }
162
163    /// The exact time from `earlier` to this time, or `None` if `earlier` is later.
164    pub fn duration_since(self, earlier: Self) -> Option<std::time::Duration> {
165        self.elapsed_since_epoch().checked_sub(earlier.elapsed_since_epoch())
166    }
167
168    /// Whole Unix seconds.
169    pub fn seconds(self) -> u64 {
170        self.seconds
171    }
172
173    /// The fraction of a second, in nanoseconds.
174    pub fn nanos(self) -> u32 {
175        self.nanos
176    }
177}
178
179/// Panics past the representable range, as `SystemTime` does; use
180/// `checked_add` where the duration is not known to fit.
181impl std::ops::Add<std::time::Duration> for StorageTime {
182    type Output = Self;
183    fn add(self, duration: std::time::Duration) -> Self {
184        self.checked_add(duration).expect("storage time past the representable range")
185    }
186}
187
188impl std::ops::AddAssign<std::time::Duration> for StorageTime {
189    fn add_assign(&mut self, duration: std::time::Duration) {
190        *self = *self + duration;
191    }
192}
193
194/// Panics before the Unix epoch; use `checked_sub` where that can happen.
195impl std::ops::Sub<std::time::Duration> for StorageTime {
196    type Output = Self;
197    fn sub(self, duration: std::time::Duration) -> Self {
198        self.checked_sub(duration).expect("storage time before the Unix epoch")
199    }
200}
201
202pub(crate) fn positive(number: u64) -> Result<(), Error> {
203    require(number > 0, "positive number", Rule::Required)
204}
205
206/// A SQLite value. Real numbers use IEEE 754 bits; NaNs and negative zero are
207/// refused so each numeric value has one representation. Infinities are allowed.
208#[derive(Clone, Debug, PartialEq, Eq)]
209pub enum Value {
210    /// SQL NULL.
211    Null,
212    /// A signed 64-bit integer.
213    Integer(i64),
214    /// A double's bits, encoded big-endian.
215    Real(u64),
216    /// UTF-8 text, without Unicode normalization.
217    Text(String),
218    /// Bytes, including an empty blob.
219    Blob(Vec<u8>),
220}
221impl Value {
222    pub(crate) fn validate(&self) -> Result<(), Error> {
223        if let Self::Real(bits) = self {
224            require(!f64::from_bits(*bits).is_nan() && *bits != (1u64 << 63), "real", Rule::Real)?;
225        }
226        Ok(())
227    }
228}
229impl Wire for Value {
230    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
231        match self {
232            Self::Null => 0u8.put(out),
233            Self::Integer(v) => {
234                1u8.put(out)?;
235                v.put(out)
236            }
237            Self::Real(v) => {
238                2u8.put(out)?;
239                v.put(out)
240            }
241            Self::Text(v) => {
242                3u8.put(out)?;
243                v.put(out)
244            }
245            Self::Blob(v) => {
246                4u8.put(out)?;
247                put_blob(v, out)
248            }
249        }
250    }
251    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
252        match u8::get(input)? {
253            0 => Ok(Self::Null),
254            1 => Ok(Self::Integer(i64::get(input)?)),
255            2 => Ok(Self::Real(u64::get(input)?)),
256            3 => Ok(Self::Text(String::get(input)?)),
257            4 => Ok(Self::Blob(get_blob(input)?)),
258            tag => Err(Error::UnknownTag { field: "SQLite value", tag }),
259        }
260    }
261}
262
263impl Wire for RowId {
264    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
265        self.table.put(out)?;
266        put_blob(&self.key, out)?;
267        self.audience.put(out)
268    }
269    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
270        Ok(Self {
271            table: crate::wire::get_name(input)?,
272            key: get_blob(input)?,
273            audience: Wire::get(input)?,
274        })
275    }
276}
277pub(crate) fn row(row: &RowId) -> Result<(), Error> {
278    name(&row.table)?;
279    crate::key::decode_key(&row.key)?;
280    Ok(())
281}
282
283/// The most bytes D2's `name` rule allows.
284pub(crate) const MAX_NAME: usize = 1024;
285
286/// Check D2's `name` rule: 1 to 1,024 bytes, no NUL. Table, column, store,
287/// device, circle and invited people's names follow it.
288pub fn name(value: &str) -> Result<(), Error> {
289    bound(value.len(), MAX_NAME, "name")?;
290    require(!value.is_empty() && !value.contains('\0'), "name", Rule::Required)
291}
292
293#[cfg(test)]
294#[path = "value_tests.rs"]
295mod tests;