init: archlens — architecture diagram generator
Some checks failed
CI / Check / Test (push) Failing after 1m24s
Some checks failed
CI / Check / Test (push) Failing after 1m24s
Hex arch + DDD, tree-sitter parsing, Mermaid/ASCII output. Supports Rust + Python. 92 tests. CI, diff, --check for staleness detection.
This commit is contained in:
15
crates/adapters/toml-config/Cargo.toml
Normal file
15
crates/adapters/toml-config/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "archlens-toml-config"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
archlens-domain.workspace = true
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
toml.workspace = true
|
||||
serde.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile.workspace = true
|
||||
3
crates/adapters/toml-config/src/lib.rs
Normal file
3
crates/adapters/toml-config/src/lib.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
mod toml_config_loader;
|
||||
|
||||
pub use toml_config_loader::TomlConfigLoader;
|
||||
81
crates/adapters/toml-config/src/toml_config_loader.rs
Normal file
81
crates/adapters/toml-config/src/toml_config_loader.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use archlens_domain::{
|
||||
AnalysisConfig, DiagramLevel, DomainError, OutputConfig, ports::ConfigLoader,
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
struct RawConfig {
|
||||
#[serde(default)]
|
||||
analysis: RawAnalysis,
|
||||
#[serde(default)]
|
||||
output: RawOutput,
|
||||
#[serde(default)]
|
||||
modules: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
struct RawAnalysis {
|
||||
#[serde(default)]
|
||||
exclude: Vec<String>,
|
||||
#[serde(default)]
|
||||
level: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
struct RawOutput {
|
||||
#[serde(default)]
|
||||
#[allow(dead_code)]
|
||||
format: Option<String>,
|
||||
#[serde(default)]
|
||||
path: Option<String>,
|
||||
#[serde(default)]
|
||||
split_by_module: bool,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TomlConfigLoader {
|
||||
raw: RawConfig,
|
||||
}
|
||||
|
||||
impl TomlConfigLoader {
|
||||
pub fn from_path(path: &Path) -> Result<Self, DomainError> {
|
||||
let content =
|
||||
std::fs::read_to_string(path).map_err(|e| DomainError::IoError(e.to_string()))?;
|
||||
let raw: RawConfig =
|
||||
toml::from_str(&content).map_err(|e| DomainError::ConfigError(e.to_string()))?;
|
||||
Ok(Self { raw })
|
||||
}
|
||||
|
||||
fn parse_level(level: &Option<String>) -> DiagramLevel {
|
||||
match level.as_deref() {
|
||||
Some("type") => DiagramLevel::Type,
|
||||
Some("project") => DiagramLevel::Project,
|
||||
_ => DiagramLevel::Module,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigLoader for TomlConfigLoader {
|
||||
fn load_analysis_config(&self) -> Result<AnalysisConfig, DomainError> {
|
||||
let config = AnalysisConfig::default()
|
||||
.with_excludes(self.raw.analysis.exclude.clone())
|
||||
.with_level(Self::parse_level(&self.raw.analysis.level))
|
||||
.with_module_mappings(self.raw.modules.clone());
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn load_output_config(&self) -> Result<OutputConfig, DomainError> {
|
||||
let mut config =
|
||||
OutputConfig::default().with_split_by_module(self.raw.output.split_by_module);
|
||||
|
||||
if let Some(path) = &self.raw.output.path {
|
||||
config = config.with_output_path(path.clone());
|
||||
}
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
68
crates/adapters/toml-config/tests/toml_config_tests.rs
Normal file
68
crates/adapters/toml-config/tests/toml_config_tests.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use std::fs;
|
||||
|
||||
use archlens_domain::{DiagramLevel, ports::ConfigLoader};
|
||||
use archlens_toml_config::TomlConfigLoader;
|
||||
|
||||
#[test]
|
||||
fn loads_analysis_config_from_toml_file() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let config_path = dir.path().join("archlens.toml");
|
||||
fs::write(
|
||||
&config_path,
|
||||
r#"
|
||||
[analysis]
|
||||
exclude = ["tests/", "vendor/"]
|
||||
level = "type"
|
||||
|
||||
[modules]
|
||||
"src/orders" = "Orders"
|
||||
"src/billing" = "Billing"
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let loader = TomlConfigLoader::from_path(&config_path).unwrap();
|
||||
let config = loader.load_analysis_config().unwrap();
|
||||
|
||||
assert_eq!(config.excludes(), &["tests/", "vendor/"]);
|
||||
assert_eq!(config.level(), DiagramLevel::Type);
|
||||
assert_eq!(
|
||||
config.module_mappings().get("src/orders").unwrap(),
|
||||
"Orders"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loads_output_config_from_toml_file() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let config_path = dir.path().join("archlens.toml");
|
||||
fs::write(
|
||||
&config_path,
|
||||
r#"
|
||||
[output]
|
||||
format = "mermaid"
|
||||
path = "docs/arch.mmd"
|
||||
split_by_module = true
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let loader = TomlConfigLoader::from_path(&config_path).unwrap();
|
||||
let config = loader.load_output_config().unwrap();
|
||||
|
||||
assert!(config.split_by_module());
|
||||
assert_eq!(config.output_path(), Some("docs/arch.mmd"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_file_returns_defaults() {
|
||||
let loader = TomlConfigLoader::default();
|
||||
|
||||
let analysis = loader.load_analysis_config().unwrap();
|
||||
assert!(analysis.excludes().is_empty());
|
||||
assert_eq!(analysis.level(), DiagramLevel::Module);
|
||||
|
||||
let output = loader.load_output_config().unwrap();
|
||||
assert!(!output.split_by_module());
|
||||
assert!(output.output_path().is_none());
|
||||
}
|
||||
Reference in New Issue
Block a user