Skip to main content

code_moniker_check/scenario/
expect.rs

1use std::fmt;
2
3#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
4pub struct ExpectedViolation {
5	pub path: String,
6	pub lines: (u32, u32),
7	pub rule_id: String,
8}
9
10impl ExpectedViolation {
11	pub fn parse(line: &str) -> Result<Self, String> {
12		parse_expected(line)
13	}
14}
15
16impl fmt::Display for ExpectedViolation {
17	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18		write!(
19			f,
20			"{} @ {}:{}",
21			self.rule_id,
22			self.path,
23			format_span(self.lines)
24		)
25	}
26}
27
28fn format_span((start, end): (u32, u32)) -> String {
29	if start == end {
30		format!("L{start}")
31	} else {
32		format!("L{start}-L{end}")
33	}
34}
35
36fn parse_expected(line: &str) -> Result<ExpectedViolation, String> {
37	let (rule_id, location) = line
38		.split_once('@')
39		.ok_or_else(|| expectation_syntax(line))?;
40	let rule_id = rule_id.trim();
41	let (path, span) = location
42		.trim()
43		.rsplit_once(':')
44		.ok_or_else(|| expectation_syntax(line))?;
45	if rule_id.is_empty() || path.is_empty() {
46		return Err(expectation_syntax(line));
47	}
48	Ok(ExpectedViolation {
49		rule_id: rule_id.to_string(),
50		path: path.to_string(),
51		lines: parse_span(span).ok_or_else(|| expectation_syntax(line))?,
52	})
53}
54
55fn parse_span(span: &str) -> Option<(u32, u32)> {
56	let span = span.strip_prefix('L')?;
57	match span.split_once("-L") {
58		Some((start, end)) => Some((start.parse().ok()?, end.parse().ok()?)),
59		None => {
60			let line = span.parse().ok()?;
61			Some((line, line))
62		}
63	}
64}
65
66fn expectation_syntax(line: &str) -> String {
67	format!("expected `<rule-id> @ <path>:L<start>[-L<end>]`, got `{line}`")
68}