zen_types/variable/
ref_deser.rs1use 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}