1 | use std::collections::{HashMap, VecDeque}; |
2 | |
3 | use serde::Serialize; |
4 | use serde_json::value::{to_value, Map, Value as Json}; |
5 | |
6 | use crate::block::{BlockContext, BlockParamHolder}; |
7 | use crate::error::RenderError; |
8 | use crate::grammar::Rule; |
9 | use crate::json::path::*; |
10 | use crate::json::value::ScopedJson; |
11 | use crate::util::extend; |
12 | |
13 | pub type Object = HashMap<String, Json>; |
14 | |
15 | /// The context wrap data you render on your templates. |
16 | /// |
17 | #[derive (Debug, Clone)] |
18 | pub struct Context { |
19 | data: Json, |
20 | } |
21 | |
22 | #[derive (Debug)] |
23 | enum ResolvedPath<'a> { |
24 | // FIXME: change to borrowed when possible |
25 | // full path |
26 | AbsolutePath(Vec<String>), |
27 | // relative path and path root |
28 | RelativePath(Vec<String>), |
29 | // relative path against block param value |
30 | BlockParamValue(Vec<String>, &'a Json), |
31 | // relative path against derived value, |
32 | LocalValue(Vec<String>, &'a Json), |
33 | } |
34 | |
35 | fn parse_json_visitor<'a, 'reg>( |
36 | relative_path: &[PathSeg], |
37 | block_contexts: &'a VecDeque<BlockContext<'reg>>, |
38 | always_for_absolute_path: bool, |
39 | ) -> ResolvedPath<'a> { |
40 | let mut path_context_depth: i64 = 0; |
41 | let mut with_block_param = None; |
42 | let mut from_root = false; |
43 | |
44 | // peek relative_path for block param, @root and "../../" |
45 | for path_seg in relative_path { |
46 | match path_seg { |
47 | PathSeg::Named(the_path) => { |
48 | if let Some((holder, base_path)) = get_in_block_params(block_contexts, the_path) { |
49 | with_block_param = Some((holder, base_path)); |
50 | } |
51 | break; |
52 | } |
53 | PathSeg::Ruled(the_rule) => match the_rule { |
54 | Rule::path_root => { |
55 | from_root = true; |
56 | break; |
57 | } |
58 | Rule::path_up => path_context_depth += 1, |
59 | _ => break, |
60 | }, |
61 | } |
62 | } |
63 | |
64 | let mut path_stack = Vec::with_capacity(relative_path.len() + 5); |
65 | match with_block_param { |
66 | Some((BlockParamHolder::Value(ref value), _)) => { |
67 | merge_json_path(&mut path_stack, &relative_path[1..]); |
68 | ResolvedPath::BlockParamValue(path_stack, value) |
69 | } |
70 | Some((BlockParamHolder::Path(ref paths), base_path)) => { |
71 | extend(&mut path_stack, base_path); |
72 | if !paths.is_empty() { |
73 | extend(&mut path_stack, paths); |
74 | } |
75 | merge_json_path(&mut path_stack, &relative_path[1..]); |
76 | |
77 | ResolvedPath::AbsolutePath(path_stack) |
78 | } |
79 | None => { |
80 | if path_context_depth > 0 { |
81 | let blk = block_contexts |
82 | .get(path_context_depth as usize) |
83 | .or_else(|| block_contexts.front()); |
84 | |
85 | if let Some(base_value) = blk.and_then(|blk| blk.base_value()) { |
86 | merge_json_path(&mut path_stack, relative_path); |
87 | ResolvedPath::LocalValue(path_stack, base_value) |
88 | } else { |
89 | if let Some(base_path) = blk.map(|blk| blk.base_path()) { |
90 | extend(&mut path_stack, base_path); |
91 | } |
92 | merge_json_path(&mut path_stack, relative_path); |
93 | ResolvedPath::AbsolutePath(path_stack) |
94 | } |
95 | } else if from_root { |
96 | merge_json_path(&mut path_stack, relative_path); |
97 | ResolvedPath::AbsolutePath(path_stack) |
98 | } else if always_for_absolute_path { |
99 | if let Some(base_value) = block_contexts.front().and_then(|blk| blk.base_value()) { |
100 | merge_json_path(&mut path_stack, relative_path); |
101 | ResolvedPath::LocalValue(path_stack, base_value) |
102 | } else { |
103 | if let Some(base_path) = block_contexts.front().map(|blk| blk.base_path()) { |
104 | extend(&mut path_stack, base_path); |
105 | } |
106 | merge_json_path(&mut path_stack, relative_path); |
107 | ResolvedPath::AbsolutePath(path_stack) |
108 | } |
109 | } else { |
110 | merge_json_path(&mut path_stack, relative_path); |
111 | ResolvedPath::RelativePath(path_stack) |
112 | } |
113 | } |
114 | } |
115 | } |
116 | |
117 | fn get_data<'a>(d: Option<&'a Json>, p: &str) -> Result<Option<&'a Json>, RenderError> { |
118 | let result: Option<&Value> = match d { |
119 | Some(&Json::Array(ref l: &Vec)) => p.parse::<usize>().map(|idx_u: usize| l.get(index:idx_u))?, |
120 | Some(&Json::Object(ref m: &Map)) => m.get(key:p), |
121 | Some(_) => None, |
122 | None => None, |
123 | }; |
124 | Ok(result) |
125 | } |
126 | |
127 | fn get_in_block_params<'a, 'reg>( |
128 | block_contexts: &'a VecDeque<BlockContext<'reg>>, |
129 | p: &str, |
130 | ) -> Option<(&'a BlockParamHolder, &'a Vec<String>)> { |
131 | for bc: &BlockContext<'_> in block_contexts { |
132 | let v: Option<&BlockParamHolder> = bc.get_block_param(block_param_name:p); |
133 | if v.is_some() { |
134 | return v.map(|v: &BlockParamHolder| (v, bc.base_path())); |
135 | } |
136 | } |
137 | |
138 | None |
139 | } |
140 | |
141 | pub(crate) fn merge_json(base: &Json, addition: &HashMap<&str, &Json>) -> Json { |
142 | let mut base_map: Map = match base { |
143 | Json::Object(ref m: &Map) => m.clone(), |
144 | _ => Map::new(), |
145 | }; |
146 | |
147 | for (k: &&str, v: &&Value) in addition.iter() { |
148 | base_map.insert(k:k.to_string(), (*v).clone()); |
149 | } |
150 | |
151 | Json::Object(base_map) |
152 | } |
153 | |
154 | impl Context { |
155 | /// Create a context with null data |
156 | pub fn null() -> Context { |
157 | Context { data: Json::Null } |
158 | } |
159 | |
160 | /// Create a context with given data |
161 | pub fn wraps<T: Serialize>(e: T) -> Result<Context, RenderError> { |
162 | to_value(e) |
163 | .map_err(RenderError::from) |
164 | .map(|d| Context { data: d }) |
165 | } |
166 | |
167 | /// Navigate the context with relative path and block scopes |
168 | pub(crate) fn navigate<'reg, 'rc>( |
169 | &'rc self, |
170 | relative_path: &[PathSeg], |
171 | block_contexts: &VecDeque<BlockContext<'reg>>, |
172 | ) -> Result<ScopedJson<'reg, 'rc>, RenderError> { |
173 | // always use absolute at the moment until we get base_value lifetime issue fixed |
174 | let resolved_visitor = parse_json_visitor(relative_path, block_contexts, true); |
175 | |
176 | match resolved_visitor { |
177 | ResolvedPath::AbsolutePath(paths) => { |
178 | let mut ptr = Some(self.data()); |
179 | for p in paths.iter() { |
180 | ptr = get_data(ptr, p)?; |
181 | } |
182 | |
183 | Ok(ptr |
184 | .map(|v| ScopedJson::Context(v, paths)) |
185 | .unwrap_or_else(|| ScopedJson::Missing)) |
186 | } |
187 | ResolvedPath::RelativePath(_paths) => { |
188 | // relative path is disabled for now |
189 | unreachable!() |
190 | // let mut ptr = block_contexts.front().and_then(|blk| blk.base_value()); |
191 | // for p in paths.iter() { |
192 | // ptr = get_data(ptr, p)?; |
193 | // } |
194 | |
195 | // Ok(ptr |
196 | // .map(|v| ScopedJson::Context(v, paths)) |
197 | // .unwrap_or_else(|| ScopedJson::Missing)) |
198 | } |
199 | ResolvedPath::BlockParamValue(paths, value) |
200 | | ResolvedPath::LocalValue(paths, value) => { |
201 | let mut ptr = Some(value); |
202 | for p in paths.iter() { |
203 | ptr = get_data(ptr, p)?; |
204 | } |
205 | Ok(ptr |
206 | .map(|v| ScopedJson::Derived(v.clone())) |
207 | .unwrap_or_else(|| ScopedJson::Missing)) |
208 | } |
209 | } |
210 | } |
211 | |
212 | /// Return the Json data wrapped in context |
213 | pub fn data(&self) -> &Json { |
214 | &self.data |
215 | } |
216 | |
217 | /// Return the mutable reference to Json data wrapped in context |
218 | pub fn data_mut(&mut self) -> &mut Json { |
219 | &mut self.data |
220 | } |
221 | } |
222 | |
223 | impl From<Json> for Context { |
224 | fn from(data: Json) -> Context { |
225 | Context { data } |
226 | } |
227 | } |
228 | |
229 | #[cfg (test)] |
230 | mod test { |
231 | use crate::block::{BlockContext, BlockParams}; |
232 | use crate::context::{self, Context}; |
233 | use crate::error::RenderError; |
234 | use crate::json::path::Path; |
235 | use crate::json::value::{self, ScopedJson}; |
236 | use serde_json::value::Map; |
237 | use std::collections::{HashMap, VecDeque}; |
238 | |
239 | fn navigate_from_root<'reg, 'rc>( |
240 | ctx: &'rc Context, |
241 | path: &str, |
242 | ) -> Result<ScopedJson<'reg, 'rc>, RenderError> { |
243 | let relative_path = Path::parse(path).unwrap(); |
244 | ctx.navigate(relative_path.segs().unwrap(), &VecDeque::new()) |
245 | } |
246 | |
247 | #[derive (Serialize)] |
248 | struct Address { |
249 | city: String, |
250 | country: String, |
251 | } |
252 | |
253 | #[derive (Serialize)] |
254 | struct Person { |
255 | name: String, |
256 | age: i16, |
257 | addr: Address, |
258 | titles: Vec<String>, |
259 | } |
260 | |
261 | #[test ] |
262 | fn test_render() { |
263 | let v = "hello" ; |
264 | let ctx = Context::wraps(&v.to_string()).unwrap(); |
265 | assert_eq!( |
266 | navigate_from_root(&ctx, "this" ).unwrap().render(), |
267 | v.to_string() |
268 | ); |
269 | } |
270 | |
271 | #[test ] |
272 | fn test_navigation() { |
273 | let addr = Address { |
274 | city: "Beijing" .to_string(), |
275 | country: "China" .to_string(), |
276 | }; |
277 | |
278 | let person = Person { |
279 | name: "Ning Sun" .to_string(), |
280 | age: 27, |
281 | addr, |
282 | titles: vec!["programmer" .to_string(), "cartographer" .to_string()], |
283 | }; |
284 | |
285 | let ctx = Context::wraps(&person).unwrap(); |
286 | assert_eq!( |
287 | navigate_from_root(&ctx, "./addr/country" ).unwrap().render(), |
288 | "China" .to_string() |
289 | ); |
290 | assert_eq!( |
291 | navigate_from_root(&ctx, "addr.[country]" ).unwrap().render(), |
292 | "China" .to_string() |
293 | ); |
294 | |
295 | let v = true; |
296 | let ctx2 = Context::wraps(&v).unwrap(); |
297 | assert_eq!( |
298 | navigate_from_root(&ctx2, "this" ).unwrap().render(), |
299 | "true" .to_string() |
300 | ); |
301 | |
302 | assert_eq!( |
303 | navigate_from_root(&ctx, "titles.[0]" ).unwrap().render(), |
304 | "programmer" .to_string() |
305 | ); |
306 | |
307 | assert_eq!( |
308 | navigate_from_root(&ctx, "age" ).unwrap().render(), |
309 | "27" .to_string() |
310 | ); |
311 | } |
312 | |
313 | #[test ] |
314 | fn test_this() { |
315 | let mut map_with_this = Map::new(); |
316 | map_with_this.insert("this" .to_string(), value::to_json("hello" )); |
317 | map_with_this.insert("age" .to_string(), value::to_json(5usize)); |
318 | let ctx1 = Context::wraps(&map_with_this).unwrap(); |
319 | |
320 | let mut map_without_this = Map::new(); |
321 | map_without_this.insert("age" .to_string(), value::to_json(4usize)); |
322 | let ctx2 = Context::wraps(&map_without_this).unwrap(); |
323 | |
324 | assert_eq!( |
325 | navigate_from_root(&ctx1, "this" ).unwrap().render(), |
326 | "[object]" .to_owned() |
327 | ); |
328 | assert_eq!( |
329 | navigate_from_root(&ctx2, "age" ).unwrap().render(), |
330 | "4" .to_owned() |
331 | ); |
332 | } |
333 | |
334 | #[test ] |
335 | fn test_merge_json() { |
336 | let map = json!({ "age" : 4 }); |
337 | let s = "hello" .to_owned(); |
338 | let mut hash = HashMap::new(); |
339 | let v = value::to_json("h1" ); |
340 | hash.insert("tag" , &v); |
341 | |
342 | let ctx_a1 = Context::wraps(&context::merge_json(&map, &hash)).unwrap(); |
343 | assert_eq!( |
344 | navigate_from_root(&ctx_a1, "age" ).unwrap().render(), |
345 | "4" .to_owned() |
346 | ); |
347 | assert_eq!( |
348 | navigate_from_root(&ctx_a1, "tag" ).unwrap().render(), |
349 | "h1" .to_owned() |
350 | ); |
351 | |
352 | let ctx_a2 = Context::wraps(&context::merge_json(&value::to_json(s), &hash)).unwrap(); |
353 | assert_eq!( |
354 | navigate_from_root(&ctx_a2, "this" ).unwrap().render(), |
355 | "[object]" .to_owned() |
356 | ); |
357 | assert_eq!( |
358 | navigate_from_root(&ctx_a2, "tag" ).unwrap().render(), |
359 | "h1" .to_owned() |
360 | ); |
361 | } |
362 | |
363 | #[test ] |
364 | fn test_key_name_with_this() { |
365 | let m = json!({ |
366 | "this_name" : "the_value" |
367 | }); |
368 | let ctx = Context::wraps(&m).unwrap(); |
369 | assert_eq!( |
370 | navigate_from_root(&ctx, "this_name" ).unwrap().render(), |
371 | "the_value" .to_string() |
372 | ); |
373 | } |
374 | |
375 | use serde::ser::Error as SerdeError; |
376 | use serde::{Serialize, Serializer}; |
377 | |
378 | struct UnserializableType {} |
379 | |
380 | impl Serialize for UnserializableType { |
381 | fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error> |
382 | where |
383 | S: Serializer, |
384 | { |
385 | Err(SerdeError::custom("test" )) |
386 | } |
387 | } |
388 | |
389 | #[test ] |
390 | fn test_serialize_error() { |
391 | let d = UnserializableType {}; |
392 | assert!(Context::wraps(&d).is_err()); |
393 | } |
394 | |
395 | #[test ] |
396 | fn test_root() { |
397 | let m = json!({ |
398 | "a" : { |
399 | "b" : { |
400 | "c" : { |
401 | "d" : 1 |
402 | } |
403 | } |
404 | }, |
405 | "b" : 2 |
406 | }); |
407 | let ctx = Context::wraps(&m).unwrap(); |
408 | let mut block = BlockContext::new(); |
409 | *block.base_path_mut() = ["a" .to_owned(), "b" .to_owned()].to_vec(); |
410 | |
411 | let mut blocks = VecDeque::new(); |
412 | blocks.push_front(block); |
413 | |
414 | assert_eq!( |
415 | ctx.navigate(&Path::parse("@root/b" ).unwrap().segs().unwrap(), &blocks) |
416 | .unwrap() |
417 | .render(), |
418 | "2" .to_string() |
419 | ); |
420 | } |
421 | |
422 | #[test ] |
423 | fn test_block_params() { |
424 | let m = json!([{ |
425 | "a" : [1, 2] |
426 | }, { |
427 | "b" : [2, 3] |
428 | }]); |
429 | |
430 | let ctx = Context::wraps(&m).unwrap(); |
431 | let mut block_params = BlockParams::new(); |
432 | block_params |
433 | .add_path("z" , ["0" .to_owned(), "a" .to_owned()].to_vec()) |
434 | .unwrap(); |
435 | block_params.add_value("t" , json!("good" )).unwrap(); |
436 | |
437 | let mut block = BlockContext::new(); |
438 | block.set_block_params(block_params); |
439 | |
440 | let mut blocks = VecDeque::new(); |
441 | blocks.push_front(block); |
442 | |
443 | assert_eq!( |
444 | ctx.navigate(&Path::parse("z.[1]" ).unwrap().segs().unwrap(), &blocks) |
445 | .unwrap() |
446 | .render(), |
447 | "2" .to_string() |
448 | ); |
449 | assert_eq!( |
450 | ctx.navigate(&Path::parse("t" ).unwrap().segs().unwrap(), &blocks) |
451 | .unwrap() |
452 | .render(), |
453 | "good" .to_string() |
454 | ); |
455 | } |
456 | } |
457 | |