Skip to main content

arborist/
types.rs

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