summaryrefslogtreecommitdiffstats
path: root/vendor/handlebars/src/json/path.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/handlebars/src/json/path.rs')
-rw-r--r--vendor/handlebars/src/json/path.rs138
1 files changed, 138 insertions, 0 deletions
diff --git a/vendor/handlebars/src/json/path.rs b/vendor/handlebars/src/json/path.rs
new file mode 100644
index 000000000..8270371b2
--- /dev/null
+++ b/vendor/handlebars/src/json/path.rs
@@ -0,0 +1,138 @@
+use std::iter::Peekable;
+
+use pest::iterators::Pair;
+use pest::Parser;
+
+use crate::error::RenderError;
+use crate::grammar::{HandlebarsParser, Rule};
+
+#[derive(PartialEq, Clone, Debug)]
+pub enum PathSeg {
+ Named(String),
+ Ruled(Rule),
+}
+
+/// Represents the Json path in templates.
+///
+/// It can be either a local variable like `@first`, `../@index`,
+/// or a normal relative path like `a/b/c`.
+#[derive(PartialEq, Clone, Debug)]
+pub enum Path {
+ Relative((Vec<PathSeg>, String)),
+ Local((usize, String, String)),
+}
+
+impl Path {
+ pub(crate) fn new(raw: &str, segs: Vec<PathSeg>) -> Path {
+ if let Some((level, name)) = get_local_path_and_level(&segs) {
+ Path::Local((level, name, raw.to_owned()))
+ } else {
+ Path::Relative((segs, raw.to_owned()))
+ }
+ }
+
+ pub fn parse(raw: &str) -> Result<Path, RenderError> {
+ HandlebarsParser::parse(Rule::path, raw)
+ .map(|p| {
+ let parsed = p.flatten();
+ let segs = parse_json_path_from_iter(&mut parsed.peekable(), raw.len());
+ Ok(Path::new(raw, segs))
+ })
+ .map_err(|_| RenderError::new("Invalid JSON path"))?
+ }
+
+ pub(crate) fn raw(&self) -> &str {
+ match self {
+ Path::Relative((_, ref raw)) => raw,
+ Path::Local((_, _, ref raw)) => raw,
+ }
+ }
+
+ pub(crate) fn current() -> Path {
+ Path::Relative((Vec::with_capacity(0), "".to_owned()))
+ }
+
+ // for test only
+ pub(crate) fn with_named_paths(name_segs: &[&str]) -> Path {
+ let segs = name_segs
+ .iter()
+ .map(|n| PathSeg::Named((*n).to_string()))
+ .collect();
+ Path::Relative((segs, name_segs.join("/")))
+ }
+
+ // for test only
+ pub(crate) fn segs(&self) -> Option<&[PathSeg]> {
+ match self {
+ Path::Relative((segs, _)) => Some(segs),
+ _ => None,
+ }
+ }
+}
+
+fn get_local_path_and_level(paths: &[PathSeg]) -> Option<(usize, String)> {
+ paths.get(0).and_then(|seg| {
+ if seg == &PathSeg::Ruled(Rule::path_local) {
+ let mut level = 0;
+ while paths.get(level + 1)? == &PathSeg::Ruled(Rule::path_up) {
+ level += 1;
+ }
+ if let Some(PathSeg::Named(name)) = paths.get(level + 1) {
+ Some((level, name.clone()))
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ })
+}
+
+pub(crate) fn parse_json_path_from_iter<'a, I>(it: &mut Peekable<I>, limit: usize) -> Vec<PathSeg>
+where
+ I: Iterator<Item = Pair<'a, Rule>>,
+{
+ let mut path_stack = Vec::with_capacity(5);
+ while let Some(n) = it.peek() {
+ let span = n.as_span();
+ if span.end() > limit {
+ break;
+ }
+
+ match n.as_rule() {
+ Rule::path_root => {
+ path_stack.push(PathSeg::Ruled(Rule::path_root));
+ }
+ Rule::path_local => {
+ path_stack.push(PathSeg::Ruled(Rule::path_local));
+ }
+ Rule::path_up => {
+ path_stack.push(PathSeg::Ruled(Rule::path_up));
+ }
+ Rule::path_id | Rule::path_raw_id => {
+ let name = n.as_str();
+ if name != "this" {
+ path_stack.push(PathSeg::Named(name.to_string()));
+ }
+ }
+ _ => {}
+ }
+
+ it.next();
+ }
+
+ path_stack
+}
+
+pub(crate) fn merge_json_path(path_stack: &mut Vec<String>, relative_path: &[PathSeg]) {
+ for seg in relative_path {
+ match seg {
+ PathSeg::Named(ref s) => {
+ path_stack.push(s.to_owned());
+ }
+ PathSeg::Ruled(Rule::path_root) => {}
+ PathSeg::Ruled(Rule::path_up) => {}
+ _ => {}
+ }
+ }
+}