mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-13 07:29:33 +00:00
Discover a Cargo.toml in a subdirectory when none sits at the scan root
introspect_cargo only ever looked at root divided by Cargo dot toml,
with no search, so any repository whose crate lives in a subdirectory
got no crate nodes or dependency edges at all, even though the AST
pass had already parsed every rs file there. The common case is a
Tauri app, whose default scaffold keeps its manifest under its own
frontend adjacent crate folder rather than the project root.
Add a bounded depth search that only runs when the root manifest is
missing, reusing the same noise directory pruning the AST scanner
already applies so a vendored or example manifest nested inside a
dependency folder never wins over the project's own. A workspace
manifest found this way now resolves its own members relative to its
own directory rather than the outer scan root, since those two can
now differ.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 577e51749a)
This commit is contained in:
1 parent
137ead6d56
commit
165a7da4a1
1 file changed
+41
-1
@@ -2,11 +2,13 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
_CONFIDENCE_EXTRACTED = "EXTRACTED"
|
||||
_MAX_DISCOVERY_DEPTH = 2
|
||||
|
||||
|
||||
def _load_toml(path: Path) -> dict[str, Any]:
|
||||
@@ -24,6 +26,37 @@ def _load_toml(path: Path) -> dict[str, Any]:
|
||||
return tomllib.load(manifest)
|
||||
|
||||
|
||||
def _discover_root_manifest(root: Path) -> Path | None:
|
||||
"""Find a Cargo.toml under *root* when none sits directly at the root.
|
||||
|
||||
A bounded-depth search rather than an unbounded walk: a Tauri app's
|
||||
crate lives in src-tauri/, a polyglot repo's in rust/ or crates/, but
|
||||
going arbitrarily deep risks matching a vendored or example manifest
|
||||
nested well inside an unrelated dependency (#3678). Reuses the same
|
||||
noise-directory pruning the AST scanner already applies (venvs, caches,
|
||||
node_modules, build output) so those never get searched into. A
|
||||
genuinely ambiguous layout with more than one candidate at the same
|
||||
depth is resolved by sorted path order rather than guessed at; the
|
||||
caller is free to point --cargo at a subdirectory directly for a repo
|
||||
where that is not the right answer.
|
||||
"""
|
||||
from graphify.detect import _is_noise_dir
|
||||
|
||||
found: list[Path] = []
|
||||
for dirpath, dirnames, filenames in os.walk(root):
|
||||
current = Path(dirpath)
|
||||
depth = len(current.relative_to(root).parts)
|
||||
if depth > 0 and "Cargo.toml" in filenames:
|
||||
found.append(current / "Cargo.toml")
|
||||
if depth >= _MAX_DISCOVERY_DEPTH:
|
||||
dirnames[:] = []
|
||||
continue
|
||||
dirnames[:] = [d for d in dirnames if not _is_noise_dir(d, current)]
|
||||
if not found:
|
||||
return None
|
||||
return sorted(found)[0]
|
||||
|
||||
|
||||
def _member_manifest_paths(root: Path, root_data: dict[str, Any]) -> list[Path]:
|
||||
paths: list[Path] = []
|
||||
if isinstance(root_data.get("package"), dict):
|
||||
@@ -48,9 +81,16 @@ def introspect_cargo(root: str | Path) -> dict[str, Any]:
|
||||
"""Return crate nodes and internal dependency edges from Cargo manifests."""
|
||||
root_path = Path(root).resolve()
|
||||
root_manifest = root_path / "Cargo.toml"
|
||||
if not root_manifest.is_file():
|
||||
discovered = _discover_root_manifest(root_path)
|
||||
if discovered is not None:
|
||||
root_manifest = discovered
|
||||
root_data = _load_toml(root_manifest)
|
||||
|
||||
manifests = _member_manifest_paths(root_path, root_data)
|
||||
# Workspace members are resolved relative to the manifest's OWN
|
||||
# directory, not necessarily root_path (#3678: the manifest can live in
|
||||
# a discovered subdirectory, e.g. src-tauri/).
|
||||
manifests = _member_manifest_paths(root_manifest.parent, root_data)
|
||||
crates: dict[str, tuple[str, Path, dict[str, Any]]] = {}
|
||||
|
||||
for manifest in manifests:
|
||||
|
||||
Reference in new issue
Block a user