lmn_core/response_template/
mod.rs1pub 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}