cg-code-graph 0.10.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. cg_code_graph-0.10.1.dist-info/METADATA +678 -0
  2. cg_code_graph-0.10.1.dist-info/RECORD +174 -0
  3. cg_code_graph-0.10.1.dist-info/WHEEL +5 -0
  4. cg_code_graph-0.10.1.dist-info/entry_points.txt +3 -0
  5. cg_code_graph-0.10.1.dist-info/licenses/LICENSE +21 -0
  6. cg_code_graph-0.10.1.dist-info/top_level.txt +1 -0
  7. codegraph/__init__.py +2 -0
  8. codegraph/aitools.py +129 -0
  9. codegraph/apps.py +76 -0
  10. codegraph/blindspots.py +428 -0
  11. codegraph/bridges.py +1701 -0
  12. codegraph/cli.py +725 -0
  13. codegraph/concepts.py +362 -0
  14. codegraph/config.py +559 -0
  15. codegraph/core/__init__.py +0 -0
  16. codegraph/core/cache.py +375 -0
  17. codegraph/core/detect.py +80 -0
  18. codegraph/core/extractors.py +187 -0
  19. codegraph/core/fsutil.py +61 -0
  20. codegraph/core/generated.py +575 -0
  21. codegraph/core/model.py +174 -0
  22. codegraph/core/paths.py +175 -0
  23. codegraph/core/plugin.py +160 -0
  24. codegraph/core/store.py +80 -0
  25. codegraph/core/syntax_errors.py +132 -0
  26. codegraph/coverage.py +928 -0
  27. codegraph/doctor.py +453 -0
  28. codegraph/external.py +613 -0
  29. codegraph/indexer.py +336 -0
  30. codegraph/link.py +434 -0
  31. codegraph/lint_async.py +524 -0
  32. codegraph/mcp_server.py +1303 -0
  33. codegraph/parity.py +473 -0
  34. codegraph/parity_structure.py +307 -0
  35. codegraph/payload.py +321 -0
  36. codegraph/plans.py +1285 -0
  37. codegraph/platform_scan.py +643 -0
  38. codegraph/platforms.py +1369 -0
  39. codegraph/plugins/__init__.py +0 -0
  40. codegraph/plugins/cfamily/__init__.py +0 -0
  41. codegraph/plugins/cfamily/plugin.py +930 -0
  42. codegraph/plugins/cfamily/syntax.py +881 -0
  43. codegraph/plugins/dart/__init__.py +0 -0
  44. codegraph/plugins/dart/bridges.py +345 -0
  45. codegraph/plugins/dart/extractor/bin/extract.dart +717 -0
  46. codegraph/plugins/dart/extractor/pubspec.lock +149 -0
  47. codegraph/plugins/dart/extractor/pubspec.yaml +7 -0
  48. codegraph/plugins/dart/http.py +904 -0
  49. codegraph/plugins/dart/models.py +308 -0
  50. codegraph/plugins/dart/plugin.py +625 -0
  51. codegraph/plugins/dart/program.py +907 -0
  52. codegraph/plugins/django/__init__.py +0 -0
  53. codegraph/plugins/django/extras.py +378 -0
  54. codegraph/plugins/django/models.py +508 -0
  55. codegraph/plugins/django/plugin.py +728 -0
  56. codegraph/plugins/django/schemas.py +339 -0
  57. codegraph/plugins/django/shapes.py +216 -0
  58. codegraph/plugins/django/urls.py +603 -0
  59. codegraph/plugins/express/__init__.py +0 -0
  60. codegraph/plugins/express/plugin.py +428 -0
  61. codegraph/plugins/flutter/__init__.py +0 -0
  62. codegraph/plugins/flutter/plugin.py +538 -0
  63. codegraph/plugins/kotlin/__init__.py +0 -0
  64. codegraph/plugins/kotlin/exact.py +457 -0
  65. codegraph/plugins/kotlin/plugin.py +1961 -0
  66. codegraph/plugins/kotlin/reparse.py +234 -0
  67. codegraph/plugins/laravel/__init__.py +0 -0
  68. codegraph/plugins/laravel/broadcast.py +351 -0
  69. codegraph/plugins/laravel/plugin.py +863 -0
  70. codegraph/plugins/laravel/tests.py +262 -0
  71. codegraph/plugins/laravel/values.py +728 -0
  72. codegraph/plugins/native/__init__.py +0 -0
  73. codegraph/plugins/native/gates.py +286 -0
  74. codegraph/plugins/native/runner.py +183 -0
  75. codegraph/plugins/native/scipread.py +194 -0
  76. codegraph/plugins/native/ts.py +54 -0
  77. codegraph/plugins/nest/__init__.py +0 -0
  78. codegraph/plugins/nest/plugin.py +654 -0
  79. codegraph/plugins/nextjs/__init__.py +0 -0
  80. codegraph/plugins/nextjs/plugin.py +336 -0
  81. codegraph/plugins/nuxt/__init__.py +0 -0
  82. codegraph/plugins/nuxt/plugin.py +308 -0
  83. codegraph/plugins/php/__init__.py +0 -0
  84. codegraph/plugins/php/extractor/composer.json +5 -0
  85. codegraph/plugins/php/extractor/composer.lock +76 -0
  86. codegraph/plugins/php/extractor/extract.php +743 -0
  87. codegraph/plugins/php/gating.py +573 -0
  88. codegraph/plugins/php/plugin.py +668 -0
  89. codegraph/plugins/php/strings.py +197 -0
  90. codegraph/plugins/python/__init__.py +0 -0
  91. codegraph/plugins/python/aitools.py +664 -0
  92. codegraph/plugins/python/external.py +245 -0
  93. codegraph/plugins/python/fields.py +107 -0
  94. codegraph/plugins/python/plugin.py +1733 -0
  95. codegraph/plugins/python/refs.py +485 -0
  96. codegraph/plugins/python/roots.py +412 -0
  97. codegraph/plugins/python/socketio.py +210 -0
  98. codegraph/plugins/python/subproc.py +864 -0
  99. codegraph/plugins/python/tests.py +1040 -0
  100. codegraph/plugins/python/values.py +179 -0
  101. codegraph/plugins/pyweb/__init__.py +0 -0
  102. codegraph/plugins/pyweb/plugin.py +1334 -0
  103. codegraph/plugins/pyweb/values.py +68 -0
  104. codegraph/plugins/rust/__init__.py +0 -0
  105. codegraph/plugins/rust/cargo.py +226 -0
  106. codegraph/plugins/rust/plugin.py +980 -0
  107. codegraph/plugins/rust/syntax.py +678 -0
  108. codegraph/plugins/scip/__init__.py +0 -0
  109. codegraph/plugins/scip/importer.py +129 -0
  110. codegraph/plugins/scip/scip.proto +962 -0
  111. codegraph/plugins/scip/scip_pb2.py +97 -0
  112. codegraph/plugins/stubs/__init__.py +0 -0
  113. codegraph/plugins/stubs/plugins.py +38 -0
  114. codegraph/plugins/swift/__init__.py +0 -0
  115. codegraph/plugins/swift/baseurl.py +109 -0
  116. codegraph/plugins/swift/exact.py +415 -0
  117. codegraph/plugins/swift/indexstore.py +209 -0
  118. codegraph/plugins/swift/packages.py +174 -0
  119. codegraph/plugins/swift/plugin.py +2890 -0
  120. codegraph/plugins/ts/__init__.py +0 -0
  121. codegraph/plugins/ts/baseurl.py +185 -0
  122. codegraph/plugins/ts/extractor/extract.mjs +2652 -0
  123. codegraph/plugins/ts/extractor/fw.mjs +685 -0
  124. codegraph/plugins/ts/extractor/package-lock.json +205 -0
  125. codegraph/plugins/ts/extractor/package.json +9 -0
  126. codegraph/plugins/ts/plugin.py +480 -0
  127. codegraph/plugins/tsweb/__init__.py +0 -0
  128. codegraph/plugins/tsweb/common.py +290 -0
  129. codegraph/plugins/tsweb/data.py +276 -0
  130. codegraph/presets/__init__.py +146 -0
  131. codegraph/presets/c_cpp.yaml +9 -0
  132. codegraph/presets/common.yaml +66 -0
  133. codegraph/presets/dart.yaml +9 -0
  134. codegraph/presets/django-ninja.yaml +15 -0
  135. codegraph/presets/django.yaml +25 -0
  136. codegraph/presets/djangorestframework.yaml +17 -0
  137. codegraph/presets/express.yaml +17 -0
  138. codegraph/presets/kotlin.yaml +11 -0
  139. codegraph/presets/laravel.yaml +40 -0
  140. codegraph/presets/nest.yaml +11 -0
  141. codegraph/presets/nextjs.yaml +15 -0
  142. codegraph/presets/nuxt.yaml +9 -0
  143. codegraph/presets/php.yaml +5 -0
  144. codegraph/presets/python.yaml +10 -0
  145. codegraph/presets/rust.yaml +5 -0
  146. codegraph/presets/swift.yaml +10 -0
  147. codegraph/presets/typescript.yaml +13 -0
  148. codegraph/process_runs.py +328 -0
  149. codegraph/protocols/__init__.py +299 -0
  150. codegraph/protocols/builtin.py +67 -0
  151. codegraph/protocols/matchers.py +144 -0
  152. codegraph/protocols/view.py +334 -0
  153. codegraph/query.py +2089 -0
  154. codegraph/realtime.py +260 -0
  155. codegraph/roundtrip.py +346 -0
  156. codegraph/routes.py +442 -0
  157. codegraph/starters.py +218 -0
  158. codegraph/tests_index.py +117 -0
  159. codegraph/viz/__init__.py +0 -0
  160. codegraph/viz/graph.py +369 -0
  161. codegraph/viz/server.py +198 -0
  162. codegraph/viz/static/app.css +148 -0
  163. codegraph/viz/static/app.js +1082 -0
  164. codegraph/viz/static/index.html +81 -0
  165. codegraph/viz/static/layered.js +237 -0
  166. codegraph/viz/static/vendor/VERSIONS.txt +4 -0
  167. codegraph/viz/static/vendor/cose-base.js +3214 -0
  168. codegraph/viz/static/vendor/cytoscape-fcose.js +1549 -0
  169. codegraph/viz/static/vendor/cytoscape.min.js +31 -0
  170. codegraph/viz/static/vendor/layout-base.js +5230 -0
  171. codegraph/viz/tools/package-lock.json +303 -0
  172. codegraph/viz/tools/package.json +7 -0
  173. codegraph/viz/tools/shoot.mjs +165 -0
  174. codegraph/xcode.py +251 -0
@@ -0,0 +1,68 @@
1
+ """String values of Python expressions, evaluated statically (no code runs): literals, f-strings, `+` / `%` /
2
+ `.format`, module and imported constants, class attribute defaults read through an instance (`settings.API_V1_STR`
3
+ with `settings = Settings()` and `API_V1_STR: str = "/api/v1"`). Used for route prefixes / paths and test URLs."""
4
+ from __future__ import annotations
5
+
6
+ import ast
7
+
8
+ UNKNOWN = "{?}"
9
+
10
+
11
+ def str_value(prog, e, ctx, depth: int = 0) -> str | None:
12
+ """The string value of `e`, or None when it cannot be determined. f-string parts that cannot be evaluated
13
+ become `{name}` placeholders (a path parameter in a URL)."""
14
+ from ..python.plugin import Ctx
15
+ if e is None or depth > 8:
16
+ return None
17
+ if isinstance(e, ast.Constant):
18
+ return e.value if isinstance(e.value, str) else None
19
+ if isinstance(e, ast.JoinedStr):
20
+ out = ""
21
+ for v in e.values:
22
+ if isinstance(v, ast.Constant):
23
+ out += str(v.value)
24
+ continue
25
+ s = str_value(prog, v.value, ctx, depth + 1)
26
+ out += s if s is not None else "{" + _ph(v.value) + "}"
27
+ return out
28
+ if isinstance(e, ast.BinOp) and isinstance(e.op, ast.Add):
29
+ a, b = str_value(prog, e.left, ctx, depth + 1), str_value(prog, e.right, ctx, depth + 1)
30
+ return a + b if a is not None and b is not None else None
31
+ if isinstance(e, ast.Name):
32
+ if ctx.func is not None:
33
+ lv = prog.local_vars(ctx).get(e.id)
34
+ if lv is not None:
35
+ vals = [v for v, _a, kind in lv if kind == "assign" and v is not None]
36
+ return str_value(prog, vals[0], ctx, depth + 1) if len(vals) == 1 else None
37
+ r = prog.resolve_name(ctx.mod, e.id)
38
+ if r and r[0] == "var":
39
+ vals = [v for v, _ln, _ann in r[1].vars.get(r[2], ()) if v is not None]
40
+ return str_value(prog, vals[0], Ctx(r[1], None, None), depth + 1) if len(vals) == 1 else None
41
+ return None
42
+ if isinstance(e, ast.Attribute):
43
+ try:
44
+ t = prog.infer(e.value, ctx)
45
+ except RecursionError:
46
+ return None
47
+ if t and t[0] == "var":
48
+ t = prog.var_type(t[1], t[2]) if hasattr(prog, "var_type") else None
49
+ if t and t[0] == "mod":
50
+ m = t[1]
51
+ vals = [v for v, _ln, _ann in m.vars.get(e.attr, ()) if v is not None]
52
+ return str_value(prog, vals[0], Ctx(m, None, None), depth + 1) if len(vals) == 1 else None
53
+ if t and t[0] in ("inst", "type"):
54
+ c = t[1]
55
+ for k in [c] + [b[1] for b in prog.mro(c) if b[0] == "type"]:
56
+ if e.attr in k.attrs:
57
+ val = k.attrs[e.attr][0]
58
+ return str_value(prog, val, Ctx(k.module, None, k), depth + 1) if val is not None else None
59
+ return None
60
+ return None
61
+
62
+
63
+ def _ph(e) -> str:
64
+ if isinstance(e, ast.Name):
65
+ return e.id
66
+ if isinstance(e, ast.Attribute):
67
+ return e.attr
68
+ return "p"
File without changes
@@ -0,0 +1,226 @@
1
+ """Cargo workspace discovery: packages, targets, features (no build, no code execution).
2
+
3
+ Uses `cargo metadata --no-deps --offline` when cargo is installed (handles workspace inheritance, globs,
4
+ auto-discovered targets); falls back to reading Cargo.toml files with tomllib and Cargo's auto-discovery rules.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import glob
9
+ import json
10
+ import os
11
+ import shutil
12
+ import subprocess
13
+ import tomllib
14
+ from dataclasses import dataclass, field
15
+ from pathlib import Path
16
+
17
+ LIB_KINDS = {"lib", "rlib", "dylib", "cdylib", "staticlib", "proc-macro"}
18
+ from ... import presets
19
+
20
+ # Cargo build output, vendored sources, Cargo home (codegraph/presets/rust.yaml; .cg.yaml skip_dirs adjusts them)
21
+ SKIP_DIRS = presets.skip_dirs("rust")
22
+
23
+
24
+ @dataclass
25
+ class Target:
26
+ kind: str # lib | bin | test | bench | example | build
27
+ name: str
28
+ src: str # repo-relative path of the crate root file
29
+ crate: str # crate path used in node ids, e.g. kv_core, kv, kv_core[test:roundtrip]
30
+ crate_types: list = field(default_factory=list)
31
+ required_features: list = field(default_factory=list)
32
+
33
+
34
+ @dataclass
35
+ class Package:
36
+ name: str
37
+ dir: str # repo-relative directory ('' = root)
38
+ manifest: str
39
+ version: str = ""
40
+ features: dict = field(default_factory=dict)
41
+ targets: list = field(default_factory=list)
42
+ deps: list = field(default_factory=list) # dependency package names (workspace-internal ones are linked)
43
+
44
+ @property
45
+ def crate_name(self) -> str:
46
+ return self.name.replace("-", "_")
47
+
48
+
49
+ def _norm(s: str) -> str:
50
+ return s.replace("-", "_")
51
+
52
+
53
+ def _rel(root: Path, p: str | Path) -> str:
54
+ try:
55
+ return Path(p).resolve().relative_to(root).as_posix()
56
+ except ValueError:
57
+ return Path(p).as_posix()
58
+
59
+
60
+ def _crate_for(pkg: Package, kind: str, name: str, lib_names: set) -> str:
61
+ pc = pkg.crate_name
62
+ if kind == "lib":
63
+ return _norm(name)
64
+ if kind == "bin":
65
+ n = _norm(name)
66
+ return f"{n}[bin]" if n in lib_names else n
67
+ if kind == "build":
68
+ return f"{pc}[build]"
69
+ return f"{pc}[{kind}:{name}]"
70
+
71
+
72
+ def _kind_of(kinds: list[str]) -> str:
73
+ k = kinds[0] if kinds else "lib"
74
+ if k in LIB_KINDS or any(x in LIB_KINDS for x in kinds):
75
+ return "lib"
76
+ return {"custom-build": "build"}.get(k, k)
77
+
78
+
79
+ def from_metadata(root: Path) -> list[Package] | None:
80
+ cargo = os.environ.get("CODEGRAPH_CARGO") or shutil.which("cargo") or str(Path.home() / ".cargo/bin/cargo")
81
+ if not Path(cargo).exists() and not shutil.which(cargo):
82
+ return None
83
+ try:
84
+ r = subprocess.run([cargo, "metadata", "--no-deps", "--format-version", "1", "--offline"], cwd=root,
85
+ capture_output=True, text=True, timeout=180)
86
+ except (OSError, subprocess.TimeoutExpired):
87
+ return None
88
+ if r.returncode != 0:
89
+ return None
90
+ md = json.loads(r.stdout)
91
+ members = set(md.get("workspace_members") or [])
92
+ pkgs = []
93
+ for p in md["packages"]:
94
+ if members and p["id"] not in members:
95
+ continue
96
+ pdir = Path(p["manifest_path"]).parent
97
+ pkg = Package(p["name"], _rel(root, pdir) if pdir != root else "", _rel(root, p["manifest_path"]), p.get("version", ""),
98
+ {k: list(v) for k, v in (p.get("features") or {}).items()},
99
+ deps=[d["name"] for d in p.get("dependencies") or []])
100
+ lib_names = {_norm(t["name"]) for t in p["targets"] if _kind_of(t["kind"]) == "lib"}
101
+ for t in p["targets"]:
102
+ k = _kind_of(t["kind"])
103
+ pkg.targets.append(Target(k, t["name"], _rel(root, t["src_path"]), _crate_for(pkg, k, t["name"], lib_names),
104
+ list(t.get("crate_types") or []), list(t.get("required-features") or [])))
105
+ pkgs.append(pkg)
106
+ return pkgs
107
+
108
+
109
+ def _members(root: Path, ws: dict) -> list[Path]:
110
+ out = []
111
+ excl = {(root / e).resolve() for e in ws.get("exclude", [])}
112
+ for m in ws.get("members", []):
113
+ for d in sorted(glob.glob(str(root / m))):
114
+ dp = Path(d).resolve()
115
+ if (dp / "Cargo.toml").exists() and dp not in excl:
116
+ out.append(dp)
117
+ return out
118
+
119
+
120
+ def _inherit(v, ws_pkg: dict, key: str):
121
+ if isinstance(v, dict) and v.get("workspace"):
122
+ return ws_pkg.get(key)
123
+ return v
124
+
125
+
126
+ def from_toml(root: Path) -> list[Package]:
127
+ """Fallback without cargo: Cargo.toml + auto-discovery (src/lib.rs, src/main.rs, src/bin/*, tests/, benches/,
128
+ examples/, build.rs)."""
129
+ try:
130
+ top = tomllib.loads((root / "Cargo.toml").read_text())
131
+ except Exception:
132
+ return []
133
+ ws = top.get("workspace") or {}
134
+ ws_pkg = ws.get("package") or {}
135
+ dirs = []
136
+ if "package" in top:
137
+ dirs.append(root)
138
+ dirs += [d for d in _members(root, ws) if d != root]
139
+ return _packages(root, dirs, ws_pkg)
140
+
141
+
142
+ def tauri_crates(root: Path, depth: int = 4) -> list[Path]:
143
+ """Without a root Cargo.toml: the Rust core of Tauri apps, <app>/src-tauri/Cargo.toml (a JS project root)."""
144
+ out = []
145
+ for dp, dn, fn in os.walk(root):
146
+ d = Path(dp)
147
+ rel = d.relative_to(root)
148
+ dn[:] = sorted(x for x in dn if not x.startswith(".") and x not in ("node_modules", "target", "dist", "build"))
149
+ if d.name == "src-tauri" and "Cargo.toml" in fn:
150
+ out.append(d)
151
+ dn[:] = []
152
+ if len(rel.parts) >= depth:
153
+ dn[:] = []
154
+ return out
155
+
156
+
157
+ def _packages(root: Path, dirs: list[Path], ws_pkg: dict) -> list[Package]:
158
+ pkgs = []
159
+ for d in dirs:
160
+ try:
161
+ t = tomllib.loads((d / "Cargo.toml").read_text())
162
+ except Exception:
163
+ continue
164
+ p = t.get("package") or {}
165
+ name = p.get("name") or d.name
166
+ pkg = Package(name, _rel(root, d) if d != root else "", _rel(root, d / "Cargo.toml"),
167
+ str(_inherit(p.get("version", ""), ws_pkg, "version") or ""),
168
+ {k: list(v) for k, v in (t.get("features") or {}).items()},
169
+ deps=list((t.get("dependencies") or {}).keys()))
170
+ tl = []
171
+ lib = t.get("lib") or {}
172
+ lib_path = lib.get("path") or ("src/lib.rs" if (d / "src/lib.rs").exists() else None)
173
+ if lib_path:
174
+ tl.append(("lib", lib.get("name") or _norm(name), lib_path, lib.get("crate-type") or ["lib"], []))
175
+ seen_bins = set()
176
+ for b in t.get("bin") or []:
177
+ path = b.get("path") or (f"src/bin/{b['name']}.rs" if (d / f"src/bin/{b['name']}.rs").exists() else
178
+ f"src/bin/{b['name']}/main.rs" if (d / f"src/bin/{b['name']}/main.rs").exists() else "src/main.rs")
179
+ tl.append(("bin", b["name"], path, ["bin"], b.get("required-features") or []))
180
+ seen_bins.add(path)
181
+ if p.get("autobins", True):
182
+ if (d / "src/main.rs").exists() and "src/main.rs" not in seen_bins:
183
+ tl.append(("bin", name, "src/main.rs", ["bin"], []))
184
+ for f in sorted((d / "src/bin").glob("*.rs")) if (d / "src/bin").is_dir() else []:
185
+ if f"src/bin/{f.name}" not in seen_bins:
186
+ tl.append(("bin", f.stem, f"src/bin/{f.name}", ["bin"], []))
187
+ for f in sorted((d / "src/bin").glob("*/main.rs")) if (d / "src/bin").is_dir() else []:
188
+ rp = f.relative_to(d).as_posix()
189
+ if rp not in seen_bins:
190
+ tl.append(("bin", f.parent.name, rp, ["bin"], []))
191
+ for kind, sect, auto in (("test", "test", "tests"), ("bench", "bench", "benches"), ("example", "example", "examples")):
192
+ listed = set()
193
+ for x in t.get(sect) or []:
194
+ path = x.get("path") or f"{auto}/{x['name']}.rs"
195
+ tl.append((kind, x["name"], path, ["bin"], x.get("required-features") or []))
196
+ listed.add(path)
197
+ if p.get(f"auto{auto}", True) and (d / auto).is_dir():
198
+ for f in sorted((d / auto).glob("*.rs")):
199
+ if f"{auto}/{f.name}" not in listed:
200
+ tl.append((kind, f.stem, f"{auto}/{f.name}", ["bin"], []))
201
+ for f in sorted((d / auto).glob("*/main.rs")):
202
+ rp = f.relative_to(d).as_posix()
203
+ if rp not in listed:
204
+ tl.append((kind, f.parent.name, rp, ["bin"], []))
205
+ build = p.get("build")
206
+ if build is None and (d / "build.rs").exists():
207
+ build = "build.rs"
208
+ if isinstance(build, str):
209
+ tl.append(("build", "build-script-build", build, ["bin"], []))
210
+ lib_names = {_norm(n) for k, n, *_ in tl if k == "lib"}
211
+ for kind, tname, path, ctypes, req in tl:
212
+ pkg.targets.append(Target(kind, tname, _rel(root, d / path), _crate_for(pkg, kind, tname, lib_names), ctypes, req))
213
+ pkgs.append(pkg)
214
+ return pkgs
215
+
216
+
217
+ def discover(root: Path) -> tuple[list[Package], str]:
218
+ if not (root / "Cargo.toml").exists():
219
+ tc = tauri_crates(root)
220
+ if tc:
221
+ return _packages(root, tc, {}), "src-tauri/Cargo.toml"
222
+ if os.environ.get("CODEGRAPH_NO_CARGO") != "1":
223
+ md = from_metadata(root)
224
+ if md is not None:
225
+ return md, "cargo metadata"
226
+ return from_toml(root), "Cargo.toml (fallback)"