use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum SymbolVisibility {
Pub,
PubCrate,
Private,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum SymbolKind {
Function,
Class,
Interface,
TypeAlias,
Enum,
Variable,
Component,
Method,
Property,
Struct,
Trait,
ImplMethod,
Const,
Static,
Macro,
}
#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DecoratorInfo {
pub name: String,
pub object: Option<String>,
pub attribute: Option<String>,
pub args_raw: Option<String>,
pub framework: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SymbolInfo {
pub name: String,
pub kind: SymbolKind,
pub line: usize,
pub col: usize,
pub line_end: usize,
pub is_exported: bool,
pub is_default: bool,
pub visibility: SymbolVisibility,
pub trait_impl: Option<String>,
pub decorators: Vec<DecoratorInfo>,
}
impl Default for SymbolInfo {
fn default() -> Self {
Self {
name: String::new(),
kind: SymbolKind::Variable,
line: 0,
col: 0,
line_end: 0,
is_exported: false,
is_default: false,
visibility: SymbolVisibility::Private,
trait_impl: None,
decorators: Vec::new(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum FileKind {
#[default]
Source,
Doc,
Config,
Ci,
Asset,
Other,
}
pub fn classify_file_kind(path: &std::path::Path) -> FileKind {
if path.components().any(|c| {
let s = c.as_os_str().to_str().unwrap_or("");
s == ".github" || s == ".gitlab" || s == ".circleci"
}) {
return FileKind::Ci;
}
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
match ext {
"ts" | "tsx" | "js" | "jsx" | "rs" | "py" | "go" => FileKind::Source,
"md" | "txt" | "rst" | "adoc" => FileKind::Doc,
"toml" | "yaml" | "yml" | "json" | "ini" | "env" | "cfg" | "conf" | "properties"
| "xml" => FileKind::Config,
"png" | "jpg" | "jpeg" | "gif" | "svg" | "ico" | "woff" | "woff2" | "ttf" | "eot"
| "mp3" | "mp4" | "webm" | "pdf" => FileKind::Asset,
_ => {
let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
match name {
"Dockerfile" | "Makefile" | "Jenkinsfile" | "Procfile" => FileKind::Config,
".gitlab-ci.yml" => FileKind::Ci,
".env" | ".env.local" | ".env.production" | ".env.development" => FileKind::Config,
_ => FileKind::Other,
}
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct FileInfo {
pub path: PathBuf,
pub language: String,
pub crate_name: Option<String>,
pub kind: FileKind,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ExternalPackageInfo {
pub name: String,
pub version: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum GraphNode {
File(FileInfo),
Symbol(SymbolInfo),
ExternalPackage(ExternalPackageInfo),
Builtin { name: String },
UnresolvedImport { specifier: String, reason: String },
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_classify_source_files() {
assert_eq!(
classify_file_kind(std::path::Path::new("src/main.rs")),
FileKind::Source
);
assert_eq!(
classify_file_kind(std::path::Path::new("app.ts")),
FileKind::Source
);
assert_eq!(
classify_file_kind(std::path::Path::new("app.tsx")),
FileKind::Source
);
assert_eq!(
classify_file_kind(std::path::Path::new("index.js")),
FileKind::Source
);
assert_eq!(
classify_file_kind(std::path::Path::new("App.jsx")),
FileKind::Source
);
assert_eq!(
classify_file_kind(std::path::Path::new("main.go")),
FileKind::Source
);
}
#[test]
fn test_classify_doc_files() {
assert_eq!(
classify_file_kind(std::path::Path::new("README.md")),
FileKind::Doc
);
assert_eq!(
classify_file_kind(std::path::Path::new("CHANGELOG.txt")),
FileKind::Doc
);
assert_eq!(
classify_file_kind(std::path::Path::new("docs/guide.rst")),
FileKind::Doc
);
assert_eq!(
classify_file_kind(std::path::Path::new("manual.adoc")),
FileKind::Doc
);
}
#[test]
fn test_classify_config_files() {
assert_eq!(
classify_file_kind(std::path::Path::new("Cargo.toml")),
FileKind::Config
);
assert_eq!(
classify_file_kind(std::path::Path::new("config.yaml")),
FileKind::Config
);
assert_eq!(
classify_file_kind(std::path::Path::new("settings.yml")),
FileKind::Config
);
assert_eq!(
classify_file_kind(std::path::Path::new("package.json")),
FileKind::Config
);
assert_eq!(
classify_file_kind(std::path::Path::new(".env")),
FileKind::Config
);
assert_eq!(
classify_file_kind(std::path::Path::new("app.xml")),
FileKind::Config
);
}
#[test]
fn test_classify_ci_files() {
assert_eq!(
classify_file_kind(std::path::Path::new(".github/workflows/ci.yml")),
FileKind::Ci
);
assert_eq!(
classify_file_kind(std::path::Path::new(".gitlab/ci/build.yml")),
FileKind::Ci
);
assert_eq!(
classify_file_kind(std::path::Path::new(".circleci/config.yml")),
FileKind::Ci
);
}
#[test]
fn test_classify_asset_files() {
assert_eq!(
classify_file_kind(std::path::Path::new("logo.png")),
FileKind::Asset
);
assert_eq!(
classify_file_kind(std::path::Path::new("photo.jpg")),
FileKind::Asset
);
assert_eq!(
classify_file_kind(std::path::Path::new("icon.svg")),
FileKind::Asset
);
assert_eq!(
classify_file_kind(std::path::Path::new("font.woff2")),
FileKind::Asset
);
}
#[test]
fn test_classify_special_names() {
assert_eq!(
classify_file_kind(std::path::Path::new("Dockerfile")),
FileKind::Config
);
assert_eq!(
classify_file_kind(std::path::Path::new("Makefile")),
FileKind::Config
);
assert_eq!(
classify_file_kind(std::path::Path::new("Jenkinsfile")),
FileKind::Config
);
}
#[test]
fn test_classify_other_files() {
assert_eq!(
classify_file_kind(std::path::Path::new("LICENSE")),
FileKind::Other
);
assert_eq!(
classify_file_kind(std::path::Path::new("some.random")),
FileKind::Other
);
}
#[test]
fn test_file_kind_default_is_source() {
assert_eq!(FileKind::default(), FileKind::Source);
}
}