1use std::sync::OnceLock;
4
5use crate::cmds::rsl::engine::FileContext;
6use crate::cmds::rsl::output::FormattedRuleViolation;
7use crate::cmds::rsl::output::ViolationOutputFormat;
8use crate::cmds::rsl::rules::aliased_import::AliasedImportRule;
9use crate::cmds::rsl::rules::misordered_fn::MisorderedFnRule;
10use crate::cmds::rsl::rules::misordered_item_group::MisorderedItemGroupRule;
11use crate::cmds::rsl::rules::misordered_visibility::MisorderedVisibilityRule;
12use crate::cmds::rsl::rules::nonadjacent_impl::NonadjacentImplRule;
13use crate::cmds::rsl::rules::overqualified_call::OverqualifiedCallRule;
14use crate::cmds::rsl::rules::qualified_item::QualifiedItemRule;
15use crate::cmds::rsl::rules::relative_path::RelativePathRule;
16use crate::cmds::rsl::rules::unqualified_call::UnqualifiedCallRule;
17
18pub(super) mod aliased_import;
19pub(super) mod common;
20pub(super) mod misordered_fn;
21pub(super) mod misordered_item_group;
22pub(super) mod misordered_visibility;
23pub(super) mod nonadjacent_impl;
24pub(super) mod overqualified_call;
25pub(super) mod qualified_item;
26pub(super) mod relative_path;
27pub(super) mod unqualified_call;
28
29static RULES: OnceLock<[Box<dyn Rule>; 9]> = OnceLock::new();
30
31trait Rule: Send + Sync {
36 fn check(&self, ctx: &FileContext<'_>) -> Vec<Box<dyn RuleViolation>>;
37}
38
39impl<T> Rule for T
40where
41 T: crate::cmds::rsl::rules::TypedRule,
42{
43 fn check(&self, ctx: &FileContext<'_>) -> Vec<Box<dyn RuleViolation>> {
44 <T as crate::cmds::rsl::rules::TypedRule>::check(self, ctx)
45 .into_iter()
46 .map(|violation| Box::new(violation) as Box<dyn RuleViolation>)
47 .collect()
48 }
49}
50
51pub(super) trait RuleViolation: Send + Sync {
53 fn render(&self, format: ViolationOutputFormat) -> String;
54}
55
56impl<T> RuleViolation for T
57where
58 T: crate::cmds::rsl::rules::TypedRuleViolation + FormattedRuleViolation,
59{
60 fn render(&self, format: ViolationOutputFormat) -> String {
61 FormattedRuleViolation::format(self, format)
62 }
63}
64
65pub(super) trait TypedRule: Send + Sync + 'static {
70 type Violation: crate::cmds::rsl::rules::TypedRuleViolation<Rule = Self> + FormattedRuleViolation + 'static;
71
72 fn code() -> &'static str;
73
74 fn check(&self, ctx: &FileContext<'_>) -> Vec<Self::Violation>;
75}
76
77pub(super) trait TypedRuleViolation: Send + Sync + 'static {
79 type Rule: crate::cmds::rsl::rules::TypedRule<Violation = Self>;
80}
81
82pub(super) fn check(ctx: &FileContext<'_>) -> Vec<Box<dyn RuleViolation>> {
83 let mut violations = Vec::new();
84 for rule in self::rules() {
85 violations.extend(rule.check(ctx));
86 }
87
88 violations
89}
90
91fn rules() -> &'static [Box<dyn Rule>] {
92 RULES
93 .get_or_init(|| {
94 [
95 Box::new(MisorderedItemGroupRule::new(None)) as Box<dyn Rule>,
96 Box::new(MisorderedVisibilityRule::new(None)),
97 Box::new(NonadjacentImplRule),
98 Box::new(MisorderedFnRule),
99 Box::new(UnqualifiedCallRule),
100 Box::new(OverqualifiedCallRule),
101 Box::new(QualifiedItemRule::new(
102 crate::cmds::rsl::rules::qualified_item::QUALIFIED_ALLOWED_PATHS,
103 )),
104 Box::new(AliasedImportRule),
105 Box::new(RelativePathRule),
106 ]
107 })
108 .as_slice()
109}
110
111#[cfg(test)]
112pub(super) fn test_ctx(file: &syn::File) -> FileContext<'_> {
113 FileContext {
114 path: std::path::Path::new("test.rs"),
115 file,
116 module_item_lists: crate::cmds::rsl::ast::module_item_lists(file),
117 }
118}