use std::fmt;
use serde::{Deserialize, Serialize};
use crate::duplication::DuplicationReport;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "kind", content = "name")]
#[serde(rename_all = "snake_case")]
pub enum ScopeSegment {
Module(String),
Type(String),
Function(String),
}
impl fmt::Display for ScopeSegment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ScopeSegment::Module(n) => write!(f, "mod {n}"),
ScopeSegment::Type(n) => write!(f, "type {n}"),
ScopeSegment::Function(n) => write!(f, "fn {n}"),
}
}
}
pub fn module_path_from_scope(scope: &[ScopeSegment]) -> String {
scope
.iter()
.filter_map(|s| match s {
ScopeSegment::Module(name) => Some(name.as_str()),
_ => None,
})
.collect::<Vec<_>>()
.join("::")
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionMetrics {
pub name: String,
pub file: String,
#[serde(default)]
pub module_path: String,
#[serde(default)]
pub scope_path: Vec<ScopeSegment>,
pub line: usize,
pub lines: usize,
pub params: usize,
pub nesting_depth: usize,
pub cyclomatic: usize,
pub mutable_bindings: usize,
pub internal_state_cardinality_log2: f64,
pub assertions: usize,
pub meaningful_assertions: usize,
pub is_test: bool,
pub is_pub: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TypeKind {
Struct,
Enum,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeMetrics {
pub name: String,
pub file: String,
#[serde(default)]
pub module_path: String,
#[serde(default)]
pub scope_path: Vec<ScopeSegment>,
pub line: usize,
pub kind: TypeKind,
pub bool_fields: usize,
pub option_fields: usize,
pub total_fields: usize,
pub state_cardinality: u64,
pub state_cardinality_log2: f64,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Summary {
pub function_count: usize,
pub max_function_lines: usize,
pub mean_function_lines: f64,
pub functions_over_70_lines: usize,
pub max_nesting_depth: usize,
pub mean_nesting_depth: f64,
pub max_cyclomatic: usize,
pub mean_cyclomatic: f64,
pub max_params: usize,
pub total_mutable_bindings: usize,
pub type_count: usize,
pub total_bool_fields: usize,
pub total_option_fields: usize,
pub max_state_cardinality_log2: f64,
pub functions_under_2_assertions: usize,
pub nontrivial_functions_under_2_assertions: usize,
pub nontrivial_function_count: usize,
pub total_assertions: usize,
pub mean_assertions_per_function: f64,
pub total_meaningful_assertions: usize,
pub mean_meaningful_assertions_per_function: f64,
pub test_function_count: usize,
pub production_function_count: usize,
pub public_function_count: usize,
#[serde(default)]
pub macro_fn_count: usize,
#[serde(default)]
pub macro_export_fn_count: usize,
pub function_overhead_ratio: f64,
pub test_density: f64,
pub total_production_cyclomatic: usize,
pub production_lines: usize,
pub exact_duplicate_groups: usize,
pub near_duplicate_pairs: usize,
pub duplication_score: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileEntropy {
pub file: String,
pub tokens: usize,
pub vocabulary: usize,
pub entropy_bits: f64,
pub normalized_entropy: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntropyMetrics {
pub total_tokens: usize,
pub vocabulary_size: usize,
pub entropy_bits: f64,
pub normalized_entropy: f64,
pub per_file: Vec<FileEntropy>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SemanticSummary {
pub coupling_density: f64,
pub coupling_module_count: usize,
pub coupling_edge_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalysisReport {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub analysis_root: Option<String>,
pub files_analyzed: usize,
pub total_lines: usize,
pub functions: Vec<FunctionMetrics>,
pub types: Vec<TypeMetrics>,
pub entropy: EntropyMetrics,
pub duplication: DuplicationReport,
pub summary: Summary,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub semantic: Option<SemanticSummary>,
}