1use core::{fmt, iter::DoubleEndedIterator};
2
3use buggy::Bug;
4use tracing::error;
5
6use crate::{
7 Address, CmdId, Command, GraphId, LocatedAddress, PeerCache, Perspective as _, Policy,
8 PolicyError, PolicyStore, Segment as _, Sink, Storage as _, StorageError, StorageProvider,
9 TraversalBuffer,
10 policy::ActionPlacement,
11 storage::{HeadSet, Spill},
12};
13
14pub(crate) mod braiding; mod buffers;
16pub(crate) mod convergence_map; mod session;
18mod transaction;
19
20pub(crate) use buffers::BraidBuffer;
21pub use buffers::RuntimeBuffers;
22
23pub use self::{session::Session, transaction::Transaction};
24
25#[derive(Debug, thiserror::Error)]
27#[non_exhaustive]
28pub enum ClientError {
29 #[error("no such parent: {0}")]
30 NoSuchParent(CmdId),
31 #[error("policy error: {0}")]
32 PolicyError(#[from] PolicyError),
33 #[error("storage error: {0}")]
34 StorageError(#[from] StorageError),
35 #[error("init error")]
36 InitError,
37 #[error("could not deserialize session command")]
38 SessionDeserialize,
39 #[error("found parallel finalize commands during braid")]
47 ParallelFinalize,
48 #[error("concurrent transaction usage")]
49 ConcurrentTransaction,
50 #[error(transparent)]
51 Bug(#[from] Bug),
52}
53
54pub struct ClientState<PS, SP> {
59 policy_store: PS,
60 provider: SP,
61}
62
63impl<PS: fmt::Debug, SP: fmt::Debug> fmt::Debug for ClientState<PS, SP> {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 f.debug_struct("ClientState")
67 .field("policy_store", &self.policy_store)
68 .field("provider", &self.provider)
69 .finish_non_exhaustive()
70 }
71}
72
73impl<PS, SP> ClientState<PS, SP> {
74 pub const fn new(policy_store: PS, provider: SP) -> Self {
76 Self {
77 policy_store,
78 provider,
79 }
80 }
81
82 pub fn provider(&mut self) -> &mut SP {
84 &mut self.provider
85 }
86}
87
88impl<PS, SP> ClientState<PS, SP>
89where
90 PS: PolicyStore,
91 SP: StorageProvider,
92{
93 pub fn new_graph(
98 &mut self,
99 policy_data: &[u8],
100 action: <PS::Policy as Policy>::Action<'_>,
101 sink: &mut impl Sink<PS::Effect>,
102 ) -> Result<GraphId, ClientError> {
103 let policy_id = self.policy_store.add_policy(policy_data)?;
104 let policy = self.policy_store.get_policy(policy_id)?;
105
106 let mut perspective = self.provider.new_perspective(policy_id);
107 sink.begin();
108 policy
109 .call_action(action, &mut perspective, sink, ActionPlacement::OnGraph)
110 .inspect_err(|_| sink.rollback())?;
111 sink.commit();
112
113 let (graph_id, _) = self.provider.new_storage(perspective)?;
114
115 Ok(graph_id)
116 }
117
118 pub fn remove_graph(&mut self, graph_id: GraphId) -> Result<(), ClientError> {
120 self.provider.remove_storage(graph_id)?;
121
122 Ok(())
123 }
124
125 pub fn commit<F, MS>(
134 &mut self,
135 trx: Transaction<SP, PS>,
136 sink: &mut impl Sink<PS::Effect>,
137 buffers: &mut RuntimeBuffers<SP::Segment>,
138 make_spill: MS,
139 ) -> Result<bool, ClientError>
140 where
141 F: Spill,
142 MS: Fn() -> Result<F, StorageError>,
143 {
144 trx.commit::<F, MS>(
145 &mut self.provider,
146 &mut self.policy_store,
147 sink,
148 buffers,
149 &make_spill,
150 )
151 }
152
153 pub fn add_commands<F, MS>(
161 &mut self,
162 trx: &mut Transaction<SP, PS>,
163 sink: &mut impl Sink<PS::Effect>,
164 commands: &[impl Command],
165 buffers: &mut RuntimeBuffers<SP::Segment>,
166 make_spill: MS,
167 ) -> Result<usize, ClientError>
168 where
169 F: Spill,
170 MS: Fn() -> Result<F, StorageError>,
171 {
172 trx.add_commands::<F, MS>(
173 commands,
174 &mut self.provider,
175 &mut self.policy_store,
176 sink,
177 buffers,
178 &make_spill,
179 )
180 }
181
182 pub fn update_heads<I>(
183 &mut self,
184 graph_id: GraphId,
185 addrs: I,
186 request_heads: &mut PeerCache,
187 buffer: &mut TraversalBuffer,
188 ) -> Result<(), ClientError>
189 where
190 I: IntoIterator<Item = Address>,
191 I::IntoIter: DoubleEndedIterator,
192 {
193 let storage = self.provider.get_storage(graph_id)?;
194
195 for address in addrs.into_iter().rev() {
199 request_heads.add_command(storage, address, buffer)?;
200 }
201
202 Ok(())
203 }
204
205 pub fn head_address(&mut self, graph_id: GraphId) -> Result<Address, ClientError> {
207 let storage = self.provider.get_storage(graph_id)?;
208 let address = storage.get_head_address()?;
209 Ok(address)
210 }
211
212 pub fn hello_head(&mut self, graph_id: GraphId) -> Result<Address, ClientError> {
221 let storage = &*self.provider.get_storage(graph_id)?;
222 let heads = storage.get_heads()?;
223 transaction::synthetic_head(storage, &self.policy_store, heads)
224 }
225
226 pub fn should_sync_on_hello(
236 &mut self,
237 graph_id: GraphId,
238 head: Address,
239 buffer: &mut TraversalBuffer,
240 ) -> Result<bool, ClientError> {
241 match self.provider.get_storage(graph_id) {
242 Err(StorageError::NoSuchStorage) => return Ok(true),
243 Err(e) => return Err(e.into()),
244 Ok(_) => {}
245 }
246 if self.hello_head(graph_id)? == head {
247 return Ok(false);
248 }
249 let storage = self.provider.get_storage(graph_id)?;
250 Ok(storage.get_location(head, buffer)?.is_none())
251 }
252
253 pub fn action<F, MS>(
259 &mut self,
260 graph_id: GraphId,
261 sink: &mut impl Sink<PS::Effect>,
262 action: <PS::Policy as Policy>::Action<'_>,
263 buffers: &mut RuntimeBuffers<SP::Segment>,
264 make_spill: MS,
265 ) -> Result<(), ClientError>
266 where
267 F: Spill,
268 MS: Fn() -> Result<F, StorageError>,
269 {
270 let storage = self.provider.get_storage(graph_id)?;
271
272 let heads = storage.get_heads()?.clone();
275 let head = transaction::collapse_heads::<_, PS, F, _>(
276 storage,
277 &mut self.policy_store,
278 heads,
279 buffers,
280 &make_spill,
281 )?;
282
283 let mut perspective = storage.get_linear_perspective(head)?;
284
285 let policy_id = perspective.policy();
286 let policy = self.policy_store.get_policy(policy_id)?;
287
288 sink.begin();
292 match policy.call_action(action, &mut perspective, sink, ActionPlacement::OnGraph) {
293 Ok(()) => {
294 let segment = storage.write(perspective)?;
295 let new_head = LocatedAddress {
296 id: segment.head_id(),
297 segment: segment.index(),
298 max_cut: segment.longest_max_cut()?,
299 };
300 let fact_cache = segment.facts()?;
301 storage.commit_heads(HeadSet::single(new_head), fact_cache)?;
302 sink.commit();
303 Ok(())
304 }
305 Err(e) => {
306 sink.rollback();
307 Err(e.into())
308 }
309 }
310 }
311}
312
313impl<PS, SP> ClientState<PS, SP>
314where
315 SP: StorageProvider,
316{
317 pub fn transaction(&mut self, graph_id: GraphId) -> Transaction<SP, PS> {
319 Transaction::new(graph_id)
320 }
321
322 pub fn session(&mut self, graph_id: GraphId) -> Result<Session<SP, PS>, ClientError> {
324 Session::new(&mut self.provider, graph_id)
325 }
326
327 pub fn command_exists(
332 &mut self,
333 graph_id: GraphId,
334 address: Address,
335 buffer: &mut TraversalBuffer,
336 ) -> bool {
337 let Ok(storage) = self.provider.get_storage(graph_id) else {
338 return false;
340 };
341 storage
342 .get_location(address, buffer)
343 .unwrap_or(None)
344 .is_some()
345 }
346}