pub mod error;
pub mod extractor;
pub mod field;
pub mod stats;
use std::path::Path;
use serde_json::Value;
use tracing::instrument;
use error::ResponseTemplateError;
use field::TrackedField;
pub struct ResponseTemplate {
pub fields: Vec<TrackedField>,
}
impl ResponseTemplate {
#[instrument(name = "lmn.response_template.parse", fields(path = %path.display()))]
pub fn parse(path: &Path) -> Result<Self, ResponseTemplateError> {
let content = std::fs::read_to_string(path)?;
let root: Value = serde_json::from_str(&content)?;
let fields = field::collect_tracked_fields(&root, &[])?;
if fields.is_empty() {
return Err(ResponseTemplateError::InvalidFieldType(
"template contains no tracked fields".to_string(),
));
}
Ok(ResponseTemplate { fields })
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
fn write_temp(content: &str) -> tempfile::NamedTempFile {
let mut f = tempfile::NamedTempFile::new().unwrap();
f.write_all(content.as_bytes()).unwrap();
f
}
#[test]
fn parse_fails_on_missing_file() {
assert!(ResponseTemplate::parse(Path::new("nonexistent.json")).is_err());
}
#[test]
fn parse_fails_on_invalid_json() {
let f = write_temp("not json");
assert!(ResponseTemplate::parse(f.path()).is_err());
}
#[test]
fn parse_fails_when_no_tracked_fields() {
let f = write_temp(r#"{"status": "ok"}"#);
assert!(ResponseTemplate::parse(f.path()).is_err());
}
#[test]
fn parse_succeeds_with_valid_template() {
let f = write_temp(r#"{"status": "{{STRING}}"}"#);
let rt = ResponseTemplate::parse(f.path()).unwrap();
assert_eq!(rt.fields.len(), 1);
}
}