1use crate::errors::*;
4use crate::names::Name;
5use crate::qualified_names::QualifiedName;
6use crate::strings::ToFeelString;
7use crate::value_null;
8use crate::values::Value;
9use dsntk_common::{DsntkError, Jsonify, Result};
10use std::collections::btree_map::Iter;
11use std::collections::BTreeMap;
12use std::fmt;
13use std::ops::Deref;
14
15type FeelContextEntries = BTreeMap<Name, Value>;
17
18#[derive(Debug, Clone, PartialEq, Default)]
20pub struct FeelContext(FeelContextEntries);
21
22impl Deref for FeelContext {
23 type Target = FeelContextEntries;
24 fn deref(&self) -> &Self::Target {
25 &self.0
26 }
27}
28
29impl TryFrom<Value> for FeelContext {
30 type Error = DsntkError;
31 fn try_from(value: Value) -> Result<Self, Self::Error> {
33 let Value::Context(ctx) = value else {
34 return Err(err_value_is_not_a_context(&value));
35 };
36 Ok(ctx)
37 }
38}
39
40impl From<FeelContext> for Value {
41 fn from(ctx: FeelContext) -> Self {
43 Value::Context(ctx)
44 }
45}
46
47impl fmt::Display for FeelContext {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 write!(
51 f,
52 "{{{}}}",
53 self
54 .0
55 .iter()
56 .map(|(name, value)| { format!("{}: {}", if name.is_empty() { r#""""#.to_string() } else { name.to_string() }, value) })
57 .collect::<Vec<String>>()
58 .join(", ")
59 )
60 }
61}
62
63impl ToFeelString for FeelContext {
64 fn to_feel_string(&self) -> String {
66 format!(
67 "{{{}}}",
68 self
69 .0
70 .iter()
71 .map(|(name, value)| {
72 let name_str = format!("{name}");
73 let padded_name_str = match name_str.as_str() {
74 "{" | "}" | ":" | "," => format!("\"{name_str}\""),
75 "\"" => "\"\\\"\"".to_string(),
76 _ => name_str,
77 };
78 format!(r#"{padded_name_str}: {value}"#)
79 })
80 .collect::<Vec<String>>()
81 .join(", ")
82 )
83 }
84}
85
86impl Jsonify for FeelContext {
87 fn jsonify(&self) -> String {
89 format!(
90 "{{{}}}",
91 self
92 .0
93 .iter()
94 .map(|(name, value)| format!(r#""{}": {}"#, name, value.jsonify()))
95 .collect::<Vec<String>>()
96 .join(", ")
97 )
98 }
99}
100
101impl FeelContext {
102 pub fn new() -> Self {
104 Self::default()
105 }
106
107 pub fn contains_entry(&self, name: &Name) -> bool {
109 self.0.contains_key(name)
110 }
111
112 pub fn contains_entries(&self, qname: &QualifiedName) -> bool {
114 self.contains_deep(qname.as_slice())
115 }
116
117 pub fn get_entry(&self, name: &Name) -> Option<&Value> {
119 self.0.get(name)
120 }
121
122 pub fn set_entry(&mut self, name: &Name, value: Value) {
124 self.0.insert(name.clone(), value);
125 }
126
127 pub fn remove_entry(&mut self, name: &Name) -> Option<Value> {
129 self.0.remove(name)
130 }
131
132 pub fn set_null(&mut self, name: Name) {
134 self.0.insert(name, value_null!());
135 }
136
137 pub fn get_entries(&self) -> Vec<(&Name, &Value)> {
139 self.0.iter().collect::<Vec<(&Name, &Value)>>()
140 }
141
142 pub fn iter(&self) -> Iter<Name, Value> {
144 self.0.iter()
145 }
146
147 pub fn get_first(&self) -> Option<&Value> {
149 self.0.values().next()
150 }
151
152 pub fn len(&self) -> usize {
154 self.0.len()
155 }
156
157 pub fn is_empty(&self) -> bool {
159 self.0.is_empty()
160 }
161
162 pub fn is_context(&self, name: &Name) -> bool {
164 matches!(self.0.get(name), Some(Value::Context(_)))
165 }
166
167 pub fn zip(&mut self, other: &FeelContext) {
168 for (name, value) in &other.0 {
169 self.0.insert(name.clone(), value.clone());
170 }
171 }
172
173 pub fn overwrite(&mut self, other: &FeelContext) {
174 for (name, value) in &other.0 {
175 if self.0.contains_key(name) {
176 self.0.insert(name.clone(), value.clone());
177 }
178 }
179 }
180
181 pub fn move_entry(&mut self, name: Name, parent: Name) {
183 if let Some(value) = self.0.remove(&name) {
184 self.create_entries(&[parent, name], value);
185 }
186 }
187
188 pub fn create_entry(&mut self, qname: &QualifiedName, value: Value) {
191 self.create_entries(qname.as_slice(), value);
192 }
193
194 pub fn search_entry<'a>(&'a self, qname: &'a QualifiedName) -> Option<&'a Value> {
196 self.search_deep(qname.as_slice())
197 }
198
199 pub fn contains_deep(&self, names: &[Name]) -> bool {
201 if names.is_empty() {
202 return false;
203 }
204 let tail = &names[1..];
205 if let Some(value) = self.0.get(&names[0]) {
206 if tail.is_empty() {
207 return true;
208 }
209 if let Value::Context(context) = value {
210 return context.contains_deep(tail);
211 }
212 }
213 false
214 }
215
216 pub fn create_entries(&mut self, names: &[Name], value: Value) {
218 if names.is_empty() {
220 return;
221 }
222 let key = names[0].clone();
223 let tail = &names[1..];
224 if tail.is_empty() {
226 self.0.insert(key, value);
227 return;
228 }
229 if let Some(Value::Context(ctx)) = self.0.get_mut(&key) {
231 ctx.create_entries(tail, value);
232 return;
233 }
234 let mut ctx = FeelContext::default();
236 ctx.create_entries(tail, value);
237 self.0.insert(key, ctx.into());
238 }
239
240 pub fn apply_entries(&mut self, names: &[Name], value: Value) -> Result<()> {
242 if names.is_empty() {
244 return Ok(());
245 }
246 let key = names[0].clone();
247 let tail = &names[1..];
248 if tail.is_empty() {
250 self.0.insert(key, value);
251 return Ok(());
252 }
253 match self.0.get_mut(&key) {
255 Some(Value::Context(ctx)) => {
256 ctx.apply_entries(tail, value)?;
257 return Ok(());
258 }
259 Some(other) => {
260 return Err(err_value_is_not_a_context(other));
261 }
262 _ => {}
263 }
264 let mut ctx = FeelContext::default();
266 ctx.apply_entries(tail, value)?;
267 self.0.insert(key, ctx.into());
268 Ok(())
269 }
270
271 pub fn search_deep(&self, names: &[Name]) -> Option<&Value> {
273 if !names.is_empty() {
274 let tail = &names[1..];
275 if let Some(value) = self.0.get(&names[0]) {
276 if let Value::Context(ctx) = value {
277 return if tail.is_empty() { Some(value) } else { ctx.search_deep(tail) };
278 } else if tail.is_empty() {
279 return Some(value);
280 }
281 }
282 }
283 None
284 }
285}