luminol_data/shared/
script.rs

1// Copyright (C) 2024 Melody Madeline Lyons
2//
3// This file is part of Luminol.
4//
5// Luminol is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Luminol is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Luminol.  If not, see <http://www.gnu.org/licenses/>.
17
18use base64::Engine;
19use rand::Rng;
20
21#[allow(missing_docs)]
22#[derive(Debug, Clone)]
23pub struct Script {
24    pub id: u32,
25    pub name: String,
26    pub script_text: String,
27}
28
29impl Script {
30    /// Creates a new `Script` with a random ID.
31    pub fn new(name: impl Into<String>, script_text: impl Into<String>) -> Self {
32        Self {
33            id: rand::thread_rng().gen_range(0..=99999999),
34            name: name.into(),
35            script_text: script_text.into(),
36        }
37    }
38}
39
40impl<'de> alox_48::Deserialize<'de> for Script {
41    fn deserialize<D>(deserializer: D) -> Result<Self, alox_48::DeError>
42    where
43        D: alox_48::DeserializerTrait<'de>,
44    {
45        struct Visitor;
46
47        impl<'de> alox_48::Visitor<'de> for Visitor {
48            type Value = Script;
49
50            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51                formatter.write_str("an array")
52            }
53
54            fn visit_array<A>(self, mut array: A) -> Result<Self::Value, alox_48::DeError>
55            where
56                A: alox_48::ArrayAccess<'de>,
57            {
58                use std::io::Read;
59
60                let Some(id) = array.next_element()? else {
61                    return Err(alox_48::DeError::missing_field("id".into()));
62                };
63
64                let Some(name) = array.next_element()? else {
65                    return Err(alox_48::DeError::missing_field("name".into()));
66                };
67
68                let Some(data) = array.next_element::<alox_48::RbString>()? else {
69                    return Err(alox_48::DeError::missing_field("data".into()));
70                };
71
72                let mut decoder = flate2::bufread::ZlibDecoder::new(data.data.as_slice());
73                let mut script = String::new();
74                decoder
75                    .read_to_string(&mut script)
76                    .map_err(alox_48::DeError::custom)?;
77
78                Ok(Script {
79                    id,
80                    name,
81                    script_text: script,
82                })
83            }
84        }
85
86        deserializer.deserialize(Visitor)
87    }
88}
89
90impl alox_48::Serialize for Script {
91    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, alox_48::SerError>
92    where
93        S: alox_48::SerializerTrait,
94    {
95        use alox_48::SerializeArray;
96        use std::io::Write;
97
98        let mut array = serializer.serialize_array(3)?;
99
100        let mut encoder = flate2::write::ZlibEncoder::new(Vec::new(), Default::default());
101        let data = encoder
102            .write_all(self.script_text.as_bytes())
103            .and_then(|_| encoder.finish())
104            .map_err(alox_48::SerError::custom)?;
105
106        array.serialize_element(&self.id)?;
107        array.serialize_element(&self.name)?;
108        array.serialize_element(&alox_48::RbString { data })?;
109
110        array.end()
111    }
112}
113
114impl<'de> serde::Deserialize<'de> for Script {
115    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
116    where
117        D: serde::Deserializer<'de>,
118    {
119        struct Visitor;
120
121        impl<'de> serde::de::Visitor<'de> for Visitor {
122            type Value = Script;
123
124            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125                formatter.write_str("a key-value mapping")
126            }
127
128            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
129            where
130                A: serde::de::MapAccess<'de>,
131            {
132                use serde::de::Error;
133                use std::io::Read;
134
135                let mut id = None;
136                let mut name = None;
137                let mut data = None;
138
139                while let Some(key) = map.next_key::<String>()? {
140                    match key.as_str() {
141                        "id" => id = Some(map.next_value()?),
142                        "name" => name = Some(map.next_value()?),
143                        "data" => data = Some(map.next_value::<String>()?),
144                        _ => {}
145                    }
146                }
147
148                let Some(name) = name else {
149                    return Err(A::Error::missing_field("name"));
150                };
151
152                let Some(data) = data else {
153                    return Err(A::Error::missing_field("data"));
154                };
155
156                let mut decoder = flate2::bufread::ZlibDecoder::new(std::io::Cursor::new(
157                    base64::engine::general_purpose::STANDARD
158                        .decode(data)
159                        .map_err(A::Error::custom)?,
160                ));
161                let mut script = String::new();
162                decoder
163                    .read_to_string(&mut script)
164                    .map_err(A::Error::custom)?;
165
166                Ok(if let Some(id) = id {
167                    Script {
168                        id,
169                        name,
170                        script_text: script,
171                    }
172                } else {
173                    Script::new(name, script)
174                })
175            }
176        }
177
178        deserializer.deserialize_any(Visitor)
179    }
180}
181
182impl serde::Serialize for Script {
183    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
184    where
185        S: serde::Serializer,
186    {
187        use serde::ser::Error;
188        use serde::ser::SerializeMap;
189        use std::io::Write;
190
191        let mut map = serializer.serialize_map(Some(3))?;
192
193        let mut encoder = flate2::write::ZlibEncoder::new(Vec::new(), Default::default());
194        let data = encoder
195            .write_all(self.script_text.as_bytes())
196            .and_then(|_| encoder.finish())
197            .map_err(S::Error::custom)?;
198
199        map.serialize_entry("id", &self.id)?;
200        map.serialize_entry("name", &self.name)?;
201        map.serialize_entry(
202            "data",
203            &base64::engine::general_purpose::STANDARD.encode(data),
204        )?;
205
206        map.end()
207    }
208}