cargo_perf/rules/
mod.rs

1pub mod allocation_rules;
2pub mod async_rules;
3pub mod database_rules;
4pub mod iter_rules;
5pub mod lock_across_await;
6pub mod memory_rules;
7pub mod registry;
8pub mod visitor;
9
10use crate::engine::AnalysisContext;
11use serde::{Deserialize, Serialize};
12use std::path::PathBuf;
13
14/// Maximum size (in bytes) for extracted source text in auto-fixes.
15/// This prevents memory issues when generating fixes for very large expressions.
16/// Any expression larger than this will not have an auto-fix generated.
17pub const MAX_FIX_TEXT_SIZE: usize = 10 * 1024; // 10 KB
18
19/// Severity levels for diagnostics
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
21#[serde(rename_all = "lowercase")]
22pub enum Severity {
23    #[default]
24    Info,
25    Warning,
26    Error,
27}
28
29impl std::fmt::Display for Severity {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        match self {
32            Severity::Info => write!(f, "info"),
33            Severity::Warning => write!(f, "warning"),
34            Severity::Error => write!(f, "error"),
35        }
36    }
37}
38
39impl std::str::FromStr for Severity {
40    type Err = String;
41
42    fn from_str(s: &str) -> Result<Self, Self::Err> {
43        match s.to_lowercase().as_str() {
44            "info" => Ok(Severity::Info),
45            "warning" | "warn" => Ok(Severity::Warning),
46            "error" | "deny" => Ok(Severity::Error),
47            _ => Err(format!("Unknown severity: {}", s)),
48        }
49    }
50}
51
52// CLI integration is only needed for the binary, not library users.
53// This impl is always available since clap is a required dependency,
54// but library users who don't need CLI can ignore it.
55#[cfg(feature = "cli")]
56impl clap::ValueEnum for Severity {
57    fn value_variants<'a>() -> &'a [Self] {
58        &[Severity::Info, Severity::Warning, Severity::Error]
59    }
60
61    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
62        match self {
63            Severity::Info => Some(clap::builder::PossibleValue::new("info")),
64            Severity::Warning => Some(clap::builder::PossibleValue::new("warning")),
65            Severity::Error => Some(clap::builder::PossibleValue::new("error")),
66        }
67    }
68}
69
70/// A diagnostic reported by a rule
71#[derive(Debug, Clone, Serialize)]
72pub struct Diagnostic {
73    pub rule_id: &'static str,
74    pub severity: Severity,
75    pub message: String,
76    pub file_path: PathBuf,
77    pub line: usize,
78    pub column: usize,
79    pub end_line: Option<usize>,
80    pub end_column: Option<usize>,
81    pub suggestion: Option<String>,
82    pub fix: Option<Fix>,
83}
84
85/// An auto-fix for a diagnostic
86#[derive(Debug, Clone, Serialize)]
87pub struct Fix {
88    pub description: String,
89    pub replacements: Vec<Replacement>,
90}
91
92#[derive(Debug, Clone, Serialize)]
93pub struct Replacement {
94    pub file_path: PathBuf,
95    pub start_byte: usize,
96    pub end_byte: usize,
97    pub new_text: String,
98}
99
100/// The Rule trait - implement this to add new checks
101pub trait Rule: Send + Sync {
102    /// Unique identifier for this rule (e.g., "async-block-in-async")
103    fn id(&self) -> &'static str;
104
105    /// Human-readable name
106    fn name(&self) -> &'static str;
107
108    /// Description of what this rule checks
109    fn description(&self) -> &'static str;
110
111    /// Default severity level
112    fn default_severity(&self) -> Severity;
113
114    /// Run the check and return diagnostics
115    fn check(&self, ctx: &AnalysisContext) -> Vec<Diagnostic>;
116}