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 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 encode::write_u8(&mut output, 1)?;
35 encode::write_str(&mut output, id.to_string().as_str())?;
36 }
37
38 ScriptRecord::Update(script) => {
39 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 0 => {
65 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 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 2 => {
80 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}