Skip to main content

arborist/
types.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::str::FromStr;
4
5/// Supported programming languages.
6///
7/// Each variant corresponds to a compile-time feature flag. Languages whose
8/// feature flag is not enabled can still be named, but attempting to analyze
9/// code in that language will return [`ArboristError::LanguageNotEnabled`].
10///
11/// The enum is `#[non_exhaustive]` — new languages may be added in minor
12/// releases without breaking existing match arms.
13///
14/// [`ArboristError::LanguageNotEnabled`]: crate::ArboristError::LanguageNotEnabled
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
16#[non_exhaustive]
17pub enum Language {
18    Rust,
19    Python,
20    JavaScript,
21    TypeScript,
22    Java,
23    CSharp,
24    Cpp,
25    C,
26    Go,
27    Php,
28    Kotlin,
29    Swift,
30}
31
32impl fmt::Display for Language {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        match self {
35            Language::Rust => write!(f, "Rust"),
36            Language::Python => write!(f, "Python"),
37            Language::JavaScript => write!(f, "JavaScript"),
38            Language::TypeScript => write!(f, "TypeScript"),
39            Language::Java => write!(f, "Java"),
40            Language::CSharp => write!(f, "C#"),
41            Language::Cpp => write!(f, "C++"),
42            Language::C => write!(f, "C"),
43            Language::Go => write!(f, "Go"),
44            Language::Php => write!(f, "PHP"),
45            Language::Kotlin => write!(f, "Kotlin"),
46            Language::Swift => write!(f, "Swift"),
47        }
48    }
49}
50
51impl FromStr for Language {
52    type Err = String;
53
54    fn from_str(s: &str) -> Result<Self, Self::Err> {
55        match s.to_lowercase().as_str() {
56            "rust" => Ok(Language::Rust),
57            "python" => Ok(Language::Python),
58            "javascript" | "js" => Ok(Language::JavaScript),
59            "typescript" | "ts" => Ok(Language::TypeScript),
60            "java" => Ok(Language::Java),
61            "csharp" | "c#" => Ok(Language::CSharp),
62            "cpp" | "c++" => Ok(Language::Cpp),
63            "c" => Ok(Language::C),
64            "go" => Ok(Language::Go),
65            "php" => Ok(Language::Php),
66            "kotlin" | "kt" => Ok(Language::Kotlin),
67            "swift" => Ok(Language::Swift),
68            _ => Err(format!("Unknown language: {s}")),
69        }
70    }
71}
72
73/// Metrics for a single function or method.
74///
75/// Each function or method discovered by the AST walker produces one
76/// `FunctionMetrics` value. Closures and lambdas do not produce their own
77/// entries; they contribute to the metrics of their containing function.
78///
79/// All three complexity dimensions are always populated. The optional
80/// `exceeds_threshold` field is only set when an [`AnalysisConfig`] with a
81/// `cognitive_threshold` is used.
82#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
83pub struct FunctionMetrics {
84    /// Function or method name (e.g., `"process"` or `"MyStruct::method"`).
85    pub name: String,
86    /// 1-based start line number.
87    pub start_line: usize,
88    /// 1-based end line number.
89    pub end_line: usize,
90    /// Cognitive complexity (SonarSource algorithm).
91    pub cognitive: u64,
92    /// Cyclomatic complexity (McCabe).
93    pub cyclomatic: u64,
94    /// Source lines of code within the function.
95    pub sloc: u64,
96    /// `Some(true)` if cognitive complexity exceeds configured threshold,
97    /// `Some(false)` if within threshold, `None` if no threshold configured.
98    pub exceeds_threshold: Option<bool>,
99}
100
101/// Analysis report for a complete source file.
102///
103/// Returned by [`analyze_file`](crate::analyze_file) and
104/// [`analyze_source`](crate::analyze_source). Contains per-function metrics
105/// and file-level aggregates. Implements `Serialize` and `Deserialize` for
106/// easy JSON output.
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108pub struct FileReport {
109    /// File path (empty string for in-memory analysis).
110    pub path: String,
111    /// Detected or specified language.
112    pub language: Language,
113    /// Functions found, ordered by `start_line` ascending.
114    pub functions: Vec<FunctionMetrics>,
115    /// Sum of all functions' cognitive complexity.
116    pub file_cognitive: u64,
117    /// Sum of all functions' cyclomatic complexity.
118    pub file_cyclomatic: u64,
119    /// Total source lines in the entire file (includes top-level code).
120    pub file_sloc: u64,
121}
122
123/// User-configurable analysis parameters.
124///
125/// Pass to [`analyze_file_with_config`](crate::analyze_file_with_config) or
126/// [`analyze_source_with_config`](crate::analyze_source_with_config) to
127/// control threshold flagging and method inclusion. The [`Default`] impl
128/// sets no threshold and includes methods.
129#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
130pub struct AnalysisConfig {
131    /// When set, populates `exceeds_threshold` on each `FunctionMetrics`.
132    pub cognitive_threshold: Option<u64>,
133    /// Whether to include class/struct methods (default: `true`).
134    pub include_methods: bool,
135}
136
137impl Default for AnalysisConfig {
138    fn default() -> Self {
139        Self {
140            cognitive_threshold: None,
141            include_methods: true,
142        }
143    }
144}