Skip to main content

code_moniker_check/scenario/
mod.rs

1//! Executable check scenarios: a Markdown document describing a file layout,
2//! an inline rules overlay, and the violations the layout is expected to
3//! produce. One document feeds an in-memory workspace the scan pipeline can run
4//! against; see `docs/check-scenarios.md` for the format contract.
5
6use crate::RuleVerdict;
7
8mod expect;
9mod parse;
10mod run;
11#[cfg(test)]
12mod tests;
13
14pub use expect::ExpectedViolation;
15pub use parse::ScenarioError;
16pub use run::ScenarioRun;
17
18#[derive(Clone, Debug, Default, Eq, PartialEq)]
19pub struct ScenarioMeta {
20	pub name: String,
21	pub title: String,
22	pub lang: String,
23	pub blurb: String,
24	pub summary: String,
25	pub published: bool,
26	pub default_rules: Option<bool>,
27}
28
29#[derive(Clone, Debug, Eq, PartialEq)]
30pub struct ScenarioFile {
31	pub path: String,
32	pub fence: String,
33	pub body: String,
34}
35
36#[derive(Clone, Debug, Eq, PartialEq)]
37pub struct UndemonstratedRule {
38	pub rule_id: String,
39	pub reason: String,
40}
41
42#[derive(Clone, Debug, Eq, PartialEq)]
43pub struct ExpectedRuleVerdict {
44	pub rule_id: String,
45	pub verdict: RuleVerdict,
46}
47
48impl std::fmt::Display for ExpectedRuleVerdict {
49	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50		write!(
51			f,
52			"verdict {} = {}",
53			self.rule_id,
54			rule_verdict_name(self.verdict)
55		)
56	}
57}
58
59#[derive(Clone, Debug, Eq, PartialEq)]
60pub struct RuleVerdictMismatch {
61	pub rule_id: String,
62	pub expected: RuleVerdict,
63	pub actual: Option<RuleVerdict>,
64}
65
66impl std::fmt::Display for RuleVerdictMismatch {
67	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68		write!(
69			f,
70			"verdict:   {} expected {}, actual {}",
71			self.rule_id,
72			rule_verdict_name(self.expected),
73			self.actual.map(rule_verdict_name).unwrap_or("absent")
74		)
75	}
76}
77
78fn rule_verdict_name(verdict: RuleVerdict) -> &'static str {
79	match verdict {
80		RuleVerdict::Pass => "pass",
81		RuleVerdict::Fail => "fail",
82		RuleVerdict::Inconclusive => "inconclusive",
83	}
84}
85
86#[derive(Clone, Debug, Eq, PartialEq)]
87pub struct Scenario {
88	pub meta: ScenarioMeta,
89	pub rules: Option<String>,
90	pub files: Vec<ScenarioFile>,
91	pub expects: Vec<ExpectedViolation>,
92	pub verdicts: Vec<ExpectedRuleVerdict>,
93	pub undemonstrated: Vec<UndemonstratedRule>,
94	pub(crate) expect_span: Option<(usize, usize)>,
95}
96
97impl Scenario {
98	pub fn parse(document: &str) -> Result<Self, ScenarioError> {
99		parse::parse_document(document)
100	}
101
102	pub fn effective_default_rules(&self) -> bool {
103		self.meta.default_rules.unwrap_or(self.rules.is_none())
104	}
105}