Skip to main content

lmn_core/response_template/
mod.rs

1pub mod error;
2pub mod extractor;
3pub mod field;
4pub mod stats;
5
6use std::path::Path;
7
8use serde_json::Value;
9use tracing::instrument;
10
11use error::ResponseTemplateError;
12use field::TrackedField;
13
14pub struct ResponseTemplate {
15    pub fields: Vec<TrackedField>,
16}
17
18impl ResponseTemplate {
19    #[instrument(name = "lmn.response_template.parse", fields(path = %path.display()))]
20    pub fn parse(path: &Path) -> Result<Self, ResponseTemplateError> {
21        let content = std::fs::read_to_string(path)?;
22        let root: Value = serde_json::from_str(&content)?;
23
24        let fields = field::collect_tracked_fields(&root, &[])?;
25
26        if fields.is_empty() {
27            return Err(ResponseTemplateError::InvalidFieldType(
28                "template contains no tracked fields".to_string(),
29            ));
30        }
31
32        Ok(ResponseTemplate { fields })
33    }
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39    use std::io::Write;
40
41    fn write_temp(content: &str) -> tempfile::NamedTempFile {
42        let mut f = tempfile::NamedTempFile::new().unwrap();
43        f.write_all(content.as_bytes()).unwrap();
44        f
45    }
46
47    #[test]
48    fn parse_fails_on_missing_file() {
49        assert!(ResponseTemplate::parse(Path::new("nonexistent.json")).is_err());
50    }
51
52    #[test]
53    fn parse_fails_on_invalid_json() {
54        let f = write_temp("not json");
55        assert!(ResponseTemplate::parse(f.path()).is_err());
56    }
57
58    #[test]
59    fn parse_fails_when_no_tracked_fields() {
60        let f = write_temp(r#"{"status": "ok"}"#);
61        assert!(ResponseTemplate::parse(f.path()).is_err());
62    }
63
64    #[test]
65    fn parse_succeeds_with_valid_template() {
66        let f = write_temp(r#"{"status": "{{STRING}}"}"#);
67        let rt = ResponseTemplate::parse(f.path()).unwrap();
68        assert_eq!(rt.fields.len(), 1);
69    }
70}