Skip to main content

coven_core/
write.rs

1//! Durable identity and publication status for one host transaction.
2
3use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7use crate::sync::store_commit::StoreBatchCommitRef;
8
9/// Stable identity of one successfully committed host transaction.
10#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
11#[serde(transparent)]
12pub struct WriteId(String);
13
14impl WriteId {
15    pub(crate) fn from_generated(value: String) -> Self {
16        Self(value)
17    }
18
19    pub fn as_str(&self) -> &str {
20        &self.0
21    }
22}
23
24impl fmt::Display for WriteId {
25    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
26        formatter.write_str(&self.0)
27    }
28}
29
30/// Exact position that made a write visible to peers.
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
32#[serde(deny_unknown_fields)]
33pub struct PublishedPosition {
34    pub device_id: String,
35    pub commit: StoreBatchCommitRef,
36}
37
38impl PublishedPosition {
39    pub fn commit(&self) -> &StoreBatchCommitRef {
40        &self.commit
41    }
42}
43
44/// A semantic write fault. Retrying transport cannot change this result.
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(rename_all = "snake_case", deny_unknown_fields)]
47pub enum WriteBlock {
48    InvalidPackage {
49        reason: String,
50    },
51    InvalidProtocolState {
52        reason: String,
53    },
54    MissingBlob {
55        namespace: String,
56        id: String,
57    },
58    LocalUserBlob {
59        namespace: String,
60        id: String,
61    },
62    RotationRequired {
63        circle_id: crate::sync::circle::CircleId,
64        removed_members: Vec<String>,
65    },
66}
67
68/// Current durable state of one host transaction.
69#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
70#[serde(rename_all = "snake_case", deny_unknown_fields)]
71pub enum WriteStatus {
72    LocalOnly,
73    Pending,
74    Publishing,
75    Published(Box<PublishedPosition>),
76    Blocked(WriteBlock),
77    Resolved(WriteResolution),
78}
79
80#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
81#[serde(rename_all = "snake_case", deny_unknown_fields)]
82pub enum WriteResolution {
83    Discarded,
84    Replaced { replacement: WriteId },
85    Retracted { witness: WriteRetractionWitness },
86}
87
88/// Durable proof that a previously published write cannot activate.
89#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
90#[serde(deny_unknown_fields)]
91pub struct WriteRetractionWitness {
92    original: PublishedPosition,
93    nonactivation: crate::sync::remote_object::CandidateNonactivation,
94}
95
96impl WriteRetractionWitness {
97    pub(crate) fn new(
98        original: PublishedPosition,
99        nonactivation: crate::sync::remote_object::CandidateNonactivation,
100    ) -> Result<Self, String> {
101        let candidate = nonactivation
102            .reference()
103            .map_err(|error| error.to_string())?;
104        if original.commit() != &candidate {
105            return Err("write retraction proof names another published commit".to_string());
106        }
107        let witness = Self {
108            original,
109            nonactivation,
110        };
111        witness.validate()?;
112        Ok(witness)
113    }
114
115    pub fn original_position(&self) -> &PublishedPosition {
116        &self.original
117    }
118
119    pub(crate) fn validate(&self) -> Result<(), String> {
120        let candidate = self
121            .nonactivation
122            .reference()
123            .map_err(|error| error.to_string())?;
124        if self.original.commit() != &candidate {
125            return Err("write retraction proof names another published commit".to_string());
126        }
127        Ok(())
128    }
129}
130
131/// One table/primary-key identity affected by the shared part of a write.
132#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
133#[serde(deny_unknown_fields)]
134pub struct AffectedRow {
135    pub table: String,
136    pub primary_key: String,
137}
138
139/// Durable write information returned by `CovenHandle::pending_writes`.
140#[derive(Debug, Clone, PartialEq, Eq)]
141pub struct PendingWrite {
142    pub write_id: WriteId,
143    pub status: WriteStatus,
144    pub affected_rows: Vec<AffectedRow>,
145}
146
147/// Result of one successful host transaction and its durable publication identity.
148#[derive(Debug, Clone, PartialEq, Eq)]
149pub struct WriteReceipt<R> {
150    pub value: R,
151    pub write_id: WriteId,
152    pub status: WriteStatus,
153}