doughnut_rs/doughnut/topping/
module.rs1use super::method::Method;
9use super::WILDCARD;
10use crate::doughnut::topping::topping::MAX_METHODS;
11use alloc::{
12 string::{String, ToString},
13 vec::Vec,
14};
15use codec::{Decode, Encode, Input, Output};
16use core::convert::TryFrom;
17
18const BLOCK_COOLDOWN_MASK: u8 = 0b0000_0001;
19
20#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
22pub struct Module {
23 pub name: String,
24 pub block_cooldown: Option<u32>,
25 pub methods: Vec<Method>,
26}
27
28impl Module {
29 pub fn new(name: &str) -> Self {
30 Self {
31 name: name.into(),
32 block_cooldown: None,
33 methods: Vec::new(),
34 }
35 }
36
37 pub fn block_cooldown(mut self, block_cooldown: u32) -> Self {
38 self.block_cooldown = Some(block_cooldown);
39 self
40 }
41
42 pub fn methods(mut self, methods: Vec<Method>) -> Self {
43 self.methods = methods;
44 self
45 }
46
47 pub fn get_method(&self, method: &str) -> Option<&Method> {
50 let mut outcome: Option<&Method> = None;
51 for m in &self.methods {
52 if m.name == method {
53 outcome = Some(m);
54 break;
55 } else if m.name == WILDCARD {
56 outcome = Some(m);
57 }
58 }
59 outcome
60 }
61}
62
63impl Encode for Module {
64 fn encode_to<T: Output + ?Sized>(&self, buf: &mut T) {
65 if self.methods.is_empty() || self.methods.len() > MAX_METHODS {
66 return;
67 }
68 let method_count = u8::try_from(self.methods.len() - 1);
69 if method_count.is_err() {
70 return;
71 }
72 let mut method_count_and_has_cooldown_byte = method_count.unwrap() << 1;
73 if self.block_cooldown.is_some() {
74 method_count_and_has_cooldown_byte |= BLOCK_COOLDOWN_MASK;
75 }
76 buf.push_byte(method_count_and_has_cooldown_byte);
77
78 let mut name = [0_u8; 32];
79 let length = 32.min(self.name.len());
80 name[0..length].clone_from_slice(&self.name.as_bytes()[0..length]);
81
82 buf.write(&name);
83
84 if let Some(cooldown) = self.block_cooldown {
85 for b in &cooldown.to_le_bytes() {
86 buf.push_byte(*b);
87 }
88 }
89
90 for method in &self.methods {
91 method.encode_to(buf);
92 }
93 }
94}
95
96impl Decode for Module {
97 fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
98 let block_cooldown_and_method_count: u8 = input.read_byte()?;
99 let method_count = (block_cooldown_and_method_count >> 1) + 1;
100
101 let mut name_buf: [u8; 32] = Default::default();
102 input
103 .read(&mut name_buf)
104 .map_err(|_| "expected 32 byte module name")?;
105 let name = core::str::from_utf8(&name_buf)
106 .map_err(|_| codec::Error::from("module names should be utf8 encoded"))?
107 .trim_matches(char::from(0))
108 .to_string();
109
110 let module_cooldown =
111 if (block_cooldown_and_method_count & BLOCK_COOLDOWN_MASK) == BLOCK_COOLDOWN_MASK {
112 Some(u32::from_le_bytes([
113 input.read_byte()?,
114 input.read_byte()?,
115 input.read_byte()?,
116 input.read_byte()?,
117 ]))
118 } else {
119 None
120 };
121
122 let mut methods: Vec<Method> = Vec::default();
123
124 for _ in 0..method_count {
125 let m: Method = Decode::decode(input)?;
126 methods.push(m);
127 }
128
129 Ok(Self {
130 name,
131 block_cooldown: module_cooldown,
132 methods,
133 })
134 }
135}
136
137#[cfg(test)]
138mod test {
139 use super::{Method, Module, BLOCK_COOLDOWN_MASK};
140 use codec::{Decode, Encode};
141 use std::assert_eq;
142
143 macro_rules! methods {
144 ($($name:expr),*) => {
145 vec![
146 $( ( Method::new($name) ), )*
147 ]
148 }
149 }
150
151 #[test]
153 fn it_initializes() {
154 let module = Module::new("TestModule");
155
156 assert_eq!(module.name, "TestModule");
157 assert_eq!(module.block_cooldown, None);
158 assert_eq!(module.methods, vec![]);
159 }
160
161 #[test]
163 fn it_encodes() {
164 let module = Module::new("TestModule").methods(methods!("TestMethod"));
165
166 let expected_name = String::from("TestModule").into_bytes();
167 let remainder = vec![0x00_u8; 32_usize - expected_name.len()];
168 let expected: Vec<u8> = [
169 vec![0_u8],
170 expected_name,
171 remainder,
172 Method::new("TestMethod").encode(),
173 ]
174 .concat();
175
176 assert_eq!(module.encode(), expected);
177 }
178
179 #[test]
180 fn it_does_not_encode_without_methods() {
181 let module = Module::new("TestModule");
182 assert_eq!(module.encode(), Vec::<u8>::default());
183 }
184
185 #[test]
186 fn it_encodes_only_32_characters_for_name() {
187 let module = Module::new("I don't like green eggs and ham, I don't like you Sam I am;")
188 .methods(methods!("TestMethod"));
189 let expected_length = 33 + 33;
190
191 assert_eq!(module.encode().len(), expected_length);
192 }
193
194 #[test]
195 fn it_encodes_with_block_cooldown() {
196 let module = Module::new("TestModule")
197 .methods(methods!("TestMethod"))
198 .block_cooldown(0x10204080);
199
200 let expected_name = String::from("TestModule").into_bytes();
201 let remainder = vec![0x00_u8; 32_usize - expected_name.len()];
202 let expected: Vec<u8> = [
203 vec![BLOCK_COOLDOWN_MASK],
204 expected_name,
205 remainder,
206 vec![0x80, 0x40, 0x20, 0x10],
207 Method::new("TestMethod").encode(),
208 ]
209 .concat();
210
211 assert_eq!(module.encode(), expected);
212 }
213
214 #[test]
215 fn it_encodes_with_many_methods() {
216 let module = Module::new("TestModule")
217 .methods(methods!("I", "do", "not", "like", "them", "Sam", "I am"));
218
219 let expected_name = String::from("TestModule").into_bytes();
220 let remainder = vec![0x00_u8; 32_usize - expected_name.len()];
221 let expected: Vec<u8> = [
222 vec![0x06_u8 << 1], expected_name,
224 remainder,
225 Method::new("I").encode(),
226 Method::new("do").encode(),
227 Method::new("not").encode(),
228 Method::new("like").encode(),
229 Method::new("them").encode(),
230 Method::new("Sam").encode(),
231 Method::new("I am").encode(),
232 ]
233 .concat();
234
235 assert_eq!(module.encode(), expected);
236 }
237
238 #[test]
240 fn it_decodes() {
241 let name_bytes = String::from("TestModule").into_bytes();
242 let remainder = vec![0x00_u8; 32_usize - name_bytes.len()];
243 let encoded: Vec<u8> = [
244 vec![0_u8],
245 name_bytes,
246 remainder,
247 Method::new("TestMethod").encode(),
248 ]
249 .concat();
250
251 let module = Module::decode(&mut &encoded[..]).unwrap();
252 assert_eq!(module.name, "TestModule");
253 assert_eq!(module.block_cooldown, None);
254 assert_eq!(module.methods.len(), 1);
255 }
256
257 #[test]
258 fn decode_fails_with_junk_bytes_in_the_name() {
259 let name_bytes = String::from("TestModule").into_bytes();
260 let remainder = vec![0xf0_u8; 32_usize - name_bytes.len()];
261 let encoded: Vec<u8> = [
262 vec![0_u8],
263 name_bytes,
264 remainder,
265 Method::new("TestMethod").encode(),
266 ]
267 .concat();
268
269 assert_eq!(
270 Module::decode(&mut &encoded[..]),
271 Err(codec::Error::from("module names should be utf8 encoded"))
272 );
273 }
274
275 #[test]
276 fn it_decodes_with_block_cooldown() {
277 let name_bytes = String::from("TestModule").into_bytes();
278 let remainder = vec![0x00_u8; 32_usize - name_bytes.len()];
279 let encoded: Vec<u8> = [
280 vec![BLOCK_COOLDOWN_MASK],
281 name_bytes,
282 remainder,
283 vec![0x80, 0x40, 0x20, 0x10],
284 Method::new("TestMethod").encode(),
285 ]
286 .concat();
287
288 let module = Module::decode(&mut &encoded[..]).unwrap();
289 assert_eq!(module.name, "TestModule");
290 assert_eq!(module.block_cooldown, Some(0x10204080));
291 assert_eq!(module.methods.len(), 1);
292 }
293
294 #[test]
295 fn decode_fails_with_insufficient_bytes_for_block_cooldown() {
296 let name_bytes = String::from("TestModule").into_bytes();
297 let remainder = vec![0x00_u8; 32_usize - name_bytes.len()];
298 let encoded: Vec<u8> = [
299 vec![BLOCK_COOLDOWN_MASK],
300 name_bytes,
301 remainder,
302 vec![0x01, 0x02],
303 Method::new("TestMethod").encode(),
304 ]
305 .concat();
306
307 assert_eq!(
308 Module::decode(&mut &encoded[..]),
309 Err(codec::Error::from("expected 32 byte method name"))
310 );
311 }
312
313 #[test]
314 fn it_decodes_with_many_methods() {
315 let name_bytes = String::from("TestModule").into_bytes();
316 let remainder = vec![0x00_u8; 32_usize - name_bytes.len()];
317 let encoded: Vec<u8> = [
318 vec![0x06_u8 << 1], name_bytes,
320 remainder,
321 Method::new("I").encode(),
322 Method::new("do").encode(),
323 Method::new("not").encode(),
324 Method::new("like").encode(),
325 Method::new("them").encode(),
326 Method::new("Sam").encode(),
327 Method::new("I am").encode(),
328 ]
329 .concat();
330
331 let module = Module::decode(&mut &encoded[..]).unwrap();
332
333 assert_eq!(module.methods[0].name, "I");
334 assert_eq!(module.methods[1].name, "do");
335 assert_eq!(module.methods[2].name, "not");
336 assert_eq!(module.methods[3].name, "like");
337 assert_eq!(module.methods[4].name, "them");
338 assert_eq!(module.methods[5].name, "Sam");
339 assert_eq!(module.methods[6].name, "I am");
340 }
341}