• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sunng87 / handlebars-rust / 13223230778

09 Feb 2025 06:42AM UTC coverage: 81.871% (-0.2%) from 82.062%
13223230778

push

github

web-flow
fix: reimplement partial context as block params (#694)

* test: correct test case for 495 regression

* fix: reimplement partial context as block params

* refactor: remove unused merge_json

2 of 2 new or added lines in 1 file covered. (100.0%)

4 existing lines in 2 files now uncovered.

1549 of 1892 relevant lines covered (81.87%)

7.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.67
/src/json/path.rs
1
use std::iter::Peekable;
2

3
use pest::iterators::Pair;
4
use pest::Parser;
5

6
use crate::error::RenderError;
7
use crate::grammar::{HandlebarsParser, Rule};
8
use crate::RenderErrorReason;
9

10
#[derive(PartialEq, Eq, Clone, Debug)]
11
#[non_exhaustive]
12
pub enum PathSeg {
13
    Named(String),
14
    Ruled(Rule),
15
}
16

17
/// Represents the Json path in templates.
18
///
19
/// It can be either a local variable like `@first`, `../@index`,
20
/// or a normal relative path like `a/b/c`.
21
#[derive(PartialEq, Eq, Clone, Debug)]
22
pub enum Path {
23
    Relative((Vec<PathSeg>, String)),
24
    Local((usize, String, String)),
25
}
26

27
impl Path {
28
    pub(crate) fn new(raw: &str, segs: Vec<PathSeg>) -> Path {
15✔
29
        if let Some((level, name)) = get_local_path_and_level(&segs) {
45✔
30
            Path::Local((level, name, raw.to_owned()))
1✔
31
        } else {
32
            Path::Relative((segs, raw.to_owned()))
15✔
33
        }
34
    }
35

36
    pub fn parse(raw: &str) -> Result<Path, RenderError> {
3✔
37
        HandlebarsParser::parse(Rule::path, raw)
3✔
38
            .map(|p| {
3✔
39
                let parsed = p.flatten();
3✔
40
                let segs = parse_json_path_from_iter(&mut parsed.peekable(), raw.len());
3✔
41
                Ok(Path::new(raw, segs))
4✔
42
            })
43
            .map_err(|_| RenderErrorReason::InvalidJsonPath(raw.to_owned()))?
×
44
    }
45

46
    pub(crate) fn raw(&self) -> &str {
13✔
47
        match self {
26✔
48
            Path::Relative((_, ref raw)) => raw,
13✔
49
            Path::Local((_, _, ref raw)) => raw,
1✔
50
        }
51
    }
52

UNCOV
53
    pub(crate) fn current() -> Path {
×
UNCOV
54
        Path::Relative((Vec::with_capacity(0), String::new()))
×
55
    }
56

57
    // for test only
58
    pub(crate) fn with_named_paths(name_segs: &[&str]) -> Path {
1✔
59
        let segs = name_segs
1✔
60
            .iter()
61
            .map(|n| PathSeg::Named((*n).to_string()))
2✔
62
            .collect();
63
        Path::Relative((segs, name_segs.join("/")))
1✔
64
    }
65

66
    // for test only
67
    pub(crate) fn segs(&self) -> Option<&[PathSeg]> {
1✔
68
        match self {
1✔
69
            Path::Relative((segs, _)) => Some(segs),
4✔
70
            _ => None,
×
71
        }
72
    }
73
}
74

75
fn get_local_path_and_level(paths: &[PathSeg]) -> Option<(usize, String)> {
19✔
76
    paths.first().and_then(|seg| {
32✔
77
        if seg == &PathSeg::Ruled(Rule::path_local) {
26✔
78
            let mut level = 0;
2✔
79
            while paths.get(level + 1)? == &PathSeg::Ruled(Rule::path_up) {
5✔
80
                level += 1;
2✔
81
            }
82
            if let Some(PathSeg::Named(name)) = paths.get(level + 1) {
3✔
83
                Some((level, name.clone()))
1✔
84
            } else {
85
                None
×
86
            }
87
        } else {
88
            None
17✔
89
        }
90
    })
91
}
92

93
pub(crate) fn parse_json_path_from_iter<'a, I>(it: &mut Peekable<I>, limit: usize) -> Vec<PathSeg>
16✔
94
where
95
    I: Iterator<Item = Pair<'a, Rule>>,
96
{
97
    let mut path_stack = Vec::with_capacity(5);
16✔
98
    while let Some(n) = it.peek() {
32✔
99
        let span = n.as_span();
16✔
100
        if span.end() > limit {
16✔
101
            break;
×
102
        }
103

104
        match n.as_rule() {
15✔
105
            Rule::path_root => {
×
106
                path_stack.push(PathSeg::Ruled(Rule::path_root));
8✔
107
            }
108
            Rule::path_local => {
×
109
                path_stack.push(PathSeg::Ruled(Rule::path_local));
4✔
110
            }
111
            Rule::path_up => {
×
112
                path_stack.push(PathSeg::Ruled(Rule::path_up));
4✔
113
            }
114
            Rule::path_id | Rule::path_raw_id => {
×
115
                let name = n.as_str();
36✔
116
                if name != "this" {
19✔
117
                    path_stack.push(PathSeg::Named(name.to_string()));
16✔
118
                }
119
            }
120
            _ => {}
×
121
        }
122

123
        it.next();
33✔
124
    }
125

126
    path_stack
18✔
127
}
128

129
pub(crate) fn merge_json_path(path_stack: &mut Vec<String>, relative_path: &[PathSeg]) {
15✔
130
    for seg in relative_path {
27✔
131
        match seg {
13✔
132
            PathSeg::Named(ref s) => {
13✔
133
                path_stack.push(s.to_owned());
13✔
134
            }
135
            PathSeg::Ruled(Rule::path_root) => {}
136
            PathSeg::Ruled(Rule::path_up) => {}
137
            _ => {}
138
        }
139
    }
140
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc