zen_types/variable/
ref_deser.rs

1use crate::rcvalue::RcValue;
2use crate::variable::Variable;
3use ahash::{HashMap, HashMapExt};
4use std::cell::RefCell;
5use std::rc::Rc;
6use thiserror::Error;
7
8pub struct RefDeserializer {
9    refs: Vec<Option<Variable>>,
10}
11
12impl RefDeserializer {
13    pub fn new() -> Self {
14        Self { refs: Vec::new() }
15    }
16
17    pub fn deserialize(&mut self, value: RcValue) -> Result<Variable, RefDeserializeError> {
18        let RcValue::Object(mut root_obj) = value else {
19            return Err(RefDeserializeError::InvalidFormat(
20                "Expected root object".into(),
21            ));
22        };
23
24        if let Some(RcValue::Array(refs_array)) = root_obj.remove(&Rc::from("$refs")) {
25            self.refs = vec![None; refs_array.len()];
26
27            for (i, _) in refs_array.iter().enumerate() {
28                match &refs_array[i] {
29                    RcValue::Array(_) => {
30                        self.refs[i] = Some(Variable::Array(Rc::new(RefCell::new(Vec::new()))));
31                    }
32                    RcValue::Object(_) => {
33                        self.refs[i] =
34                            Some(Variable::Object(Rc::new(RefCell::new(HashMap::default()))));
35                    }
36                    _ => {
37                        self.refs[i] = Some(self.deserialize_value(&refs_array[i])?);
38                    }
39                }
40            }
41
42            for (i, ref_value) in refs_array.iter().enumerate() {
43                match ref_value {
44                    RcValue::Array(arr) => {
45                        if let Some(Variable::Array(target)) = &self.refs[i] {
46                            let mut items = Vec::with_capacity(arr.len());
47                            for item in arr {
48                                items.push(self.deserialize_value(item)?);
49                            }
50                            *target.borrow_mut() = items;
51                        }
52                    }
53                    RcValue::Object(obj) => {
54                        if let Some(Variable::Object(target)) = &self.refs[i] {
55                            let mut map = HashMap::with_capacity(obj.len());
56                            for (key, value) in obj {
57                                let key_var = self.deserialize_key(key)?;
58                                let value_var = self.deserialize_value(value)?;
59                                map.insert(key_var, value_var);
60                            }
61                            *target.borrow_mut() = map;
62                        }
63                    }
64                    _ => {}
65                }
66            }
67        }
68
69        let root_value = root_obj
70            .remove(&Rc::from("$root"))
71            .ok_or_else(|| RefDeserializeError::InvalidFormat("Missing $root".into()))?;
72
73        self.deserialize_value(&root_value)
74    }
75
76    fn deserialize_key(&self, key: &Rc<str>) -> Result<Rc<str>, RefDeserializeError> {
77        if let Some(ref_id) = parse_ref_id(key) {
78            if ref_id >= self.refs.len() {
79                return Err(RefDeserializeError::InvalidReference(ref_id));
80            }
81
82            match &self.refs[ref_id] {
83                Some(Variable::String(s)) => Ok(s.clone()),
84                Some(_) => Err(RefDeserializeError::InvalidFormat(
85                    "Reference used as key must be a string".into(),
86                )),
87                None => Err(RefDeserializeError::UnresolvedReference(ref_id)),
88            }
89        } else {
90            Ok(unescape_at_string(key))
91        }
92    }
93
94    fn deserialize_value(&self, value: &RcValue) -> Result<Variable, RefDeserializeError> {
95        match value {
96            RcValue::Null => Ok(Variable::Null),
97            RcValue::Bool(b) => Ok(Variable::Bool(*b)),
98            RcValue::Number(n) => Ok(Variable::Number(*n)),
99            RcValue::String(s) => {
100                if let Some(ref_id) = parse_ref_id(s) {
101                    if ref_id >= self.refs.len() {
102                        return Err(RefDeserializeError::InvalidReference(ref_id));
103                    }
104
105                    self.refs[ref_id]
106                        .clone()
107                        .ok_or(RefDeserializeError::UnresolvedReference(ref_id))
108                } else {
109                    Ok(Variable::String(unescape_at_string(s)))
110                }
111            }
112            RcValue::Array(arr) => {
113                let mut items = Vec::with_capacity(arr.len());
114                for item in arr {
115                    items.push(self.deserialize_value(item)?);
116                }
117                Ok(Variable::Array(Rc::new(RefCell::new(items))))
118            }
119            RcValue::Object(obj) => {
120                let mut map = HashMap::with_capacity(obj.len());
121                for (key, value) in obj {
122                    let key_var = self.deserialize_key(key)?;
123                    let value_var = self.deserialize_value(value)?;
124                    map.insert(key_var, value_var);
125                }
126                Ok(Variable::Object(Rc::new(RefCell::new(map))))
127            }
128        }
129    }
130}
131
132#[derive(Debug, Error)]
133pub enum RefDeserializeError {
134    #[error("Invalid format: {0}")]
135    InvalidFormat(String),
136    #[error("Invalid reference: {0}")]
137    InvalidReference(usize),
138    #[error("UnresolvedReference: {0}")]
139    UnresolvedReference(usize),
140}
141
142fn unescape_at_string(s: &Rc<str>) -> Rc<str> {
143    if s.starts_with("@@") {
144        Rc::from(&s[1..])
145    } else {
146        s.clone()
147    }
148}
149
150fn parse_ref_id(s: &str) -> Option<usize> {
151    s.strip_prefix('@')?.parse().ok()
152}