open_gpui_canvas/persistence/
byte_store.rs1use super::{
2 CanvasCheckpoint, CanvasJsonPersistenceCodec, CanvasLogEntry, CanvasPersistenceCodec,
3 CanvasPersistenceStore,
4};
5use std::{error::Error, fmt};
6
7#[derive(Clone, Debug, Eq, PartialEq)]
8pub struct CanvasEncodedLogEntry {
9 pub sequence: u64,
10 pub bytes: Vec<u8>,
11}
12
13impl CanvasEncodedLogEntry {
14 pub fn new(sequence: u64, bytes: impl Into<Vec<u8>>) -> Self {
15 Self {
16 sequence,
17 bytes: bytes.into(),
18 }
19 }
20}
21
22pub trait CanvasPersistenceByteStore {
23 type Error: fmt::Debug + fmt::Display;
24
25 fn load_checkpoint_bytes(&self) -> Result<Option<Vec<u8>>, Self::Error>;
26
27 fn save_checkpoint_bytes(&mut self, bytes: Vec<u8>) -> Result<(), Self::Error>;
28
29 fn append_log_entry_bytes(&mut self, sequence: u64, bytes: Vec<u8>) -> Result<(), Self::Error>;
30
31 fn load_log_entry_bytes(
32 &self,
33 after_sequence: u64,
34 ) -> Result<Vec<CanvasEncodedLogEntry>, Self::Error>;
35
36 fn compact_log_entry_bytes(&mut self, through_sequence: u64) -> Result<(), Self::Error>;
37}
38
39#[derive(Debug, Eq, PartialEq)]
40pub enum CanvasPersistenceByteStoreError<StoreError, CodecError> {
41 Store(StoreError),
42 Codec(CodecError),
43 LogSequenceMismatch { key: u64, payload: u64 },
44}
45
46impl<StoreError, CodecError> fmt::Display
47 for CanvasPersistenceByteStoreError<StoreError, CodecError>
48where
49 StoreError: fmt::Display,
50 CodecError: fmt::Display,
51{
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 match self {
54 Self::Store(error) => write!(f, "canvas persistence byte store error: {error}"),
55 Self::Codec(error) => fmt::Display::fmt(error, f),
56 Self::LogSequenceMismatch { key, payload } => write!(
57 f,
58 "canvas persistence log key sequence `{key}` does not match payload sequence `{payload}`"
59 ),
60 }
61 }
62}
63
64impl<StoreError, CodecError> Error for CanvasPersistenceByteStoreError<StoreError, CodecError>
65where
66 StoreError: fmt::Debug + fmt::Display,
67 CodecError: fmt::Debug + fmt::Display,
68{
69}
70
71#[derive(Clone, Debug)]
72pub struct CanvasPersistenceByteStoreAdapter<S, C = CanvasJsonPersistenceCodec> {
73 store: S,
74 codec: C,
75}
76
77impl<S> CanvasPersistenceByteStoreAdapter<S, CanvasJsonPersistenceCodec> {
78 pub fn new(store: S) -> Self {
79 Self::with_codec(store, CanvasJsonPersistenceCodec)
80 }
81}
82
83impl<S, C> CanvasPersistenceByteStoreAdapter<S, C> {
84 pub fn with_codec(store: S, codec: C) -> Self {
85 Self { store, codec }
86 }
87
88 pub fn store(&self) -> &S {
89 &self.store
90 }
91
92 pub fn store_mut(&mut self) -> &mut S {
93 &mut self.store
94 }
95
96 pub fn into_store(self) -> S {
97 self.store
98 }
99
100 pub fn codec(&self) -> &C {
101 &self.codec
102 }
103}
104
105impl<S, C> CanvasPersistenceStore for CanvasPersistenceByteStoreAdapter<S, C>
106where
107 S: CanvasPersistenceByteStore,
108 C: CanvasPersistenceCodec,
109{
110 type Error = CanvasPersistenceByteStoreError<S::Error, C::Error>;
111
112 fn load_checkpoint(&self) -> Result<Option<CanvasCheckpoint>, Self::Error> {
113 self.store
114 .load_checkpoint_bytes()
115 .map_err(CanvasPersistenceByteStoreError::Store)?
116 .map(|bytes| {
117 self.codec
118 .decode_checkpoint(&bytes)
119 .map_err(CanvasPersistenceByteStoreError::Codec)
120 })
121 .transpose()
122 }
123
124 fn save_checkpoint(&mut self, checkpoint: CanvasCheckpoint) -> Result<(), Self::Error> {
125 let bytes = self
126 .codec
127 .encode_checkpoint(&checkpoint)
128 .map_err(CanvasPersistenceByteStoreError::Codec)?;
129 self.store
130 .save_checkpoint_bytes(bytes)
131 .map_err(CanvasPersistenceByteStoreError::Store)
132 }
133
134 fn append_log_entry(&mut self, entry: CanvasLogEntry) -> Result<(), Self::Error> {
135 let sequence = entry.sequence();
136 let bytes = self
137 .codec
138 .encode_log_entry(&entry)
139 .map_err(CanvasPersistenceByteStoreError::Codec)?;
140 self.store
141 .append_log_entry_bytes(sequence, bytes)
142 .map_err(CanvasPersistenceByteStoreError::Store)
143 }
144
145 fn load_log_entries(&self, after_sequence: u64) -> Result<Vec<CanvasLogEntry>, Self::Error> {
146 self.store
147 .load_log_entry_bytes(after_sequence)
148 .map_err(CanvasPersistenceByteStoreError::Store)?
149 .into_iter()
150 .map(|encoded| {
151 let entry = self
152 .codec
153 .decode_log_entry(&encoded.bytes)
154 .map_err(CanvasPersistenceByteStoreError::Codec)?;
155 if entry.sequence() != encoded.sequence {
156 return Err(CanvasPersistenceByteStoreError::LogSequenceMismatch {
157 key: encoded.sequence,
158 payload: entry.sequence(),
159 });
160 }
161 Ok(entry)
162 })
163 .collect()
164 }
165
166 fn compact_log_entries(&mut self, through_sequence: u64) -> Result<(), Self::Error> {
167 self.store
168 .compact_log_entry_bytes(through_sequence)
169 .map_err(CanvasPersistenceByteStoreError::Store)
170 }
171}