1use atuin_domain::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 use rstest::rstest;
98
99 #[test]
100 fn test_serialize_create() {
101 let script = Script::builder()
102 .id(uuid::Uuid::parse_str("0195c825a35f7982bdb016168881cbc6").unwrap())
103 .name("test".to_string())
104 .description("test".to_string())
105 .shebang("test".to_string())
106 .tags(vec!["test".to_string()])
107 .script("test".to_string())
108 .build();
109
110 let record = ScriptRecord::Create(script);
111
112 let serialized = record.serialize().unwrap();
113
114 assert_eq!(
115 serialized.0,
116 vec![
117 204, 0, 196, 65, 150, 217, 36, 48, 49, 57, 53, 99, 56, 50, 53, 45, 97, 51, 53, 102,
118 45, 55, 57, 56, 50, 45, 98, 100, 98, 48, 45, 49, 54, 49, 54, 56, 56, 56, 49, 99,
119 98, 99, 54, 164, 116, 101, 115, 116, 164, 116, 101, 115, 116, 164, 116, 101, 115,
120 116, 145, 164, 116, 101, 115, 116, 164, 116, 101, 115, 116
121 ]
122 );
123 }
124
125 #[test]
126 fn test_serialize_delete() {
127 let record = ScriptRecord::Delete(
128 uuid::Uuid::parse_str("0195c825a35f7982bdb016168881cbc6").unwrap(),
129 );
130
131 let serialized = record.serialize().unwrap();
132
133 assert_eq!(
134 serialized.0,
135 vec![
136 204, 1, 217, 36, 48, 49, 57, 53, 99, 56, 50, 53, 45, 97, 51, 53, 102, 45, 55, 57,
137 56, 50, 45, 98, 100, 98, 48, 45, 49, 54, 49, 54, 56, 56, 56, 49, 99, 98, 99, 54
138 ]
139 );
140 }
141
142 #[test]
143 fn test_serialize_update() {
144 let script = Script::builder()
145 .id(uuid::Uuid::parse_str("0195c825a35f7982bdb016168881cbc6").unwrap())
146 .name(String::from("test"))
147 .description(String::from("test"))
148 .shebang(String::from("test"))
149 .tags(vec![String::from("test"), String::from("test2")])
150 .script(String::from("test"))
151 .build();
152
153 let record = ScriptRecord::Update(script);
154
155 let serialized = record.serialize().unwrap();
156
157 assert_eq!(
158 serialized.0,
159 vec![
160 204, 2, 196, 71, 150, 217, 36, 48, 49, 57, 53, 99, 56, 50, 53, 45, 97, 51, 53, 102,
161 45, 55, 57, 56, 50, 45, 98, 100, 98, 48, 45, 49, 54, 49, 54, 56, 56, 56, 49, 99,
162 98, 99, 54, 164, 116, 101, 115, 116, 164, 116, 101, 115, 116, 164, 116, 101, 115,
163 116, 146, 164, 116, 101, 115, 116, 165, 116, 101, 115, 116, 50, 164, 116, 101, 115,
164 116
165 ],
166 );
167 }
168
169 #[rstest]
170 #[case::create(ScriptRecord::Create(
171 Script::builder()
172 .name("test".to_string())
173 .description("test".to_string())
174 .shebang("test".to_string())
175 .tags(vec!["test".to_string()])
176 .script("test".to_string())
177 .build(),
178 ))]
179 #[case::delete(ScriptRecord::Delete(
180 uuid::Uuid::parse_str("0195c825a35f7982bdb016168881cbc6").unwrap(),
181 ))]
182 #[case::update(ScriptRecord::Update(
183 Script::builder()
184 .name("test".to_string())
185 .description("test".to_string())
186 .shebang("test".to_string())
187 .tags(vec!["test".to_string()])
188 .script("test".to_string())
189 .build(),
190 ))]
191 fn serialize_deserialize(#[case] record: ScriptRecord) {
192 let serialized = record.serialize().unwrap();
193 let deserialized = ScriptRecord::deserialize(&serialized, SCRIPT_VERSION).unwrap();
194
195 assert_eq!(record, deserialized);
196 }
197}