1use 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#[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#[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 pub const UNIX_EPOCH: Self = Self { seconds: 0, nanos: 0 };
124
125 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 #[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 pub fn since_epoch(since: std::time::Duration) -> Self {
145 Self { seconds: since.as_secs(), nanos: since.subsec_nanos() }
146 }
147
148 pub fn elapsed_since_epoch(self) -> std::time::Duration {
150 std::time::Duration::new(self.seconds, self.nanos)
151 }
152
153 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 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 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 pub fn seconds(self) -> u64 {
170 self.seconds
171 }
172
173 pub fn nanos(self) -> u32 {
175 self.nanos
176 }
177}
178
179impl 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
194impl 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#[derive(Clone, Debug, PartialEq, Eq)]
209pub enum Value {
210 Null,
212 Integer(i64),
214 Real(u64),
216 Text(String),
218 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
283pub(crate) const MAX_NAME: usize = 1024;
285
286pub 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;