atuin_scripts/store/
record.rs

1use atuin_common::record::DecryptedData;
2use eyre::{Result, eyre};
3use uuid::Uuid;
4
5use crate::store::script::SCRIPT_VERSION;
6
7use super::script::Script;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum ScriptRecord {
11    Create(Script),
12    Update(Script),
13    Delete(Uuid),
14}
15
16impl ScriptRecord {
17    pub fn serialize(&self) -> Result<DecryptedData> {
18        use rmp::encode;
19
20        let mut output = vec![];
21
22        match self {
23            ScriptRecord::Create(script) => {
24                // 0 -> a script create
25                encode::write_u8(&mut output, 0)?;
26
27                let bytes = script.serialize()?;
28
29                encode::write_bin(&mut output, &bytes.0)?;
30            }
31
32            ScriptRecord::Delete(id) => {
33                // 1 -> a script delete
34                encode::write_u8(&mut output, 1)?;
35                encode::write_str(&mut output, id.to_string().as_str())?;
36            }
37
38            ScriptRecord::Update(script) => {
39                // 2 -> a script update
40                encode::write_u8(&mut output, 2)?;
41                let bytes = script.serialize()?;
42                encode::write_bin(&mut output, &bytes.0)?;
43            }
44        };
45
46        Ok(DecryptedData(output))
47    }
48
49    pub fn deserialize(data: &DecryptedData, version: &str) -> Result<Self> {
50        use rmp::decode;
51
52        fn error_report<E: std::fmt::Debug>(err: E) -> eyre::Report {
53            eyre!("{err:?}")
54        }
55
56        match version {
57            SCRIPT_VERSION => {
58                let mut bytes = decode::Bytes::new(&data.0);
59
60                let record_type = decode::read_u8(&mut bytes).map_err(error_report)?;
61
62                match record_type {
63                    // create
64                    0 => {
65                        // written by encode::write_bin above
66                        let _ = decode::read_bin_len(&mut bytes).map_err(error_report)?;
67                        let script = Script::deserialize(bytes.remaining_slice())?;
68                        Ok(ScriptRecord::Create(script))
69                    }
70
71                    // delete
72                    1 => {
73                        let bytes = bytes.remaining_slice();
74                        let (id, _) = decode::read_str_from_slice(bytes).map_err(error_report)?;
75                        Ok(ScriptRecord::Delete(Uuid::parse_str(id)?))
76                    }
77
78                    // update
79                    2 => {
80                        // written by encode::write_bin above
81                        let _ = decode::read_bin_len(&mut bytes).map_err(error_report)?;
82                        let script = Script::deserialize(bytes.remaining_slice())?;
83                        Ok(ScriptRecord::Update(script))
84                    }
85
86                    _ => Err(eyre!("unknown script record type {record_type}")),
87                }
88            }
89            _ => Err(eyre!("unknown version {version:?}")),
90        }
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_serialize_create() {
100        let script = Script::builder()
101            .id(uuid::Uuid::parse_str("0195c825a35f7982bdb016168881cbc6").unwrap())
102            .name("test".to_string())
103            .description("test".to_string())
104            .shebang("test".to_string())
105            .tags(vec!["test".to_string()])
106            .script("test".to_string())
107            .build();
108
109        let record = ScriptRecord::Create(script);
110
111        let serialized = record.serialize().unwrap();
112
113        assert_eq!(
114            serialized.0,
115            vec![
116                204, 0, 196, 65, 150, 217, 36, 48, 49, 57, 53, 99, 56, 50, 53, 45, 97, 51, 53, 102,
117                45, 55, 57, 56, 50, 45, 98, 100, 98, 48, 45, 49, 54, 49, 54, 56, 56, 56, 49, 99,
118                98, 99, 54, 164, 116, 101, 115, 116, 164, 116, 101, 115, 116, 164, 116, 101, 115,
119                116, 145, 164, 116, 101, 115, 116, 164, 116, 101, 115, 116
120            ]
121        );
122    }
123
124    #[test]
125    fn test_serialize_delete() {
126        let record = ScriptRecord::Delete(
127            uuid::Uuid::parse_str("0195c825a35f7982bdb016168881cbc6").unwrap(),
128        );
129
130        let serialized = record.serialize().unwrap();
131
132        assert_eq!(
133            serialized.0,
134            vec![
135                204, 1, 217, 36, 48, 49, 57, 53, 99, 56, 50, 53, 45, 97, 51, 53, 102, 45, 55, 57,
136                56, 50, 45, 98, 100, 98, 48, 45, 49, 54, 49, 54, 56, 56, 56, 49, 99, 98, 99, 54
137            ]
138        );
139    }
140
141    #[test]
142    fn test_serialize_update() {
143        let script = Script::builder()
144            .id(uuid::Uuid::parse_str("0195c825a35f7982bdb016168881cbc6").unwrap())
145            .name(String::from("test"))
146            .description(String::from("test"))
147            .shebang(String::from("test"))
148            .tags(vec![String::from("test"), String::from("test2")])
149            .script(String::from("test"))
150            .build();
151
152        let record = ScriptRecord::Update(script);
153
154        let serialized = record.serialize().unwrap();
155
156        assert_eq!(
157            serialized.0,
158            vec![
159                204, 2, 196, 71, 150, 217, 36, 48, 49, 57, 53, 99, 56, 50, 53, 45, 97, 51, 53, 102,
160                45, 55, 57, 56, 50, 45, 98, 100, 98, 48, 45, 49, 54, 49, 54, 56, 56, 56, 49, 99,
161                98, 99, 54, 164, 116, 101, 115, 116, 164, 116, 101, 115, 116, 164, 116, 101, 115,
162                116, 146, 164, 116, 101, 115, 116, 165, 116, 101, 115, 116, 50, 164, 116, 101, 115,
163                116
164            ],
165        );
166    }
167
168    #[test]
169    fn test_serialize_deserialize_create() {
170        let script = Script::builder()
171            .name("test".to_string())
172            .description("test".to_string())
173            .shebang("test".to_string())
174            .tags(vec!["test".to_string()])
175            .script("test".to_string())
176            .build();
177
178        let record = ScriptRecord::Create(script);
179
180        let serialized = record.serialize().unwrap();
181        let deserialized = ScriptRecord::deserialize(&serialized, SCRIPT_VERSION).unwrap();
182
183        assert_eq!(record, deserialized);
184    }
185
186    #[test]
187    fn test_serialize_deserialize_delete() {
188        let record = ScriptRecord::Delete(
189            uuid::Uuid::parse_str("0195c825a35f7982bdb016168881cbc6").unwrap(),
190        );
191
192        let serialized = record.serialize().unwrap();
193        let deserialized = ScriptRecord::deserialize(&serialized, SCRIPT_VERSION).unwrap();
194
195        assert_eq!(record, deserialized);
196    }
197
198    #[test]
199    fn test_serialize_deserialize_update() {
200        let script = Script::builder()
201            .name("test".to_string())
202            .description("test".to_string())
203            .shebang("test".to_string())
204            .tags(vec!["test".to_string()])
205            .script("test".to_string())
206            .build();
207
208        let record = ScriptRecord::Update(script);
209
210        let serialized = record.serialize().unwrap();
211        let deserialized = ScriptRecord::deserialize(&serialized, SCRIPT_VERSION).unwrap();
212
213        assert_eq!(record, deserialized);
214    }
215}