Skip to main content

rustquty_core/
context.rs

1//! Runtime context passed to collectors.
2
3use std::path::PathBuf;
4
5/// Active quality scan profile.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum Profile {
8    /// Fast scan: fmt + clippy only.
9    Fast,
10    /// Full scan: all collectors except slow ones like mutants.
11    #[default]
12    Full,
13    /// Deep scan: all collectors including mutation testing.
14    Deep,
15}
16
17impl std::str::FromStr for Profile {
18    type Err = String;
19    fn from_str(s: &str) -> Result<Self, Self::Err> {
20        match s.to_lowercase().as_str() {
21            "fast" => Ok(Profile::Fast),
22            "full" => Ok(Profile::Full),
23            "deep" => Ok(Profile::Deep),
24            _ => Err(format!("unknown profile: {}", s)),
25        }
26    }
27}
28
29/// Collector names that can be skipped via CLI.
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum CollectorName {
32    Fmt,
33    Clippy,
34    Tests,
35    Coverage,
36    Deny,
37    Audit,
38    Hack,
39    Mutants,
40    Duplicates,
41    Loc,
42    Size,
43    Complexity,
44}
45
46impl std::fmt::Display for CollectorName {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        match self {
49            CollectorName::Fmt => write!(f, "fmt"),
50            CollectorName::Clippy => write!(f, "clippy"),
51            CollectorName::Tests => write!(f, "tests"),
52            CollectorName::Coverage => write!(f, "coverage"),
53            CollectorName::Deny => write!(f, "deny"),
54            CollectorName::Audit => write!(f, "audit"),
55            CollectorName::Hack => write!(f, "hack"),
56            CollectorName::Mutants => write!(f, "mutants"),
57            CollectorName::Duplicates => write!(f, "duplicates"),
58            CollectorName::Loc => write!(f, "loc"),
59            CollectorName::Size => write!(f, "size"),
60            CollectorName::Complexity => write!(f, "complexity"),
61        }
62    }
63}
64
65impl std::str::FromStr for CollectorName {
66    type Err = String;
67    fn from_str(s: &str) -> Result<Self, Self::Err> {
68        match s.to_lowercase().as_str() {
69            "fmt" => Ok(CollectorName::Fmt),
70            "clippy" => Ok(CollectorName::Clippy),
71            "tests" => Ok(CollectorName::Tests),
72            "coverage" => Ok(CollectorName::Coverage),
73            "deny" => Ok(CollectorName::Deny),
74            "audit" => Ok(CollectorName::Audit),
75            "hack" => Ok(CollectorName::Hack),
76            "mutants" => Ok(CollectorName::Mutants),
77            "duplicates" => Ok(CollectorName::Duplicates),
78            "loc" => Ok(CollectorName::Loc),
79            "size" => Ok(CollectorName::Size),
80            "complexity" => Ok(CollectorName::Complexity),
81            _ => Err(format!("unknown collector: {}", s)),
82        }
83    }
84}
85
86/// Runtime context for collector execution.
87#[derive(Debug, Clone)]
88pub struct Context {
89    /// Root of the Cargo workspace being scanned.
90    pub workspace_root: PathBuf,
91    /// Active quality scan profile.
92    pub profile: Profile,
93    /// Explicitly disabled collectors.
94    pub disabled_collectors: Vec<CollectorName>,
95    /// Directory where quality JSON files are written.
96    pub output_dir: PathBuf,
97}
98
99impl Context {
100    pub fn new(workspace_root: PathBuf) -> Self {
101        Self {
102            workspace_root,
103            profile: Profile::default(),
104            disabled_collectors: Vec::new(),
105            output_dir: PathBuf::from("quality"),
106        }
107    }
108
109    pub fn with_profile(mut self, profile: Profile) -> Self {
110        self.profile = profile;
111        self
112    }
113
114    pub fn with_output_dir(mut self, dir: PathBuf) -> Self {
115        self.output_dir = dir;
116        self
117    }
118
119    pub fn disable_collector(&mut self, name: CollectorName) {
120        self.disabled_collectors.push(name);
121    }
122
123    pub fn is_collector_disabled(&self, name: CollectorName) -> bool {
124        self.disabled_collectors.contains(&name)
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn test_profile_parsing() {
134        assert_eq!("fast".parse::<Profile>().unwrap(), Profile::Fast);
135        assert_eq!("FULL".parse::<Profile>().unwrap(), Profile::Full);
136        assert_eq!("Deep".parse::<Profile>().unwrap(), Profile::Deep);
137        assert!("unknown".parse::<Profile>().is_err());
138    }
139
140    #[test]
141    fn test_context_disabled_collectors() {
142        let mut ctx = Context::new(PathBuf::from("/tmp"));
143        assert!(!ctx.is_collector_disabled(CollectorName::Clippy));
144        ctx.disable_collector(CollectorName::Clippy);
145        assert!(ctx.is_collector_disabled(CollectorName::Clippy));
146        assert!(!ctx.is_collector_disabled(CollectorName::Tests));
147    }
148}