Skip to main content

featherbit/vars/
jsonpath.rs

1//! JSONPath subjects for condition expressions: `$.user.name` (request
2//! body), `request_body:$...`, `response_body:$...`. Paths are compiled at
3//! config load (RFC 9535 via serde_json_path); malformed paths fail policy
4//! compilation, not requests.
5
6use serde_json_path::JsonPath;
7
8/// Which body a JSONPath subject queries.
9#[derive(Debug)]
10pub enum BodyTarget {
11    Request,
12    Response,
13}
14
15/// A compiled JSONPath subject.
16#[derive(Debug)]
17pub struct JsonSubject {
18    pub target: BodyTarget,
19    pub path: JsonPath,
20    /// The subject string as written in config, for error messages.
21    /// Not yet read within this crate (kept for downstream diagnostics /
22    /// future condition-engine work); silence dead_code accordingly.
23    #[allow(dead_code)]
24    pub raw: String,
25}
26
27/// Recognizes and compiles a JSONPath subject. `None` means the subject is
28/// a plain var name; `Some(Err)` means it looked like a JSONPath subject
29/// but the path is malformed.
30pub fn parse_json_subject(subject: &str) -> Option<Result<JsonSubject, String>> {
31    let (target, path_str) = if let Some(p) = subject.strip_prefix("request_body:") {
32        (BodyTarget::Request, p)
33    } else if let Some(p) = subject.strip_prefix("response_body:") {
34        (BodyTarget::Response, p)
35    } else if subject.starts_with('$') {
36        (BodyTarget::Request, subject)
37    } else {
38        return None;
39    };
40    Some(
41        JsonPath::parse(path_str)
42            .map(|path| JsonSubject {
43                target,
44                path,
45                raw: subject.to_string(),
46            })
47            .map_err(|e| format!("invalid JSONPath '{}': {}", subject, e)),
48    )
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn test_jsonpath_subject_detection() {
57        assert!(parse_json_subject("http_authorization").is_none());
58        assert!(parse_json_subject("arg_name").is_none());
59        assert!(matches!(
60            parse_json_subject("$.user.name"),
61            Some(Ok(JsonSubject {
62                target: BodyTarget::Request,
63                ..
64            }))
65        ));
66        assert!(matches!(
67            parse_json_subject("request_body:$.a"),
68            Some(Ok(JsonSubject {
69                target: BodyTarget::Request,
70                ..
71            }))
72        ));
73        assert!(matches!(
74            parse_json_subject("response_body:$.a[*].b"),
75            Some(Ok(JsonSubject {
76                target: BodyTarget::Response,
77                ..
78            }))
79        ));
80        // looked like JSONPath, malformed path -> hard error
81        assert!(matches!(parse_json_subject("$.["), Some(Err(_))));
82        assert!(matches!(
83            parse_json_subject("response_body:nope"),
84            Some(Err(_))
85        ));
86    }
87}