use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
pub const DEFAULT_LOC_THRESHOLD: usize = 1000;
pub const COLOR_RED: &str = "\u{001b}[31m";
pub const COLOR_RESET: &str = "\u{001b}[0m";
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum ColorMode {
#[default]
Auto,
Always,
Never,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum OutputMode {
Human,
Json,
Jsonl,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Mode {
Tree,
AnalyzeImports,
Init,
Slice,
Trace,
ForAi,
Findings,
Summary,
Git(GitSubcommand),
Search,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum GitSubcommand {
Compare {
from: String,
to: Option<String>,
},
Blame {
file: String,
},
History {
symbol: Option<String>,
file: Option<String>,
limit: usize,
},
WhenIntroduced {
circular: Option<String>,
dead: Option<String>,
import: Option<String>,
},
}
#[derive(Clone)]
pub struct Options {
pub extensions: Option<HashSet<String>>,
pub ignore_paths: Vec<std::path::PathBuf>,
pub ignore_globs: Option<std::sync::Arc<globset::GlobSet>>,
pub use_gitignore: bool,
pub max_depth: Option<usize>,
pub color: ColorMode,
pub output: OutputMode,
pub summary: bool,
pub summary_limit: usize,
pub summary_only: bool,
pub show_hidden: bool,
pub show_ignored: bool,
pub loc_threshold: usize,
pub analyze_limit: usize,
pub report_path: Option<std::path::PathBuf>,
pub serve: bool,
pub editor_cmd: Option<String>,
pub max_graph_nodes: Option<usize>,
pub max_graph_edges: Option<usize>,
pub verbose: bool,
pub scan_all: bool,
pub symbol: Option<String>,
pub impact: Option<String>,
pub find_artifacts: bool,
}
impl Default for Options {
fn default() -> Self {
Self {
extensions: None,
ignore_paths: Vec::new(),
ignore_globs: None,
use_gitignore: true,
max_depth: None,
color: ColorMode::Auto,
output: OutputMode::Human,
summary: false,
summary_limit: 50,
summary_only: false,
show_hidden: false,
show_ignored: false,
loc_threshold: 500,
analyze_limit: 100,
report_path: None,
serve: false,
editor_cmd: None,
max_graph_nodes: None,
max_graph_edges: None,
verbose: false,
scan_all: false,
symbol: None,
impact: None,
find_artifacts: false,
}
}
}
pub struct LineEntry {
pub label: String,
pub loc: Option<usize>,
pub relative_path: String,
pub is_dir: bool,
pub is_large: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SymbolMatch {
pub line: usize,
pub context: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct LocalSymbol {
pub name: String,
pub kind: String,
#[serde(default)]
pub line: Option<usize>,
#[serde(default)]
pub context: String,
#[serde(default)]
pub is_exported: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SymbolUsage {
pub name: String,
pub line: usize,
#[serde(default)]
pub context: String,
}
pub struct LargeEntry {
pub path: String,
pub loc: usize,
}
#[derive(Default)]
pub struct Stats {
pub directories: usize,
pub files: usize,
pub files_with_loc: usize,
pub total_loc: usize,
}
pub struct Collectors<'a> {
pub entries: &'a mut Vec<LineEntry>,
pub large_entries: &'a mut Vec<LargeEntry>,
pub stats: &'a mut Stats,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ImportEntry {
#[serde(default)]
pub line: Option<usize>,
pub source: String,
pub source_raw: String,
pub kind: ImportKind,
pub resolved_path: Option<String>,
pub is_bare: bool,
pub symbols: Vec<ImportSymbol>,
pub resolution: ImportResolutionKind,
pub is_type_checking: bool,
#[serde(default)]
pub is_lazy: bool,
#[serde(default)]
pub is_crate_relative: bool,
#[serde(default)]
pub is_super_relative: bool,
#[serde(default)]
pub is_self_relative: bool,
#[serde(default)]
pub is_mod_declaration: bool,
#[serde(default)]
pub raw_path: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ImportKind {
Static,
Type,
SideEffect,
Dynamic,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ImportResolutionKind {
Local,
Stdlib,
Dynamic,
Unknown,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ImportSymbol {
pub name: String,
pub alias: Option<String>,
#[serde(default)]
pub is_default: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ReexportEntry {
pub source: String,
pub kind: ReexportKind,
pub resolved: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ReexportKind {
Star,
Named(Vec<(String, String)>),
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ParamInfo {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub type_annotation: Option<String>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub has_default: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ExportSymbol {
pub name: String,
pub kind: String,
pub export_type: String,
pub line: Option<usize>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub params: Vec<ParamInfo>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CommandRef {
pub name: String,
pub exposed_name: Option<String>,
pub line: usize,
pub generic_type: Option<String>,
pub payload: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub plugin_name: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CommandPayloadCasing {
pub command: String,
pub key: String,
pub path: String,
pub line: usize,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct StringLiteral {
pub value: String,
pub line: usize,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct RouteInfo {
pub framework: String,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
pub line: usize,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct DynamicExecTemplate {
pub template: String,
pub generated_prefixes: Vec<String>,
pub line: usize,
pub call_type: String,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct SysModulesInjection {
pub module_name: String,
pub line: usize,
pub value: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EventRef {
pub raw_name: Option<String>,
pub name: String,
pub line: usize,
pub kind: String,
pub awaited: bool,
pub payload: Option<String>,
#[serde(default)]
pub is_dynamic: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PyRaceIndicator {
pub line: usize,
pub concurrency_type: String,
pub pattern: String,
pub risk: String,
pub message: String,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct FileAnalysis {
#[serde(default)]
pub path: String,
#[serde(default)]
pub loc: usize,
#[serde(default)]
pub language: String,
#[serde(default)]
pub kind: String,
#[serde(default)]
pub is_test: bool,
#[serde(default)]
pub is_generated: bool,
#[serde(default)]
pub is_flow_file: bool,
#[serde(default)]
pub imports: Vec<ImportEntry>,
#[serde(default)]
pub reexports: Vec<ReexportEntry>,
#[serde(default)]
pub dynamic_imports: Vec<String>,
#[serde(default)]
pub exports: Vec<ExportSymbol>,
#[serde(default)]
pub local_symbols: Vec<LocalSymbol>,
#[serde(default)]
pub symbol_usages: Vec<SymbolUsage>,
#[serde(default)]
pub command_calls: Vec<CommandRef>,
#[serde(default)]
pub command_handlers: Vec<CommandRef>,
#[serde(default)]
pub command_payload_casing: Vec<CommandPayloadCasing>,
#[serde(default)]
pub string_literals: Vec<StringLiteral>,
#[serde(default)]
pub event_emits: Vec<EventRef>,
#[serde(default)]
pub event_listens: Vec<EventRef>,
#[serde(default)]
pub event_consts: HashMap<String, String>,
#[serde(default)]
pub matches: Vec<SymbolMatch>,
#[serde(default)]
pub entry_points: Vec<String>,
#[serde(default)]
pub tauri_registered_handlers: Vec<String>,
#[serde(default)]
pub mtime: u64,
#[serde(default)]
pub size: u64,
#[serde(default)]
pub py_race_indicators: Vec<PyRaceIndicator>,
#[serde(default)]
pub is_typed_package: bool,
#[serde(default)]
pub is_namespace_package: bool,
#[serde(default)]
pub local_uses: Vec<String>,
#[serde(default)]
pub signature_uses: Vec<SignatureUse>,
#[serde(default)]
pub routes: Vec<RouteInfo>,
#[serde(default)]
pub pytest_fixtures: Vec<String>,
#[serde(default)]
pub has_weak_collections: bool,
#[serde(default)]
pub dynamic_exec_templates: Vec<DynamicExecTemplate>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub sys_modules_injections: Vec<SysModulesInjection>,
}
impl ImportEntry {
pub fn new(source: String, kind: ImportKind) -> Self {
let is_bare = !source.starts_with('.') && !source.starts_with('/');
Self {
line: None,
source_raw: source.clone(),
source,
kind,
resolved_path: None,
is_bare,
symbols: Vec::new(),
resolution: ImportResolutionKind::Unknown,
is_type_checking: false,
is_lazy: false,
is_crate_relative: false,
is_super_relative: false,
is_self_relative: false,
raw_path: String::new(),
is_mod_declaration: false,
}
}
}
impl ExportSymbol {
pub fn new(name: String, kind: &str, export_type: &str, line: Option<usize>) -> Self {
Self {
name,
kind: kind.to_string(),
export_type: export_type.to_string(),
line,
params: Vec::new(),
}
}
pub fn with_params(
name: String,
kind: &str,
export_type: &str,
line: Option<usize>,
params: Vec<ParamInfo>,
) -> Self {
Self {
name,
kind: kind.to_string(),
export_type: export_type.to_string(),
line,
params,
}
}
}
impl FileAnalysis {
pub fn new(path: String) -> Self {
Self {
path,
loc: 0,
language: String::new(),
kind: "code".to_string(),
is_test: false,
is_generated: false,
is_flow_file: false,
imports: Vec::new(),
reexports: Vec::new(),
dynamic_imports: Vec::new(),
exports: Vec::new(),
local_symbols: Vec::new(),
symbol_usages: Vec::new(),
command_calls: Vec::new(),
command_handlers: Vec::new(),
command_payload_casing: Vec::new(),
string_literals: Vec::new(),
event_emits: Vec::new(),
event_listens: Vec::new(),
event_consts: HashMap::new(),
matches: Vec::new(),
entry_points: Vec::new(),
tauri_registered_handlers: Vec::new(),
py_race_indicators: Vec::new(),
mtime: 0,
size: 0,
is_typed_package: false,
is_namespace_package: false,
local_uses: Vec::new(),
signature_uses: Vec::new(),
routes: Vec::new(),
pytest_fixtures: Vec::new(),
has_weak_collections: false,
dynamic_exec_templates: Vec::new(),
sys_modules_injections: Vec::new(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SignatureUseKind {
Parameter,
Return,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SignatureUse {
pub function: String,
pub usage: SignatureUseKind,
pub type_name: String,
#[serde(default)]
pub line: Option<usize>,
}
pub type ExportIndex = HashMap<String, Vec<String>>;
pub type PayloadEntry = (String, usize, Option<String>);
pub type PayloadMap = HashMap<String, Vec<PayloadEntry>>;