use serde::Deserialize;
fn default_schema_version() -> u32 {
1
}
#[derive(Debug, Clone, Deserialize)]
pub struct LogMatcherDef {
pub id: String,
pub source: String,
#[serde(default)]
pub priority: u32,
#[serde(default = "default_schema_version")]
pub schema_version: u32,
pub start: StartDef,
#[serde(default)]
pub body: Vec<BodyRuleDef>,
#[serde(default)]
pub max_lines: Option<u32>,
pub end: EndDef,
pub emit: EmitDef,
}
#[derive(Debug, Clone, Deserialize)]
pub struct StartDef {
#[serde(rename = "match")]
pub pattern: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BodyRuleDef {
#[serde(rename = "match")]
pub pattern: String,
#[serde(default)]
pub optional: bool,
#[serde(default)]
pub repeat: bool,
}
#[derive(Debug, Clone, Deserialize)]
pub struct EndDef {
pub condition: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct EmitDef {
pub severity: String,
pub message: String,
#[serde(default)]
pub file: Option<String>,
#[serde(default)]
pub line: Option<String>,
#[serde(default)]
pub column: Option<String>,
#[serde(default)]
pub code: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
fn parse(yaml: &str) -> LogMatcherDef {
serde_saphyr::from_str(yaml).expect("should parse")
}
#[test]
fn parse_minimal_matcher() {
let yaml = r#"
id: rust.cargo.error
source: cargo
start:
match: '^error'
end:
condition: next_start
emit:
severity: error
message: "{{ message }}"
"#;
let def = parse(yaml);
assert_eq!(def.id, "rust.cargo.error");
assert_eq!(def.source, "cargo");
assert_eq!(def.priority, 0);
assert_eq!(def.schema_version, 1);
assert_eq!(def.start.pattern, "^error");
assert!(def.body.is_empty());
assert!(def.max_lines.is_none());
assert_eq!(def.end.condition, "next_start");
assert_eq!(def.emit.severity, "error");
assert_eq!(def.emit.message, "{{ message }}");
}
#[test]
fn parse_with_body_rules() {
let yaml = r#"
id: my.matcher
source: test
start:
match: '^start'
body:
- match: '^\s+\|'
repeat: true
optional: true
end:
condition: blank_line
emit:
severity: warning
message: "found"
"#;
let def = parse(yaml);
assert_eq!(def.body.len(), 1);
assert!(def.body[0].repeat);
assert!(def.body[0].optional);
}
#[test]
fn parse_all_emit_fields() {
let yaml = r#"
id: full.emit
source: test
start:
match: '^e'
end:
condition: next_start
emit:
severity: error
message: "{{ msg }}"
file: "{{ file }}"
line: "{{ line }}"
column: "{{ col }}"
code: "E{{ code }}"
"#;
let def = parse(yaml);
assert!(def.emit.file.is_some());
assert!(def.emit.line.is_some());
assert!(def.emit.column.is_some());
assert!(def.emit.code.is_some());
}
#[test]
fn parse_max_lines() {
let yaml = r#"
id: capped
source: test
start:
match: '^e'
max_lines: 50
end:
condition: next_start
emit:
severity: error
message: "err"
"#;
let def = parse(yaml);
assert_eq!(def.max_lines, Some(50));
}
}