refactor: five architectural deepening improvements
Candidate 1 (NormalizedGraph): qualify→resolve→filter is now a single
named operation returning a distinct type; raw CodeGraph cannot call
module_edges/subgraph_by_module — pipeline order enforced at compile time.
Candidate 2 (Use cases): GenerateDiagram, CheckFreshness, DiffDiagram
extracted to application/src/use_cases/; presentation is now a thin CLI
dispatcher (~100 lines less, three fewer local functions).
Candidate 3 (ExtractionContext): shared accumulator for both Rust and
Python extractors replaces parallel Vec<> + 4-arg passing chains.
Candidate 4 (ModuleAssignment): ModuleName::assign() returns
ModuleAssignment { Explicit | Inferred | Unresolved } instead of Option,
callers can distinguish resolution strategies.
Candidate 5 (SplitRenderer): append_cross_module_deps removed from
DiagramRenderer port; replaced by render_for_module() default impl —
port interface now reflects what all renderers actually share.
This commit is contained in:
56
crates/adapters/tree-sitter/src/extraction_context.rs
Normal file
56
crates/adapters/tree-sitter/src/extraction_context.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use archlens_domain::{
|
||||
AnalysisResult, AnalysisWarning, CodeElement, DomainError, FilePath, Relationship,
|
||||
};
|
||||
|
||||
/// Accumulator passed through tree-walking helpers.
|
||||
///
|
||||
/// Bundles the four parallel mutable collections that every language extractor
|
||||
/// maintains, giving helpers a single receiver to push into and eliminating
|
||||
/// 4-parameter passing chains.
|
||||
pub struct ExtractionContext {
|
||||
elements: Vec<CodeElement>,
|
||||
relationships: Vec<Relationship>,
|
||||
warnings: Vec<AnalysisWarning>,
|
||||
/// Names of types defined in the current file — used to filter primitive
|
||||
/// and external types from relationship targets.
|
||||
pub local_types: HashSet<String>,
|
||||
file_path: FilePath,
|
||||
}
|
||||
|
||||
impl ExtractionContext {
|
||||
pub fn new(file_path: FilePath) -> Self {
|
||||
Self {
|
||||
elements: Vec::new(),
|
||||
relationships: Vec::new(),
|
||||
warnings: Vec::new(),
|
||||
local_types: HashSet::new(),
|
||||
file_path,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_element(&mut self, element: CodeElement) {
|
||||
self.local_types.insert(element.name().to_string());
|
||||
self.elements.push(element);
|
||||
}
|
||||
|
||||
pub fn add_relationship(&mut self, rel: Relationship) {
|
||||
self.relationships.push(rel.with_source_file(self.file_path.clone()));
|
||||
}
|
||||
|
||||
pub fn add_warning(&mut self, file_path: FilePath, line: usize, message: &str) {
|
||||
if let Ok(w) = AnalysisWarning::new(file_path, line, message) {
|
||||
self.warnings.push(w);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_path(&self) -> &FilePath {
|
||||
&self.file_path
|
||||
}
|
||||
|
||||
/// Consumes the context and returns the completed `AnalysisResult`.
|
||||
pub fn into_result(self) -> Result<AnalysisResult, DomainError> {
|
||||
Ok(AnalysisResult::new(self.elements, self.relationships, self.warnings))
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
mod extraction_context;
|
||||
mod language_extractor;
|
||||
mod python;
|
||||
mod rust;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use tree_sitter::{Node, Parser};
|
||||
|
||||
use archlens_domain::{
|
||||
AnalysisResult, AnalysisWarning, CodeElement, CodeElementKind, DomainError, FilePath,
|
||||
Relationship, RelationshipKind,
|
||||
AnalysisResult, CodeElement, CodeElementKind, DomainError, FilePath, Relationship,
|
||||
RelationshipKind,
|
||||
};
|
||||
|
||||
use crate::extraction_context::ExtractionContext;
|
||||
use crate::language_extractor::LanguageExtractor;
|
||||
|
||||
pub struct PythonExtractor;
|
||||
@@ -27,40 +26,16 @@ pub fn analyze(source: &str, file_path: &FilePath) -> Result<AnalysisResult, Dom
|
||||
.parse(source, None)
|
||||
.ok_or_else(|| DomainError::AnalysisError("failed to parse".to_string()))?;
|
||||
|
||||
let mut elements = Vec::new();
|
||||
let mut relationships = Vec::new();
|
||||
let mut warnings = Vec::new();
|
||||
let mut type_names: HashSet<String> = HashSet::new();
|
||||
|
||||
let mut ctx = ExtractionContext::new(file_path.clone());
|
||||
let root = tree.root_node();
|
||||
collect_classes(
|
||||
&root,
|
||||
source,
|
||||
file_path,
|
||||
&mut elements,
|
||||
&mut type_names,
|
||||
&mut relationships,
|
||||
&mut warnings,
|
||||
);
|
||||
collect_imports(&root, source, file_path, &mut relationships);
|
||||
|
||||
let relationships = relationships
|
||||
.into_iter()
|
||||
.map(|r| r.with_source_file(file_path.clone()))
|
||||
.collect();
|
||||
collect_classes(&root, source, &mut ctx);
|
||||
collect_imports(&root, source, &mut ctx);
|
||||
|
||||
Ok(AnalysisResult::new(elements, relationships, warnings))
|
||||
ctx.into_result()
|
||||
}
|
||||
|
||||
fn collect_classes(
|
||||
node: &Node,
|
||||
source: &str,
|
||||
file_path: &FilePath,
|
||||
elements: &mut Vec<CodeElement>,
|
||||
type_names: &mut HashSet<String>,
|
||||
relationships: &mut Vec<Relationship>,
|
||||
warnings: &mut Vec<AnalysisWarning>,
|
||||
) {
|
||||
fn collect_classes(node: &Node, source: &str, ctx: &mut ExtractionContext) {
|
||||
let mut cursor = node.walk();
|
||||
for child in node.children(&mut cursor) {
|
||||
if child.kind() != "class_definition" {
|
||||
@@ -79,26 +54,21 @@ fn collect_classes(
|
||||
.map(|body| collect_methods(&body, source))
|
||||
.unwrap_or_default();
|
||||
|
||||
match CodeElement::new(name, CodeElementKind::Class, file_path.clone(), line) {
|
||||
Ok(element) => {
|
||||
type_names.insert(name.to_string());
|
||||
elements.push(element.with_methods(methods));
|
||||
}
|
||||
match CodeElement::new(name, CodeElementKind::Class, ctx.file_path().clone(), line) {
|
||||
Ok(element) => ctx.add_element(element.with_methods(methods)),
|
||||
Err(e) => {
|
||||
if let Ok(w) = AnalysisWarning::new(file_path.clone(), line, &e.to_string()) {
|
||||
warnings.push(w);
|
||||
}
|
||||
ctx.add_warning(ctx.file_path().clone(), line, &e.to_string());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(superclasses) = child.child_by_field_name("superclasses") {
|
||||
collect_inheritance(&superclasses, source, name, type_names, relationships);
|
||||
collect_inheritance(&superclasses, source, name, ctx);
|
||||
}
|
||||
|
||||
if let Some(body) = child.child_by_field_name("body") {
|
||||
collect_typed_fields(&body, source, name, type_names, relationships);
|
||||
collect_constructor_params(&body, source, name, type_names, relationships);
|
||||
collect_typed_fields(&body, source, name, ctx);
|
||||
collect_constructor_params(&body, source, name, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,8 +77,7 @@ fn collect_inheritance(
|
||||
superclasses: &Node,
|
||||
source: &str,
|
||||
class_name: &str,
|
||||
_type_names: &HashSet<String>,
|
||||
relationships: &mut Vec<Relationship>,
|
||||
ctx: &mut ExtractionContext,
|
||||
) {
|
||||
let mut cursor = superclasses.walk();
|
||||
for child in superclasses.children(&mut cursor) {
|
||||
@@ -118,7 +87,7 @@ fn collect_inheritance(
|
||||
&& let Ok(rel) =
|
||||
Relationship::new(class_name, base_name, RelationshipKind::Inheritance)
|
||||
{
|
||||
relationships.push(rel);
|
||||
ctx.add_relationship(rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,16 +197,12 @@ fn is_external_import(module: &str) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn collect_imports(
|
||||
node: &Node,
|
||||
source: &str,
|
||||
file_path: &FilePath,
|
||||
relationships: &mut Vec<Relationship>,
|
||||
) {
|
||||
let file_name = std::path::Path::new(file_path.as_str())
|
||||
fn collect_imports(node: &Node, source: &str, ctx: &mut ExtractionContext) {
|
||||
let file_name = std::path::Path::new(ctx.file_path().as_str())
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("unknown");
|
||||
.unwrap_or("unknown")
|
||||
.to_string();
|
||||
|
||||
let mut cursor = node.walk();
|
||||
for child in node.children(&mut cursor) {
|
||||
@@ -249,9 +214,9 @@ fn collect_imports(
|
||||
let module = &source[name_child.byte_range()];
|
||||
if !is_external_import(module)
|
||||
&& let Ok(rel) =
|
||||
Relationship::new(file_name, module, RelationshipKind::Import)
|
||||
Relationship::new(&file_name, module, RelationshipKind::Import)
|
||||
{
|
||||
relationships.push(rel);
|
||||
ctx.add_relationship(rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,9 +226,9 @@ fn collect_imports(
|
||||
let module = &source[module_node.byte_range()];
|
||||
if !is_external_import(module)
|
||||
&& let Ok(rel) =
|
||||
Relationship::new(file_name, module, RelationshipKind::Import)
|
||||
Relationship::new(&file_name, module, RelationshipKind::Import)
|
||||
{
|
||||
relationships.push(rel);
|
||||
ctx.add_relationship(rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,7 +279,6 @@ fn extract_python_params(params_node: &Node, source: &str) -> String {
|
||||
match param.kind() {
|
||||
"typed_parameter" => {
|
||||
if let Some(type_node) = param.child_by_field_name("type") {
|
||||
// name is the first identifier child (not a named field)
|
||||
let mut inner = param.walk();
|
||||
let name = param
|
||||
.children(&mut inner)
|
||||
@@ -339,13 +303,7 @@ fn extract_python_params(params_node: &Node, source: &str) -> String {
|
||||
parts.join(", ")
|
||||
}
|
||||
|
||||
fn collect_constructor_params(
|
||||
body: &Node,
|
||||
source: &str,
|
||||
class_name: &str,
|
||||
_type_names: &HashSet<String>,
|
||||
relationships: &mut Vec<Relationship>,
|
||||
) {
|
||||
fn collect_constructor_params(body: &Node, source: &str, class_name: &str, ctx: &mut ExtractionContext) {
|
||||
let mut cursor = body.walk();
|
||||
for child in body.children(&mut cursor) {
|
||||
if child.kind() != "function_definition" {
|
||||
@@ -373,30 +331,18 @@ fn collect_constructor_params(
|
||||
&& let Ok(rel) =
|
||||
Relationship::new(class_name, base_type, RelationshipKind::Composition)
|
||||
{
|
||||
relationships.push(rel);
|
||||
ctx.add_relationship(rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_typed_fields(
|
||||
body: &Node,
|
||||
source: &str,
|
||||
class_name: &str,
|
||||
type_names: &HashSet<String>,
|
||||
relationships: &mut Vec<Relationship>,
|
||||
) {
|
||||
collect_typed_fields_recursive(body, source, class_name, type_names, relationships);
|
||||
fn collect_typed_fields(body: &Node, source: &str, class_name: &str, ctx: &mut ExtractionContext) {
|
||||
collect_typed_fields_recursive(body, source, class_name, ctx);
|
||||
}
|
||||
|
||||
fn collect_typed_fields_recursive(
|
||||
node: &Node,
|
||||
source: &str,
|
||||
class_name: &str,
|
||||
_type_names: &HashSet<String>,
|
||||
relationships: &mut Vec<Relationship>,
|
||||
) {
|
||||
fn collect_typed_fields_recursive(node: &Node, source: &str, class_name: &str, ctx: &mut ExtractionContext) {
|
||||
let mut cursor = node.walk();
|
||||
for child in node.children(&mut cursor) {
|
||||
if (child.kind() == "assignment" || child.kind() == "typed_assignment")
|
||||
@@ -410,10 +356,10 @@ fn collect_typed_fields_recursive(
|
||||
&& let Ok(rel) =
|
||||
Relationship::new(class_name, base_type, RelationshipKind::Composition)
|
||||
{
|
||||
relationships.push(rel);
|
||||
ctx.add_relationship(rel);
|
||||
}
|
||||
}
|
||||
|
||||
collect_typed_fields_recursive(&child, source, class_name, _type_names, relationships);
|
||||
collect_typed_fields_recursive(&child, source, class_name, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,17 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use tree_sitter::{Node, Parser};
|
||||
|
||||
const RUST_PRIMITIVES: &[&str] = &[
|
||||
"bool",
|
||||
"char",
|
||||
"str",
|
||||
"String",
|
||||
"u8",
|
||||
"u16",
|
||||
"u32",
|
||||
"u64",
|
||||
"u128",
|
||||
"usize",
|
||||
"i8",
|
||||
"i16",
|
||||
"i32",
|
||||
"i64",
|
||||
"i128",
|
||||
"isize",
|
||||
"f32",
|
||||
"f64",
|
||||
"Vec",
|
||||
"Option",
|
||||
"Result",
|
||||
"Box",
|
||||
"Rc",
|
||||
"Arc",
|
||||
"HashMap",
|
||||
"HashSet",
|
||||
"BTreeMap",
|
||||
"BTreeSet",
|
||||
"PhantomData",
|
||||
"Pin",
|
||||
"Cow",
|
||||
"Self",
|
||||
"bool", "char", "str", "String", "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16",
|
||||
"i32", "i64", "i128", "isize", "f32", "f64", "Vec", "Option", "Result", "Box", "Rc", "Arc",
|
||||
"HashMap", "HashSet", "BTreeMap", "BTreeSet", "PhantomData", "Pin", "Cow", "Self",
|
||||
];
|
||||
|
||||
use archlens_domain::{
|
||||
AnalysisResult, AnalysisWarning, CodeElement, CodeElementKind, DomainError, FilePath,
|
||||
Relationship, RelationshipKind, Visibility,
|
||||
AnalysisResult, CodeElement, CodeElementKind, DomainError, FilePath, Relationship,
|
||||
RelationshipKind, Visibility,
|
||||
};
|
||||
|
||||
use crate::extraction_context::ExtractionContext;
|
||||
use crate::language_extractor::LanguageExtractor;
|
||||
|
||||
pub struct RustExtractor;
|
||||
@@ -62,46 +32,18 @@ pub fn analyze(source: &str, file_path: &FilePath) -> Result<AnalysisResult, Dom
|
||||
.parse(source, None)
|
||||
.ok_or_else(|| DomainError::AnalysisError("failed to parse".to_string()))?;
|
||||
|
||||
let mut elements = Vec::new();
|
||||
let mut relationships = Vec::new();
|
||||
let mut warnings = Vec::new();
|
||||
let mut type_names: HashSet<String> = HashSet::new();
|
||||
|
||||
let mut ctx = ExtractionContext::new(file_path.clone());
|
||||
let root = tree.root_node();
|
||||
collect_types(
|
||||
&root,
|
||||
source,
|
||||
file_path,
|
||||
&mut elements,
|
||||
&mut type_names,
|
||||
&mut warnings,
|
||||
);
|
||||
collect_relationships(
|
||||
&root,
|
||||
source,
|
||||
&type_names,
|
||||
&mut relationships,
|
||||
&mut warnings,
|
||||
);
|
||||
collect_mod_declarations(&root, source, file_path, &mut relationships);
|
||||
collect_use_imports(&root, source, file_path, &mut relationships);
|
||||
|
||||
let relationships = relationships
|
||||
.into_iter()
|
||||
.map(|r| r.with_source_file(file_path.clone()))
|
||||
.collect();
|
||||
collect_types(&root, source, &mut ctx);
|
||||
collect_relationships(&root, source, &mut ctx);
|
||||
collect_mod_declarations(&root, source, &mut ctx);
|
||||
collect_use_imports(&root, source, &mut ctx);
|
||||
|
||||
Ok(AnalysisResult::new(elements, relationships, warnings))
|
||||
ctx.into_result()
|
||||
}
|
||||
|
||||
fn collect_types(
|
||||
node: &Node,
|
||||
source: &str,
|
||||
file_path: &FilePath,
|
||||
elements: &mut Vec<CodeElement>,
|
||||
type_names: &mut HashSet<String>,
|
||||
warnings: &mut Vec<AnalysisWarning>,
|
||||
) {
|
||||
fn collect_types(node: &Node, source: &str, ctx: &mut ExtractionContext) {
|
||||
let mut cursor = node.walk();
|
||||
for child in node.children(&mut cursor) {
|
||||
let (kind, name_field) = match child.kind() {
|
||||
@@ -116,52 +58,34 @@ fn collect_types(
|
||||
let line = child.start_position().row + 1;
|
||||
let visibility = detect_visibility(&child, source);
|
||||
|
||||
match CodeElement::new(name, kind, file_path.clone(), line) {
|
||||
match CodeElement::new(name, kind, ctx.file_path().clone(), line) {
|
||||
Ok(element) => {
|
||||
let fields = extract_fields(&child, source);
|
||||
let methods = extract_methods(node, source, name);
|
||||
let element = element
|
||||
.with_visibility(visibility)
|
||||
.with_fields(fields)
|
||||
.with_methods(methods);
|
||||
type_names.insert(name.to_string());
|
||||
elements.push(element);
|
||||
}
|
||||
Err(e) => {
|
||||
if let Ok(w) = AnalysisWarning::new(file_path.clone(), line, &e.to_string()) {
|
||||
warnings.push(w);
|
||||
}
|
||||
ctx.add_element(
|
||||
element
|
||||
.with_visibility(visibility)
|
||||
.with_fields(fields)
|
||||
.with_methods(methods),
|
||||
);
|
||||
}
|
||||
Err(e) => ctx.add_warning(ctx.file_path().clone(), line, &e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_relationships(
|
||||
node: &Node,
|
||||
source: &str,
|
||||
type_names: &HashSet<String>,
|
||||
relationships: &mut Vec<Relationship>,
|
||||
warnings: &mut Vec<AnalysisWarning>,
|
||||
) {
|
||||
fn collect_relationships(node: &Node, source: &str, ctx: &mut ExtractionContext) {
|
||||
let mut cursor = node.walk();
|
||||
for child in node.children(&mut cursor) {
|
||||
match child.kind() {
|
||||
"struct_item" => {
|
||||
if let Some(name_node) = child.child_by_field_name("name") {
|
||||
let struct_name = source[name_node.byte_range()].to_string();
|
||||
collect_field_compositions(
|
||||
&child,
|
||||
source,
|
||||
&struct_name,
|
||||
type_names,
|
||||
relationships,
|
||||
);
|
||||
collect_field_compositions(&child, source, &struct_name, ctx);
|
||||
}
|
||||
}
|
||||
"impl_item" => {
|
||||
collect_trait_impl(&child, source, type_names, relationships, warnings);
|
||||
}
|
||||
"impl_item" => collect_trait_impl(&child, source, ctx),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -171,8 +95,7 @@ fn collect_field_compositions(
|
||||
struct_node: &Node,
|
||||
source: &str,
|
||||
struct_name: &str,
|
||||
_type_names: &HashSet<String>,
|
||||
relationships: &mut Vec<Relationship>,
|
||||
ctx: &mut ExtractionContext,
|
||||
) {
|
||||
if let Some(body) = struct_node.child_by_field_name("body") {
|
||||
let mut cursor = body.walk();
|
||||
@@ -187,20 +110,14 @@ fn collect_field_compositions(
|
||||
&& let Ok(rel) =
|
||||
Relationship::new(struct_name, &type_text, RelationshipKind::Composition)
|
||||
{
|
||||
relationships.push(rel);
|
||||
ctx.add_relationship(rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_trait_impl(
|
||||
impl_node: &Node,
|
||||
source: &str,
|
||||
_type_names: &HashSet<String>,
|
||||
relationships: &mut Vec<Relationship>,
|
||||
_warnings: &mut Vec<AnalysisWarning>,
|
||||
) {
|
||||
fn collect_trait_impl(impl_node: &Node, source: &str, ctx: &mut ExtractionContext) {
|
||||
let trait_node = impl_node.child_by_field_name("trait");
|
||||
let type_node = impl_node.child_by_field_name("type");
|
||||
|
||||
@@ -215,7 +132,7 @@ fn collect_trait_impl(
|
||||
&& let Ok(rel) =
|
||||
Relationship::new(&type_name, &trait_name, RelationshipKind::Inheritance)
|
||||
{
|
||||
relationships.push(rel);
|
||||
ctx.add_relationship(rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,12 +143,8 @@ fn extract_fields(node: &Node, source: &str) -> Vec<String> {
|
||||
let mut cursor = body.walk();
|
||||
for child in body.children(&mut cursor) {
|
||||
if child.kind() == "field_declaration" {
|
||||
let name = child
|
||||
.child_by_field_name("name")
|
||||
.map(|n| &source[n.byte_range()]);
|
||||
let ty = child
|
||||
.child_by_field_name("type")
|
||||
.map(|n| extract_base_type(&n, source));
|
||||
let name = child.child_by_field_name("name").map(|n| &source[n.byte_range()]);
|
||||
let ty = child.child_by_field_name("type").map(|n| extract_base_type(&n, source));
|
||||
if let (Some(name), Some(ty)) = (name, ty) {
|
||||
fields.push(format!("{name}: {ty}"));
|
||||
}
|
||||
@@ -245,19 +158,15 @@ fn extract_methods(root: &Node, source: &str, type_name: &str) -> Vec<String> {
|
||||
let mut methods = Vec::new();
|
||||
let mut cursor = root.walk();
|
||||
for child in root.children(&mut cursor) {
|
||||
if child.kind() != "impl_item" {
|
||||
if child.kind() != "impl_item" || child.child_by_field_name("trait").is_some() {
|
||||
continue;
|
||||
}
|
||||
if child.child_by_field_name("trait").is_some() {
|
||||
if child
|
||||
.child_by_field_name("type")
|
||||
.is_some_and(|tn| extract_base_type(&tn, source) != type_name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let type_node = child.child_by_field_name("type");
|
||||
if let Some(tn) = type_node {
|
||||
let impl_name = extract_base_type(&tn, source);
|
||||
if impl_name != type_name {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let Some(body) = child.child_by_field_name("body") {
|
||||
let mut body_cursor = body.walk();
|
||||
for item in body.children(&mut body_cursor) {
|
||||
@@ -265,11 +174,7 @@ fn extract_methods(root: &Node, source: &str, type_name: &str) -> Vec<String> {
|
||||
&& let Some(name_node) = item.child_by_field_name("name")
|
||||
{
|
||||
let fn_name = &source[name_node.byte_range()];
|
||||
let vis = if detect_visibility(&item, source) == Visibility::Public {
|
||||
"+"
|
||||
} else {
|
||||
"-"
|
||||
};
|
||||
let vis = if detect_visibility(&item, source) == Visibility::Public { "+" } else { "-" };
|
||||
let params = extract_fn_params(&item, source);
|
||||
let ret = extract_fn_return(&item, source);
|
||||
let sig = if ret.is_empty() {
|
||||
@@ -317,45 +222,34 @@ fn extract_fn_return(fn_item: &Node, source: &str) -> String {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn collect_mod_declarations(
|
||||
node: &Node,
|
||||
source: &str,
|
||||
file_path: &FilePath,
|
||||
relationships: &mut Vec<Relationship>,
|
||||
) {
|
||||
let file_name = std::path::Path::new(file_path.as_str())
|
||||
fn collect_mod_declarations(node: &Node, source: &str, ctx: &mut ExtractionContext) {
|
||||
let file_name = std::path::Path::new(ctx.file_path().as_str())
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("unknown");
|
||||
.unwrap_or("unknown")
|
||||
.to_string();
|
||||
|
||||
let mut cursor = node.walk();
|
||||
for child in node.children(&mut cursor) {
|
||||
if child.kind() != "mod_item" {
|
||||
continue;
|
||||
}
|
||||
if child.child_by_field_name("body").is_some() {
|
||||
if child.kind() != "mod_item" || child.child_by_field_name("body").is_some() {
|
||||
continue;
|
||||
}
|
||||
if let Some(name_node) = child.child_by_field_name("name") {
|
||||
let mod_name = &source[name_node.byte_range()];
|
||||
let target = format!("crate::{mod_name}");
|
||||
if let Ok(rel) = Relationship::new(file_name, &target, RelationshipKind::Import) {
|
||||
relationships.push(rel);
|
||||
if let Ok(rel) = Relationship::new(&file_name, &target, RelationshipKind::Import) {
|
||||
ctx.add_relationship(rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_use_imports(
|
||||
node: &Node,
|
||||
source: &str,
|
||||
file_path: &FilePath,
|
||||
relationships: &mut Vec<Relationship>,
|
||||
) {
|
||||
let file_name = std::path::Path::new(file_path.as_str())
|
||||
fn collect_use_imports(node: &Node, source: &str, ctx: &mut ExtractionContext) {
|
||||
let file_name = std::path::Path::new(ctx.file_path().as_str())
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("unknown");
|
||||
.unwrap_or("unknown")
|
||||
.to_string();
|
||||
|
||||
let mut cursor = node.walk();
|
||||
for child in node.children(&mut cursor) {
|
||||
@@ -364,16 +258,12 @@ fn collect_use_imports(
|
||||
}
|
||||
if let Some(arg) = child.child_by_field_name("argument") {
|
||||
let text = &source[arg.byte_range()];
|
||||
let path = text
|
||||
.split('{')
|
||||
.next()
|
||||
.unwrap_or(text)
|
||||
.trim_end_matches("::");
|
||||
let path = text.split('{').next().unwrap_or(text).trim_end_matches("::");
|
||||
|
||||
if (path.starts_with("crate::") || path.starts_with("super::"))
|
||||
&& let Ok(rel) = Relationship::new(file_name, path, RelationshipKind::Import)
|
||||
&& let Ok(rel) = Relationship::new(&file_name, path, RelationshipKind::Import)
|
||||
{
|
||||
relationships.push(rel);
|
||||
ctx.add_relationship(rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user