1use std::fmt;
2
3use thiserror::Error;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct SourceLocation {
11 pub line: u32,
12 pub col: u32,
13}
14
15impl fmt::Display for SourceLocation {
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 write!(f, "{}:{}", self.line, self.col)
18 }
19}
20
21#[derive(Debug, Error)]
23#[non_exhaustive]
24pub enum SigmaParserError {
25 #[error("YAML parsing error: {0}")]
26 Yaml(#[from] yaml_serde::Error),
27
28 #[error("{}", format_with_location(.0, .1))]
29 Condition(String, Option<SourceLocation>),
30
31 #[error("Unknown modifier '{0}'")]
32 UnknownModifier(String),
33
34 #[error("Modifier '{0}' is applied more than once")]
35 DuplicateModifier(String),
36
37 #[error(
42 "`not` is not a value modifier in Sigma; express negation in the \
43 condition (e.g. `not selection`) or move the inverted check into a \
44 separate detection used as a filter (e.g. `selection and not other`)"
45 )]
46 NotIsNotAModifier,
47
48 #[error("Invalid field specification: {0}")]
49 InvalidFieldSpec(String),
50
51 #[error("Invalid modifier combination: {0}")]
52 InvalidModifiers(String),
53
54 #[error("Invalid rule: {0}")]
55 InvalidRule(String),
56
57 #[error("Missing required field '{0}'")]
58 MissingField(String),
59
60 #[error("Invalid detection: {0}")]
61 InvalidDetection(String),
62
63 #[error("Invalid correlation rule: {0}")]
64 InvalidCorrelation(String),
65
66 #[error("Invalid timespan '{0}'")]
67 InvalidTimespan(String),
68
69 #[error("Invalid value: {0}")]
70 InvalidValue(String),
71
72 #[error("Invalid collection action '{0}'")]
73 InvalidAction(String),
74
75 #[error("IO error: {0}")]
76 Io(#[from] std::io::Error),
77
78 #[error("YAML merge exceeds maximum depth ({0})")]
79 MergeTooDeep(usize),
80
81 #[error("Condition string too long ({0} bytes, max {1})")]
82 ConditionTooLong(usize, usize),
83}
84
85impl SigmaParserError {
86 pub fn location(&self) -> Option<SourceLocation> {
88 match self {
89 SigmaParserError::Condition(_, loc) => *loc,
90 _ => None,
91 }
92 }
93}
94
95fn format_with_location(msg: &str, loc: &Option<SourceLocation>) -> String {
96 match loc {
97 Some(loc) => format!("Condition parse error at {loc}: {msg}"),
98 None => format!("Condition parse error: {msg}"),
99 }
100}
101
102pub type Result<T> = std::result::Result<T, SigmaParserError>;