1use crate::error::FieldError;
5use crate::node::Node;
6use crate::value::Value;
7use std::any::Any;
8use std::collections::HashMap;
9use std::sync::Arc;
10
11#[derive(Clone, Debug, PartialEq)]
13pub enum PathPart {
14 Key(Arc<str>),
16 Index(usize),
17}
18
19impl PathPart {
20 pub fn as_key(&self) -> String {
21 match self {
22 PathPart::Key(k) => k.to_string(),
23 PathPart::Index(i) => i.to_string(),
24 }
25 }
26
27 fn write_to(&self, out: &mut String) {
28 match self {
29 PathPart::Key(k) => out.push_str(k),
30 PathPart::Index(i) => out.push_str(&i.to_string()),
31 }
32 }
33}
34
35#[derive(Default)]
38pub struct Context {
39 pub custom: HashMap<String, Box<dyn Any + Send + Sync>>,
43 pub refs: HashMap<String, Arc<Node>>,
45 pub err: Vec<FieldError>,
47 pub(crate) terse: bool,
50}
51
52impl Context {
53 pub fn new() -> Context {
54 Context::default()
55 }
56
57 pub fn get<T: 'static>(&self, key: &str) -> Option<&T> {
59 self.custom.get(key).and_then(|b| b.downcast_ref::<T>())
60 }
61
62 pub fn set<T: Any + Send + Sync>(&mut self, key: &str, value: T) {
64 self.custom.insert(key.to_string(), Box::new(value));
65 }
66}
67
68pub struct State<'a> {
70 pub path_arr: &'a [PathPart],
73 pub key: &'a str,
75 pub value: &'a Value,
77 pub node: &'a Node,
79 pub parent_is_array: bool,
81 pub is_match: bool,
83 pub ctx: &'a mut Context,
84 pub absent: bool,
86 pub check_name: &'a str,
88 pub(crate) defs: &'a HashMap<String, Arc<Node>>,
92}
93
94impl<'a> State<'a> {
95 pub fn path_str(&self) -> String {
97 join_path(self.path_arr)
98 }
99
100 pub fn path_keys(&self) -> Vec<String> {
102 self.path_arr.iter().map(|p| p.as_key()).collect()
103 }
104}
105
106pub fn join_path(path: &[PathPart]) -> String {
108 let mut out = String::new();
109 for p in path {
110 if matches!(p, PathPart::Key(k) if k.is_empty()) {
111 continue;
112 }
113 if !out.is_empty() {
114 out.push('.');
115 }
116 p.write_to(&mut out);
117 }
118 out
119}
120
121#[derive(Clone, Debug)]
123pub enum UpdateErr {
124 Text(String),
126 Field(Box<FieldError>),
128 Fields(Vec<FieldError>),
130}
131
132#[derive(Default)]
134pub struct Update {
135 pub done: bool,
137 pub why: Option<String>,
139 pub mark: i64,
141 pub err: Option<UpdateErr>,
142 pub val: Option<Value>,
144 pub node: Option<Arc<Node>>,
146 pub fatal: bool,
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn context_custom_is_typed() {
156 let mut c = Context::new();
157 c.set("n", 3u32);
158 assert_eq!(c.get::<u32>("n"), Some(&3));
159 assert_eq!(c.get::<String>("n"), None);
160 assert_eq!(c.get::<u32>("m"), None);
161 }
162
163 #[test]
164 fn paths_join_and_render() {
165 let k = |s: &str| PathPart::Key(s.into());
166 assert_eq!(
167 join_path(&[k("a"), k(""), k("b"), PathPart::Index(1)]),
168 "a.b.1"
169 );
170 assert_eq!(join_path(&[]), "");
171 assert_eq!(PathPart::Key("k".into()).as_key(), "k");
172 assert_eq!(PathPart::Index(2).as_key(), "2");
173 let mut ctx = Context::new();
174 let node = Node::default();
175 let path = [k("a")];
176 let defs = HashMap::new();
177 let s = State {
178 path_arr: &path,
179 key: "a",
180 value: &Value::Null,
181 node: &node,
182 parent_is_array: false,
183 is_match: false,
184 ctx: &mut ctx,
185 absent: false,
186 check_name: "",
187 defs: &defs,
188 };
189 assert_eq!(s.path_str(), "a");
190 assert_eq!(s.path_keys(), vec!["a".to_string()]);
191 let u = Update::default();
192 assert!(!u.done && u.val.is_none());
193 }
194}