1use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7use crate::sync::store_commit::StoreBatchCommitRef;
8
9#[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#[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#[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#[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#[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#[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#[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#[derive(Debug, Clone, PartialEq, Eq)]
149pub struct WriteReceipt<R> {
150 pub value: R,
151 pub write_id: WriteId,
152 pub status: WriteStatus,
153}