refactor: move scattered business logic into domain
- CodeGraph::merge_project_edges() replaces presentation-layer function - Language::is_test_file() centralises test file detection (was in walkdir) - AnalysisConfig::is_standard_excluded() centralises default dir exclusions (was in walkdir) - normalize_cargo_package() / normalize_python_package() in domain replace duplicated normalisers in each adapter - walkdir, cargo-workspace, python-project updated to call domain methods
This commit is contained in:
@@ -5,7 +5,7 @@ use serde::Deserialize;
|
|||||||
|
|
||||||
use archlens_domain::{
|
use archlens_domain::{
|
||||||
CodeElement, CodeElementKind, CodeGraph, DomainError, FilePath, ModuleName, Relationship,
|
CodeElement, CodeElementKind, CodeGraph, DomainError, FilePath, ModuleName, Relationship,
|
||||||
RelationshipKind, ports::ProjectAnalyzer,
|
RelationshipKind, normalize_cargo_package, ports::ProjectAnalyzer,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct CargoWorkspaceAnalyzer;
|
pub struct CargoWorkspaceAnalyzer;
|
||||||
@@ -97,7 +97,7 @@ impl ProjectAnalyzer for CargoWorkspaceAnalyzer {
|
|||||||
.map_err(|e| DomainError::ConfigError(e.to_string()))?;
|
.map_err(|e| DomainError::ConfigError(e.to_string()))?;
|
||||||
|
|
||||||
for dep_name in member.dependencies.keys() {
|
for dep_name in member.dependencies.keys() {
|
||||||
let normalized = dep_name.replace('_', "-");
|
let normalized = normalize_cargo_package(dep_name);
|
||||||
if name_set.contains(&normalized)
|
if name_set.contains(&normalized)
|
||||||
&& let Ok(rel) =
|
&& let Ok(rel) =
|
||||||
Relationship::new(package_name, &normalized, RelationshipKind::Composition)
|
Relationship::new(package_name, &normalized, RelationshipKind::Composition)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use serde::Deserialize;
|
|||||||
|
|
||||||
use archlens_domain::{
|
use archlens_domain::{
|
||||||
CodeElement, CodeElementKind, CodeGraph, DomainError, FilePath, Relationship, RelationshipKind,
|
CodeElement, CodeElementKind, CodeGraph, DomainError, FilePath, Relationship, RelationshipKind,
|
||||||
ports::ProjectAnalyzer,
|
normalize_python_package, ports::ProjectAnalyzer,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PythonProjectAnalyzer;
|
pub struct PythonProjectAnalyzer;
|
||||||
@@ -59,7 +59,7 @@ fn extract_dep_name(dep: &str) -> &str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn normalize(name: &str) -> String {
|
fn normalize(name: &str) -> String {
|
||||||
name.to_lowercase().replace(['-', '.'], "_")
|
normalize_python_package(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProjectAnalyzer for PythonProjectAnalyzer {
|
impl ProjectAnalyzer for PythonProjectAnalyzer {
|
||||||
|
|||||||
@@ -6,20 +6,6 @@ use archlens_domain::{
|
|||||||
AnalysisConfig, DomainError, FilePath, Language, SourceFile, ports::FileDiscovery,
|
AnalysisConfig, DomainError, FilePath, Language, SourceFile, ports::FileDiscovery,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_EXCLUDES: &[&str] = &[
|
|
||||||
".venv",
|
|
||||||
"venv",
|
|
||||||
"node_modules",
|
|
||||||
"__pycache__",
|
|
||||||
".git",
|
|
||||||
"target",
|
|
||||||
"bin",
|
|
||||||
"obj",
|
|
||||||
"dist",
|
|
||||||
".tox",
|
|
||||||
".eggs",
|
|
||||||
];
|
|
||||||
|
|
||||||
pub struct WalkdirDiscovery;
|
pub struct WalkdirDiscovery;
|
||||||
|
|
||||||
impl Default for WalkdirDiscovery {
|
impl Default for WalkdirDiscovery {
|
||||||
@@ -42,34 +28,13 @@ impl WalkdirDiscovery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_test_file(path: &Path, language: Language) -> bool {
|
|
||||||
let stem = path
|
|
||||||
.file_stem()
|
|
||||||
.and_then(|s| s.to_str())
|
|
||||||
.unwrap_or_default();
|
|
||||||
let in_tests_dir = path
|
|
||||||
.parent()
|
|
||||||
.map(|p| p.components().any(|c| c.as_os_str() == "tests"))
|
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
if in_tests_dir {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
match language {
|
|
||||||
Language::Rust => stem.ends_with("_test") || stem.ends_with("_tests"),
|
|
||||||
Language::Python => stem.starts_with("test_") || stem.ends_with("_test"),
|
|
||||||
Language::CSharp => stem.ends_with("Tests") || stem.ends_with("Test"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_excluded(path: &Path, root: &Path, excludes: &[String]) -> bool {
|
fn is_excluded(path: &Path, root: &Path, excludes: &[String]) -> bool {
|
||||||
let relative = path.strip_prefix(root).unwrap_or(path);
|
let relative = path.strip_prefix(root).unwrap_or(path);
|
||||||
let relative_str = relative.to_string_lossy();
|
let relative_str = relative.to_string_lossy();
|
||||||
|
|
||||||
for component in relative.components() {
|
for component in relative.components() {
|
||||||
let name = component.as_os_str().to_string_lossy();
|
let name = component.as_os_str().to_string_lossy();
|
||||||
if DEFAULT_EXCLUDES.iter().any(|e| name == *e) {
|
if AnalysisConfig::is_standard_excluded(&name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +74,7 @@ impl FileDiscovery for WalkdirDiscovery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(language) = Self::detect_language(path) {
|
if let Some(language) = Self::detect_language(path) {
|
||||||
if !config.include_tests() && Self::is_test_file(path, language) {
|
if !config.include_tests() && language.is_test_file(path) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(changed) = config.changed_files() {
|
if let Some(changed) = config.changed_files() {
|
||||||
|
|||||||
@@ -279,6 +279,43 @@ impl CodeGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Merge project-level crate dependencies into module-level edges.
|
||||||
|
///
|
||||||
|
/// Maps each crate in `project_graph` to a module name (using the crate's
|
||||||
|
/// explicit module if set, otherwise capitalizing its directory name), then
|
||||||
|
/// adds `Composition` edges between modules whose crates have a dependency —
|
||||||
|
/// but only when both modules already exist in this graph.
|
||||||
|
pub fn merge_project_edges(&mut self, project_graph: &CodeGraph) {
|
||||||
|
let mut crate_to_module: HashMap<String, String> = HashMap::new();
|
||||||
|
for element in project_graph.elements() {
|
||||||
|
let module = if let Some(m) = element.module() {
|
||||||
|
m.as_str().to_string()
|
||||||
|
} else {
|
||||||
|
let path = element.file_path().as_str();
|
||||||
|
let dir = path.split('/').rev().nth(1).unwrap_or(element.name());
|
||||||
|
ModuleName::capitalize(dir)
|
||||||
|
};
|
||||||
|
crate_to_module.insert(element.name().to_string(), module);
|
||||||
|
}
|
||||||
|
|
||||||
|
let graph_modules: HashSet<String> =
|
||||||
|
self.modules().iter().map(|m| m.as_str().to_string()).collect();
|
||||||
|
|
||||||
|
for rel in project_graph.relationships() {
|
||||||
|
let src_module = crate_to_module.get(rel.source());
|
||||||
|
let tgt_module = crate_to_module.get(rel.target());
|
||||||
|
if let (Some(src), Some(tgt)) = (src_module, tgt_module)
|
||||||
|
&& src != tgt
|
||||||
|
&& graph_modules.contains(src)
|
||||||
|
&& graph_modules.contains(tgt)
|
||||||
|
&& let Ok(edge) =
|
||||||
|
Relationship::new(src, tgt, RelationshipKind::Composition)
|
||||||
|
{
|
||||||
|
self.add_relationship(edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute module-to-module edges with relationship counts.
|
/// Compute module-to-module edges with relationship counts.
|
||||||
///
|
///
|
||||||
/// Handles three cases:
|
/// Handles three cases:
|
||||||
|
|||||||
@@ -12,4 +12,6 @@ pub use value_objects::analysis::{AnalysisConfig, AnalysisResult, AnalysisWarnin
|
|||||||
pub use value_objects::graph::{CodeElementKind, RelationshipKind, Visibility};
|
pub use value_objects::graph::{CodeElementKind, RelationshipKind, Visibility};
|
||||||
pub use value_objects::output::{DiagramLevel, OutputConfig, RenderOutput, RenderedFile};
|
pub use value_objects::output::{DiagramLevel, OutputConfig, RenderOutput, RenderedFile};
|
||||||
pub use value_objects::rules::{BoundaryRule, RuleKind, RuleViolation, check_boundary_rules};
|
pub use value_objects::rules::{BoundaryRule, RuleKind, RuleViolation, check_boundary_rules};
|
||||||
pub use value_objects::source::{FilePath, Language, ModuleName, SourceFile};
|
pub use value_objects::source::{
|
||||||
|
FilePath, Language, ModuleName, SourceFile, normalize_cargo_package, normalize_python_package,
|
||||||
|
};
|
||||||
|
|||||||
@@ -68,6 +68,26 @@ impl AnalysisConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const STANDARD_EXCLUDED_DIRS: &[&str] = &[
|
||||||
|
".venv",
|
||||||
|
"venv",
|
||||||
|
"node_modules",
|
||||||
|
"__pycache__",
|
||||||
|
".git",
|
||||||
|
"target",
|
||||||
|
"bin",
|
||||||
|
"obj",
|
||||||
|
"dist",
|
||||||
|
".tox",
|
||||||
|
".eggs",
|
||||||
|
];
|
||||||
|
|
||||||
|
impl AnalysisConfig {
|
||||||
|
pub fn is_standard_excluded(name: &str) -> bool {
|
||||||
|
STANDARD_EXCLUDED_DIRS.contains(&name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for AnalysisConfig {
|
impl Default for AnalysisConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum Language {
|
pub enum Language {
|
||||||
Rust,
|
Rust,
|
||||||
@@ -13,4 +15,26 @@ impl Language {
|
|||||||
Self::Python => "Python",
|
Self::Python => "Python",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_test_file(&self, path: &Path) -> bool {
|
||||||
|
let stem = path
|
||||||
|
.file_stem()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let in_tests_dir = path
|
||||||
|
.parent()
|
||||||
|
.map(|p| p.components().any(|c| c.as_os_str() == "tests"))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if in_tests_dir {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Rust => stem.ends_with("_test") || stem.ends_with("_tests"),
|
||||||
|
Self::Python => stem.starts_with("test_") || stem.ends_with("_test"),
|
||||||
|
Self::CSharp => stem.ends_with("Tests") || stem.ends_with("Test"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,3 +7,11 @@ pub use file_path::FilePath;
|
|||||||
pub use language::Language;
|
pub use language::Language;
|
||||||
pub use module_name::ModuleName;
|
pub use module_name::ModuleName;
|
||||||
pub use source_file::SourceFile;
|
pub use source_file::SourceFile;
|
||||||
|
|
||||||
|
pub fn normalize_cargo_package(name: &str) -> String {
|
||||||
|
name.replace('_', "-")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normalize_python_package(name: &str) -> String {
|
||||||
|
name.to_lowercase().replace(['-', '.'], "_")
|
||||||
|
}
|
||||||
|
|||||||
162
crates/domain/tests/policy_tests.rs
Normal file
162
crates/domain/tests/policy_tests.rs
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use archlens_domain::{
|
||||||
|
CodeElement, CodeElementKind, CodeGraph, FilePath, Language, ModuleName, Relationship,
|
||||||
|
RelationshipKind, normalize_cargo_package, normalize_python_package,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn make_element(name: &str, module: Option<&str>) -> CodeElement {
|
||||||
|
let mut el = CodeElement::new(
|
||||||
|
name,
|
||||||
|
CodeElementKind::Class,
|
||||||
|
FilePath::new(&format!("src/{name}.rs")).unwrap(),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
if let Some(m) = module {
|
||||||
|
el = el.with_module(ModuleName::new(m).unwrap());
|
||||||
|
}
|
||||||
|
el
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── merge_project_edges ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn merge_project_edges_adds_module_level_edges_from_project_deps() {
|
||||||
|
let mut graph = CodeGraph::new();
|
||||||
|
graph.add_element(make_element("Service", Some("Application")));
|
||||||
|
graph.add_element(make_element("Order", Some("Domain")));
|
||||||
|
|
||||||
|
let mut project_graph = CodeGraph::new();
|
||||||
|
project_graph.add_element(
|
||||||
|
CodeElement::new(
|
||||||
|
"myapp-application",
|
||||||
|
CodeElementKind::Project,
|
||||||
|
FilePath::new("crates/application/Cargo.toml").unwrap(),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
project_graph.add_element(
|
||||||
|
CodeElement::new(
|
||||||
|
"myapp-domain",
|
||||||
|
CodeElementKind::Project,
|
||||||
|
FilePath::new("crates/domain/Cargo.toml").unwrap(),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
project_graph.add_relationship(
|
||||||
|
Relationship::new(
|
||||||
|
"myapp-application",
|
||||||
|
"myapp-domain",
|
||||||
|
RelationshipKind::Composition,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
graph.merge_project_edges(&project_graph);
|
||||||
|
|
||||||
|
let module_edges = graph.module_edges();
|
||||||
|
assert!(
|
||||||
|
module_edges.contains_key(&("Application".to_string(), "Domain".to_string())),
|
||||||
|
"expected Application -> Domain edge, got: {module_edges:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn merge_project_edges_ignores_crates_with_no_matching_module() {
|
||||||
|
let mut graph = CodeGraph::new();
|
||||||
|
graph.add_element(make_element("Order", Some("Domain")));
|
||||||
|
|
||||||
|
let mut project_graph = CodeGraph::new();
|
||||||
|
project_graph.add_element(
|
||||||
|
CodeElement::new(
|
||||||
|
"external-lib",
|
||||||
|
CodeElementKind::Project,
|
||||||
|
FilePath::new("external/Cargo.toml").unwrap(),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
project_graph.add_element(
|
||||||
|
CodeElement::new(
|
||||||
|
"myapp-domain",
|
||||||
|
CodeElementKind::Project,
|
||||||
|
FilePath::new("crates/domain/Cargo.toml").unwrap(),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
project_graph.add_relationship(
|
||||||
|
Relationship::new("external-lib", "myapp-domain", RelationshipKind::Composition).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
graph.merge_project_edges(&project_graph);
|
||||||
|
|
||||||
|
// "External" (from external-lib) isn't a known module in graph → no edge
|
||||||
|
assert!(graph.module_edges().is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Language::is_test_file ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn language_detects_python_test_prefix_files() {
|
||||||
|
assert!(Language::Python.is_test_file(Path::new("src/test_orders.py")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn language_detects_python_test_suffix_files() {
|
||||||
|
assert!(Language::Python.is_test_file(Path::new("src/orders_test.py")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn language_detects_rust_test_files() {
|
||||||
|
assert!(Language::Rust.is_test_file(Path::new("src/orders_tests.rs")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn language_detects_files_in_tests_dir() {
|
||||||
|
assert!(Language::Rust.is_test_file(Path::new("tests/integration.rs")));
|
||||||
|
assert!(Language::Python.is_test_file(Path::new("tests/helpers.py")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn language_does_not_flag_regular_files_as_tests() {
|
||||||
|
assert!(!Language::Rust.is_test_file(Path::new("src/orders.rs")));
|
||||||
|
assert!(!Language::Python.is_test_file(Path::new("src/orders.py")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── AnalysisConfig::is_standard_excluded ─────────────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn analysis_config_identifies_standard_excluded_dirs() {
|
||||||
|
use archlens_domain::AnalysisConfig;
|
||||||
|
assert!(AnalysisConfig::is_standard_excluded(".venv"));
|
||||||
|
assert!(AnalysisConfig::is_standard_excluded("node_modules"));
|
||||||
|
assert!(AnalysisConfig::is_standard_excluded("target"));
|
||||||
|
assert!(AnalysisConfig::is_standard_excluded("__pycache__"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn analysis_config_does_not_exclude_source_dirs() {
|
||||||
|
use archlens_domain::AnalysisConfig;
|
||||||
|
assert!(!AnalysisConfig::is_standard_excluded("src"));
|
||||||
|
assert!(!AnalysisConfig::is_standard_excluded("domain"));
|
||||||
|
assert!(!AnalysisConfig::is_standard_excluded("application"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Name normalization ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn normalize_cargo_package_converts_underscores_to_hyphens() {
|
||||||
|
assert_eq!(normalize_cargo_package("myapp_domain"), "myapp-domain");
|
||||||
|
assert_eq!(normalize_cargo_package("myapp-domain"), "myapp-domain");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn normalize_python_package_lowercases_and_unifies_separators() {
|
||||||
|
assert_eq!(normalize_python_package("My-Package"), "my_package");
|
||||||
|
assert_eq!(normalize_python_package("my.package"), "my_package");
|
||||||
|
assert_eq!(normalize_python_package("my_package"), "my_package");
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ use archlens_ascii::AsciiRenderer;
|
|||||||
use archlens_cargo_workspace::CargoWorkspaceAnalyzer;
|
use archlens_cargo_workspace::CargoWorkspaceAnalyzer;
|
||||||
use archlens_d2::D2Renderer;
|
use archlens_d2::D2Renderer;
|
||||||
use archlens_domain::{
|
use archlens_domain::{
|
||||||
BoundaryRule, CodeGraph, DiagramLevel, ModuleName, check_boundary_rules,
|
BoundaryRule, CodeGraph, DiagramLevel, check_boundary_rules,
|
||||||
ports::{ConfigLoader, OutputWriter, ProjectAnalyzer},
|
ports::{ConfigLoader, OutputWriter, ProjectAnalyzer},
|
||||||
};
|
};
|
||||||
use archlens_file_writer::FileOutputWriter;
|
use archlens_file_writer::FileOutputWriter;
|
||||||
@@ -154,7 +154,7 @@ fn build_graph(args: &Cli, level: DiagramLevel) -> Result<CodeGraph> {
|
|||||||
PythonProjectAnalyzer::new().analyze(&args.path).ok()
|
PythonProjectAnalyzer::new().analyze(&args.path).ok()
|
||||||
};
|
};
|
||||||
if let Some(pg) = project_graph {
|
if let Some(pg) = project_graph {
|
||||||
merge_project_deps_as_module_edges(&mut graph, &pg);
|
graph.merge_project_edges(&pg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,49 +265,6 @@ fn write_single(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_project_deps_as_module_edges(
|
|
||||||
graph: &mut archlens_domain::CodeGraph,
|
|
||||||
project_graph: &archlens_domain::CodeGraph,
|
|
||||||
) {
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
let mut crate_to_module: HashMap<String, String> = HashMap::new();
|
|
||||||
for element in project_graph.elements() {
|
|
||||||
let module = if let Some(m) = element.module() {
|
|
||||||
m.as_str().to_string()
|
|
||||||
} else {
|
|
||||||
let path = element.file_path().as_str();
|
|
||||||
let dir = path.split('/').rev().nth(1).unwrap_or(element.name());
|
|
||||||
ModuleName::capitalize(dir)
|
|
||||||
};
|
|
||||||
crate_to_module.insert(element.name().to_string(), module);
|
|
||||||
}
|
|
||||||
|
|
||||||
let graph_modules: std::collections::HashSet<String> = graph
|
|
||||||
.modules()
|
|
||||||
.iter()
|
|
||||||
.map(|m| m.as_str().to_string())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for rel in project_graph.relationships() {
|
|
||||||
let src_module = crate_to_module.get(rel.source());
|
|
||||||
let tgt_module = crate_to_module.get(rel.target());
|
|
||||||
|
|
||||||
if let (Some(src_cap), Some(tgt_cap)) = (src_module, tgt_module)
|
|
||||||
&& src_cap != tgt_cap
|
|
||||||
&& graph_modules.contains(src_cap)
|
|
||||||
&& graph_modules.contains(tgt_cap)
|
|
||||||
&& let Ok(edge) = archlens_domain::Relationship::new(
|
|
||||||
src_cap,
|
|
||||||
tgt_cap,
|
|
||||||
archlens_domain::RelationshipKind::Composition,
|
|
||||||
)
|
|
||||||
{
|
|
||||||
graph.add_relationship(edge);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_diff(args: &Cli, existing_path: &std::path::Path) -> Result<()> {
|
fn run_diff(args: &Cli, existing_path: &std::path::Path) -> Result<()> {
|
||||||
init_tracing(args.verbose);
|
init_tracing(args.verbose);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user