Skip to main content

open_gpui_canvas/persistence/
byte_store.rs

1use 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}