1use std::iter::Peekable;
2
3use pest::iterators::Pair;
4use pest::Parser;
5
6use crate::error::RenderError;
7use crate::grammar::{HandlebarsParser, Rule};
8use crate::RenderErrorReason;
9
10#[derive(PartialEq, Eq, Clone, Debug)]
11pub enum PathSeg {
12 Named(String),
13 Ruled(Rule),
14}
15
16#[derive(PartialEq, Eq, Clone, Debug)]
21pub enum Path {
22 Relative((Vec<PathSeg>, String)),
23 Local((usize, String, String)),
24}
25
26impl Path {
27 pub(crate) fn new(raw: &str, segs: Vec<PathSeg>) -> Path {
28 if let Some((level, name)) = get_local_path_and_level(&segs) {
29 Path::Local((level, name, raw.to_owned()))
30 } else {
31 Path::Relative((segs, raw.to_owned()))
32 }
33 }
34
35 pub fn parse(raw: &str) -> Result<Path, RenderError> {
36 HandlebarsParser::parse(Rule::path, raw)
37 .map(|p| {
38 let parsed = p.flatten();
39 let segs = parse_json_path_from_iter(&mut parsed.peekable(), raw.len());
40 Ok(Path::new(raw, segs))
41 })
42 .map_err(|_| RenderErrorReason::InvalidJsonPath(raw.to_owned()))?
43 }
44
45 pub(crate) fn raw(&self) -> &str {
46 match self {
47 Path::Relative((_, ref raw)) => raw,
48 Path::Local((_, _, ref raw)) => raw,
49 }
50 }
51
52 pub(crate) fn current() -> Path {
53 Path::Relative((Vec::with_capacity(0), "".to_owned()))
54 }
55
56 pub(crate) fn with_named_paths(name_segs: &[&str]) -> Path {
58 let segs = name_segs
59 .iter()
60 .map(|n| PathSeg::Named((*n).to_string()))
61 .collect();
62 Path::Relative((segs, name_segs.join("/")))
63 }
64
65 pub(crate) fn segs(&self) -> Option<&[PathSeg]> {
67 match self {
68 Path::Relative((segs, _)) => Some(segs),
69 _ => None,
70 }
71 }
72}
73
74fn get_local_path_and_level(paths: &[PathSeg]) -> Option<(usize, String)> {
75 paths.first().and_then(|seg| {
76 if seg == &PathSeg::Ruled(Rule::path_local) {
77 let mut level = 0;
78 while paths.get(level + 1)? == &PathSeg::Ruled(Rule::path_up) {
79 level += 1;
80 }
81 if let Some(PathSeg::Named(name)) = paths.get(level + 1) {
82 Some((level, name.clone()))
83 } else {
84 None
85 }
86 } else {
87 None
88 }
89 })
90}
91
92pub(crate) fn parse_json_path_from_iter<'a, I>(it: &mut Peekable<I>, limit: usize) -> Vec<PathSeg>
93where
94 I: Iterator<Item = Pair<'a, Rule>>,
95{
96 let mut path_stack = Vec::with_capacity(5);
97 while let Some(n) = it.peek() {
98 let span = n.as_span();
99 if span.end() > limit {
100 break;
101 }
102
103 match n.as_rule() {
104 Rule::path_root => {
105 path_stack.push(PathSeg::Ruled(Rule::path_root));
106 }
107 Rule::path_local => {
108 path_stack.push(PathSeg::Ruled(Rule::path_local));
109 }
110 Rule::path_up => {
111 path_stack.push(PathSeg::Ruled(Rule::path_up));
112 }
113 Rule::path_id | Rule::path_raw_id => {
114 let name = n.as_str();
115 if name != "this" {
116 path_stack.push(PathSeg::Named(name.to_string()));
117 }
118 }
119 _ => {}
120 }
121
122 it.next();
123 }
124
125 path_stack
126}
127
128pub(crate) fn merge_json_path(path_stack: &mut Vec<String>, relative_path: &[PathSeg]) {
129 for seg in relative_path {
130 match seg {
131 PathSeg::Named(ref s) => {
132 path_stack.push(s.to_owned());
133 }
134 PathSeg::Ruled(Rule::path_root) => {}
135 PathSeg::Ruled(Rule::path_up) => {}
136 _ => {}
137 }
138 }
139}