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