vertigo/driver_module/js_value/
js_value_struct.rs

1use std::collections::HashMap;
2
3use super::{
4    js_json_struct::{decode_js_json_inner, JsJson},
5    js_value_list_decoder::JsValueListDecoder,
6    memory_block::MemoryBlock,
7    memory_block_read::MemoryBlockRead,
8    memory_block_write::MemoryBlockWrite,
9};
10
11const PARAM_TYPE: u32 = 1;
12const STRING_SIZE: u32 = 4;
13const VEC_SIZE: u32 = 4;
14const LIST_COUNT: u32 = 4;
15const OBJECT_COUNT: u32 = 2;
16
17enum JsValueConst {
18    U32 = 1,
19    I32 = 2,
20    U64 = 3,
21    I64 = 4,
22    F64 = 5,
23
24    True = 6,
25    False = 7,
26    Null = 8,
27    Undefined = 9,
28
29    Vec = 10,
30    String = 11,
31    List = 12,
32    Object = 13,
33    Json = 14,
34}
35
36impl JsValueConst {
37    fn from_byte(byte: u8) -> Option<JsValueConst> {
38        match byte {
39            1 => Some(JsValueConst::U32),
40            2 => Some(JsValueConst::I32),
41            3 => Some(JsValueConst::U64),
42            4 => Some(JsValueConst::I64),
43            5 => Some(JsValueConst::F64),
44            6 => Some(JsValueConst::True),
45            7 => Some(JsValueConst::False),
46            8 => Some(JsValueConst::Null),
47            9 => Some(JsValueConst::Undefined),
48            10 => Some(JsValueConst::Vec),
49            11 => Some(JsValueConst::String),
50            12 => Some(JsValueConst::List),
51            13 => Some(JsValueConst::Object),
52            14 => Some(JsValueConst::Json),
53            _ => None,
54        }
55    }
56}
57
58impl From<JsValueConst> for u8 {
59    fn from(value: JsValueConst) -> Self {
60        value as u8
61    }
62}
63
64/// Represents a JavaScript value that can be passed to a JS function.
65#[derive(Debug, PartialEq, Clone)]
66pub enum JsValue {
67    U32(u32),
68    I32(i32),
69    U64(u64),
70    I64(i64),
71    F64(f64),
72
73    True,
74    False,
75    Null,
76    Undefined,
77
78    Vec(Vec<u8>),       // type, length, sequence of bytes
79    String(String),     // type, length, sequence of chars
80    List(Vec<JsValue>), // type, length
81    Object(HashMap<String, JsValue>),
82
83    Json(JsJson),
84}
85
86impl JsValue {
87    pub fn str(value: impl Into<String>) -> JsValue {
88        JsValue::String(value.into())
89    }
90
91    pub fn from_block(block: MemoryBlock) -> Result<JsValue, std::string::String> {
92        let mut buffer = MemoryBlockRead::new(block);
93        decode_js_value_inner(&mut buffer)
94    }
95
96    pub fn bool(value: bool) -> JsValue {
97        if value {
98            JsValue::True
99        } else {
100            JsValue::False
101        }
102    }
103
104    pub fn string_option(value: Option<String>) -> JsValue {
105        match value {
106            Some(body) => JsValue::String(body),
107            None => JsValue::Null,
108        }
109    }
110
111    fn get_string_size(value: &str) -> u32 {
112        value.len() as u32
113    }
114
115    fn get_size(&self) -> u32 {
116        match self {
117            Self::U32(_) => PARAM_TYPE + 4,
118            Self::I32(_) => PARAM_TYPE + 4,
119            Self::U64(_) => PARAM_TYPE + 8,
120            Self::I64(_) => PARAM_TYPE + 8,
121            Self::F64(_) => PARAM_TYPE + 8,
122
123            Self::True => PARAM_TYPE,
124            Self::False => PARAM_TYPE,
125            Self::Null => PARAM_TYPE,
126            Self::Undefined => PARAM_TYPE,
127
128            Self::Vec(value) => PARAM_TYPE + VEC_SIZE + value.len() as u32,
129            Self::String(value) => PARAM_TYPE + STRING_SIZE + JsValue::get_string_size(value),
130            Self::List(items) => {
131                let mut sum = PARAM_TYPE + LIST_COUNT;
132
133                for param in items {
134                    sum += param.get_size();
135                }
136
137                sum
138            }
139            Self::Object(map) => {
140                let mut sum = PARAM_TYPE + OBJECT_COUNT;
141
142                for (key, value) in map {
143                    sum += STRING_SIZE + Self::get_string_size(key);
144                    sum += value.get_size();
145                }
146
147                sum
148            }
149            Self::Json(json) => PARAM_TYPE + json.get_size(),
150        }
151    }
152
153    fn write_to(&self, buff: &mut MemoryBlockWrite) {
154        match self {
155            Self::U32(value) => {
156                buff.write_u8(JsValueConst::U32);
157                buff.write_u32(*value);
158            }
159            Self::I32(value) => {
160                buff.write_u8(JsValueConst::I32);
161                buff.write_i32(*value);
162            }
163            Self::U64(value) => {
164                buff.write_u8(JsValueConst::U64);
165                buff.write_u64(*value);
166            }
167            Self::I64(value) => {
168                buff.write_u8(JsValueConst::I64);
169                buff.write_i64(*value);
170            }
171            Self::F64(value)=> {
172                buff.write_u8(JsValueConst::F64);
173                buff.write_f64(*value);
174            }
175
176            Self::True => {
177                buff.write_u8(JsValueConst::True);
178            }
179            Self::False => {
180                buff.write_u8(JsValueConst::False);
181            }
182            Self::Null => {
183                buff.write_u8(JsValueConst::Null);
184            }
185            Self::Undefined => {
186                buff.write_u8(JsValueConst::Undefined);
187            }
188
189            Self::Vec(inner_buff) => {
190                buff.write_u8(JsValueConst::Vec);
191                let data = inner_buff.as_slice();
192                buff.write_u32(data.len() as u32);
193                buff.write(inner_buff.as_slice());
194            }
195            Self::String(value) => {
196                buff.write_u8(JsValueConst::String);
197                write_string_to(value.as_str(), buff);
198            }
199            Self::List(list) => {
200                buff.write_u8(JsValueConst::List);
201                buff.write_u32(list.len() as u32);
202
203                for param in list {
204                    param.write_to(buff);
205                }
206            }
207            Self::Object(map) => {
208                buff.write_u8(JsValueConst::Object);
209                buff.write_u16(map.len() as u16);
210
211                for (key, value) in map {
212                    write_string_to(key.as_str(), buff);
213                    value.write_to(buff);
214                }
215            }
216            Self::Json(json) => {
217                buff.write_u8(JsValueConst::Json);
218                json.write_to(buff);
219            }
220        }
221    }
222
223    pub fn to_snapshot(&self) -> MemoryBlock {
224        let buff_size = self.get_size();
225        let block = MemoryBlock::new(buff_size);
226
227        let mut buff = MemoryBlockWrite::new(block);
228        self.write_to(&mut buff);
229        buff.get_block()
230    }
231
232    pub fn typename(&self) -> &'static str {
233        match self {
234            Self::U32(_) => "u32",
235            Self::I32(_) => "i32",
236            Self::U64(_) => "u64",
237            Self::I64(_) => "i64",
238            Self::F64(_) => "f64",
239            Self::True => "true",
240            Self::False => "false",
241            Self::Null => "null",
242            Self::Undefined => "undefined",
243            Self::Vec(_) => "vec",
244            Self::String(_) => "string",
245            Self::List(_) => "list",
246            Self::Object(_) => "object",
247            Self::Json(_) => "json",
248        }
249    }
250
251    pub fn convert<T, F: FnOnce(JsValueListDecoder) -> Result<T, String>>(
252        self,
253        convert: F,
254    ) -> Result<T, String> {
255        match self {
256            JsValue::List(list) => {
257                let decoder = JsValueListDecoder::new(list);
258                convert(decoder)
259            }
260            _ => Err(String::from("convert => ParamItem::Vec expected")),
261        }
262    }
263}
264
265impl Default for JsValue {
266    fn default() -> Self {
267        JsValue::List(Vec::new())
268    }
269}
270
271fn write_string_to(value: &str, buff: &mut MemoryBlockWrite) {
272    let data = value.as_bytes();
273    buff.write_u32(data.len() as u32);
274    buff.write(data);
275}
276
277fn decode_js_value_inner(buffer: &mut MemoryBlockRead) -> Result<JsValue, String> {
278    let type_param = buffer.get_byte();
279
280    let Some(type_param) = JsValueConst::from_byte(type_param) else {
281        return Err(format!("JsValue: Unknown data type prefix {type_param}"));
282    };
283
284    let result = match type_param {
285        JsValueConst::U32 => {
286            let value = buffer.get_u32();
287            JsValue::U32(value)
288        }
289        JsValueConst::I32 => {
290            let value = buffer.get_i32();
291            JsValue::I32(value)
292        }
293        JsValueConst::U64 => {
294            let value = buffer.get_u64();
295            JsValue::U64(value)
296        }
297        JsValueConst::I64 => {
298            let value = buffer.get_i64();
299            JsValue::I64(value)
300        }
301        JsValueConst::F64 => {
302            let value = buffer.get_f64();
303            JsValue::F64(value)
304        }
305        JsValueConst::True => JsValue::True,
306        JsValueConst::False => JsValue::False,
307        JsValueConst::Null => JsValue::Null,
308        JsValueConst::Undefined => JsValue::Undefined,
309        JsValueConst::Vec => {
310            let len = buffer.get_u32();
311            let param = buffer.get_vec(len);
312            JsValue::Vec(param)
313        }
314        JsValueConst::String => {
315            let str_len = buffer.get_u32();
316            let param = buffer.get_string(str_len)?;
317            JsValue::String(param)
318        }
319        JsValueConst::List => {
320            let mut param_list = Vec::new();
321
322            let list_size = buffer.get_u32();
323
324            for _ in 0..list_size {
325                let param = decode_js_value_inner(buffer)?;
326                param_list.push(param);
327            }
328
329            JsValue::List(param_list)
330        }
331        JsValueConst::Object => {
332            let mut props = HashMap::new();
333            let object_size = buffer.get_u16();
334
335            for _ in 0..object_size {
336                let prop_name = decode_js_value_inner(buffer)?;
337                let JsValue::String(prop_name) = prop_name else {
338                    return Err("string expected".into());
339                };
340
341                let prop_value = decode_js_value_inner(buffer)?;
342
343                props.insert(prop_name, prop_value);
344            }
345
346            JsValue::Object(props)
347        }
348        JsValueConst::Json => {
349            let json = decode_js_json_inner(buffer)?;
350            JsValue::Json(json)
351        }
352    };
353
354    Ok(result)
355}
356
357macro_rules! impl_from {
358    ($from:ty, $to:ty, $js_type:ident) => {
359        impl From<$from> for JsValue {
360            fn from(value: $from) -> Self {
361                Self::$js_type(value as $to)
362            }
363        }
364    };
365}
366
367impl_from!(i8, i32, I32);
368impl_from!(i16, i32, I32);
369impl_from!(i32, i32, I32);
370impl_from!(i64, i64, I64);
371impl_from!(isize, i64, I64);
372
373impl_from!(u8, u32, U32);
374impl_from!(u16, u32, U32);
375impl_from!(u32, u32, U32);
376impl_from!(u64, u64, U64);
377impl_from!(usize, u64, U64);
378
379impl_from!(f64, f64, F64);
380
381impl From<&str> for JsValue {
382    fn from(value: &str) -> Self {
383        Self::str(value)
384    }
385}
386
387impl From<String> for JsValue {
388    fn from(value: String) -> Self {
389        Self::String(value)
390    }
391}
392
393impl From<bool> for JsValue {
394    fn from(value: bool) -> Self {
395        if value {
396            Self::True
397        } else {
398            Self::False
399        }
400    }
401}
402
403impl From<HashMap<String, JsValue>> for JsValue {
404    fn from(value: HashMap<String, JsValue>) -> Self {
405        Self::Object(value)
406    }
407}
408
409impl FromIterator<(String, JsValue)> for JsValue {
410    fn from_iter<T: IntoIterator<Item = (String, JsValue)>>(iter: T) -> Self {
411        let hash_map = HashMap::from_iter(iter);
412        JsValue::Object(hash_map)
413    }
414}
415
416impl From<Vec<(String, JsValue)>> for JsValue {
417    fn from(value: Vec<(String, JsValue)>) -> Self {
418        JsValue::from_iter(value)
419    }
420}
421
422impl<'a> FromIterator<(&'a str, JsValue)> for JsValue {
423    fn from_iter<T: IntoIterator<Item = (&'a str, JsValue)>>(iter: T) -> Self {
424        let iter = iter.into_iter()
425            .map(|(k, v)| (k.to_string(), v));
426        let hash_map = HashMap::from_iter(iter);
427        JsValue::Object(hash_map)
428    }
429}
430
431impl From<Vec<(&str, JsValue)>> for JsValue {
432    fn from(value: Vec<(&str, JsValue)>) -> Self {
433        JsValue::from_iter(value)
434    }
435}
436