1use std::collections::{HashMap, VecDeque};
2
3use serde::Serialize;
4use serde_json::value::{to_value, Map, Value as Json};
5
6use crate::block::{BlockContext, BlockParamHolder};
7use crate::error::RenderError;
8use crate::grammar::Rule;
9use crate::json::path::*;
10use crate::json::value::ScopedJson;
11use crate::util::extend;
12
13pub type Object = HashMap<String, Json>;
14
15/// The context wrap data you render on your templates.
16///
17#[derive(Debug, Clone)]
18pub struct Context {
19 data: Json,
20}
21
22#[derive(Debug)]
23enum 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
35fn 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
117fn 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
127fn 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
141pub(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
154impl 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
223impl From<Json> for Context {
224 fn from(data: Json) -> Context {
225 Context { data }
226 }
227}
228
229#[cfg(test)]
230mod 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