devlensio 0.6.2 → 1.0.1
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.
- package/README.md +89 -31
- package/dist/extractors/detectLanguage.d.ts +2 -0
- package/dist/extractors/detectLanguage.js +33 -0
- package/dist/extractors/index.d.ts +6 -0
- package/dist/extractors/index.js +155 -0
- package/dist/extractors/runner.d.ts +7 -0
- package/dist/extractors/runner.js +194 -0
- package/dist/extractors/types.d.ts +37 -0
- package/dist/extractors/types.js +8 -0
- package/dist/graph/buildLookup.d.ts +1 -0
- package/dist/graph/buildLookup.js +2 -1
- package/dist/graph/edges/callEdges.js +19 -5
- package/dist/graph/edges/callEdges.test.d.ts +1 -0
- package/dist/graph/edges/callEdges.test.js +200 -0
- package/dist/graph/edges/importEdges.js +12 -0
- package/dist/graph/edges/inheritanceEdges.d.ts +3 -0
- package/dist/graph/edges/inheritanceEdges.js +73 -0
- package/dist/graph/edges/inheritanceEdges.test.d.ts +1 -0
- package/dist/graph/edges/inheritanceEdges.test.js +140 -0
- package/dist/graph/index.js +4 -0
- package/dist/parser/classes.test.d.ts +1 -0
- package/dist/parser/classes.test.js +360 -0
- package/dist/parser/extractors/classes.d.ts +5 -0
- package/dist/parser/extractors/classes.js +241 -0
- package/dist/parser/index.d.ts +2 -0
- package/dist/parser/index.js +7 -1
- package/dist/pipeline/index.d.ts +2 -1
- package/dist/pipeline/index.js +30 -42
- package/dist/scoring/index.js +8 -0
- package/dist/scoring/index.test.js +43 -0
- package/dist/scoring/nodeScorer.js +2 -0
- package/dist/scoring/pruneDisconnected.d.ts +8 -0
- package/dist/scoring/pruneDisconnected.js +66 -0
- package/dist/scoring/pruneDisconnected.test.d.ts +1 -0
- package/dist/scoring/pruneDisconnected.test.js +123 -0
- package/dist/summarizer/prompts.d.ts +1 -1
- package/dist/types.d.ts +5 -5
- package/extractors/go/bin/darwin-amd64/devlens_go_extractor +0 -0
- package/extractors/go/bin/darwin-arm64/devlens_go_extractor +0 -0
- package/extractors/go/bin/linux-amd64/devlens_go_extractor +0 -0
- package/extractors/go/bin/linux-arm64/devlens_go_extractor +0 -0
- package/extractors/go/bin/windows-amd64/devlens_go_extractor.exe +0 -0
- package/extractors/go/build.mjs +43 -0
- package/extractors/go/calls.go +289 -0
- package/extractors/go/contract.go +140 -0
- package/extractors/go/extractor.go +161 -0
- package/extractors/go/fingerprint.go +167 -0
- package/extractors/go/go.mod +3 -0
- package/extractors/go/imports.go +90 -0
- package/extractors/go/inheritance.go +138 -0
- package/extractors/go/lookup.go +106 -0
- package/extractors/go/main.go +57 -0
- package/extractors/go/nodes.go +177 -0
- package/extractors/go/orm_edges.go +277 -0
- package/extractors/go/parser.go +469 -0
- package/extractors/go/routes.go +521 -0
- package/extractors/go/tests.go +45 -0
- package/extractors/go/thirdparty.go +138 -0
- package/extractors/go/typeload.go +178 -0
- package/extractors/go/walker.go +67 -0
- package/extractors/java/build.mjs +90 -0
- package/extractors/java/devlens_java_extractor.jar +0 -0
- package/extractors/java/src/devlens/extractor/Contract.java +171 -0
- package/extractors/java/src/devlens/extractor/Extractor.java +262 -0
- package/extractors/java/src/devlens/extractor/ExtractorResult.java +12 -0
- package/extractors/java/src/devlens/extractor/Fingerprint.java +239 -0
- package/extractors/java/src/devlens/extractor/LookupMaps.java +123 -0
- package/extractors/java/src/devlens/extractor/Main.java +66 -0
- package/extractors/java/src/devlens/extractor/Parser.java +522 -0
- package/extractors/java/src/devlens/extractor/SourceWalker.java +82 -0
- package/extractors/java/src/devlens/extractor/ThirdParty.java +141 -0
- package/extractors/java/src/devlens/extractor/TypeSolverFactory.java +43 -0
- package/extractors/java/src/devlens/extractor/edges/Calls.java +266 -0
- package/extractors/java/src/devlens/extractor/edges/Enrich.java +54 -0
- package/extractors/java/src/devlens/extractor/edges/Imports.java +154 -0
- package/extractors/java/src/devlens/extractor/edges/Inheritance.java +79 -0
- package/extractors/java/src/devlens/extractor/edges/OrmEdges.java +209 -0
- package/extractors/java/src/devlens/extractor/edges/Routes.java +143 -0
- package/extractors/java/src/devlens/extractor/edges/Tests.java +53 -0
- package/extractors/python/devlens_extractors_python/__init__.py +3 -0
- package/extractors/python/devlens_extractors_python/__main__.py +33 -0
- package/extractors/python/devlens_extractors_python/contract.py +94 -0
- package/extractors/python/devlens_extractors_python/edges/__init__.py +28 -0
- package/extractors/python/devlens_extractors_python/edges/calls.py +112 -0
- package/extractors/python/devlens_extractors_python/edges/enrich.py +44 -0
- package/extractors/python/devlens_extractors_python/edges/imports.py +155 -0
- package/extractors/python/devlens_extractors_python/edges/inheritance.py +97 -0
- package/extractors/python/devlens_extractors_python/edges/orm_edges.py +225 -0
- package/extractors/python/devlens_extractors_python/edges/routes/__init__.py +28 -0
- package/extractors/python/devlens_extractors_python/edges/routes/common.py +79 -0
- package/extractors/python/devlens_extractors_python/edges/routes/decorators.py +212 -0
- package/extractors/python/devlens_extractors_python/edges/routes/django_urls.py +213 -0
- package/extractors/python/devlens_extractors_python/edges/routes/drf.py +128 -0
- package/extractors/python/devlens_extractors_python/edges/tests.py +53 -0
- package/extractors/python/devlens_extractors_python/extractor.py +107 -0
- package/extractors/python/devlens_extractors_python/fingerprint.py +201 -0
- package/extractors/python/devlens_extractors_python/lookup.py +72 -0
- package/extractors/python/devlens_extractors_python/parser/__init__.py +72 -0
- package/extractors/python/devlens_extractors_python/parser/classes.py +109 -0
- package/extractors/python/devlens_extractors_python/parser/functions.py +163 -0
- package/extractors/python/devlens_extractors_python/parser/walker.py +30 -0
- package/extractors/python/devlens_extractors_python/third_party.py +103 -0
- package/extractors/python/pyproject.toml +16 -0
- package/extractors/python/setup.mjs +54 -0
- package/extractors/rust/Cargo.toml +29 -0
- package/extractors/rust/bin/darwin-amd64/devlens_rust_extractor +0 -0
- package/extractors/rust/bin/darwin-arm64/devlens_rust_extractor +0 -0
- package/extractors/rust/bin/linux-amd64/devlens_rust_extractor +0 -0
- package/extractors/rust/bin/linux-arm64/devlens_rust_extractor +0 -0
- package/extractors/rust/bin/windows-amd64/devlens_rust_extractor.exe +0 -0
- package/extractors/rust/build.mjs +85 -0
- package/extractors/rust/src/calls.rs +295 -0
- package/extractors/rust/src/contract.rs +236 -0
- package/extractors/rust/src/enrich.rs +107 -0
- package/extractors/rust/src/extractor.rs +199 -0
- package/extractors/rust/src/fingerprint.rs +238 -0
- package/extractors/rust/src/imports.rs +101 -0
- package/extractors/rust/src/inheritance.rs +233 -0
- package/extractors/rust/src/lookup.rs +226 -0
- package/extractors/rust/src/main.rs +53 -0
- package/extractors/rust/src/module_map.rs +131 -0
- package/extractors/rust/src/nodes.rs +174 -0
- package/extractors/rust/src/orm_edges.rs +125 -0
- package/extractors/rust/src/parser.rs +859 -0
- package/extractors/rust/src/routes.rs +1265 -0
- package/extractors/rust/src/tests.rs +109 -0
- package/extractors/rust/src/thirdparty.rs +114 -0
- package/extractors/rust/src/walker.rs +82 -0
- package/package.json +22 -4
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// nodes.rs — CodeNode building from parsed facts (mirrors go/nodes.go).
|
|
2
|
+
//
|
|
3
|
+
// Contract rules enforced here:
|
|
4
|
+
// - FILE id `file::rel/path.rs`; file nodes parent themselves
|
|
5
|
+
// - TEST files (any #[test] fn) = LEAF nodes: nodes=[] for them, test fns
|
|
6
|
+
// ride in metadata.testCases only (the bug that shipped in Python's
|
|
7
|
+
// parser — asserted in the bun suite)
|
|
8
|
+
// - rawCode + codeHash on every FUNCTION/METHOD/STRUCT/ENUM/TRAIT/IMPL_BLOCK
|
|
9
|
+
// - deterministic ids (file-scoped; trait-impl method suffix for the
|
|
10
|
+
// same-name-different-trait collision class)
|
|
11
|
+
|
|
12
|
+
use crate::contract::{base_metadata, code_node, file_node, CodeNode, NODE_FILE, NODE_TEST};
|
|
13
|
+
use crate::parser::{ParsedFile, ParsedItem};
|
|
14
|
+
use serde_json::{Map, Value};
|
|
15
|
+
|
|
16
|
+
/// FILE (or TEST leaf) nodes — always parents of their file's children.
|
|
17
|
+
pub fn collect_file_nodes(files: &[ParsedFile]) -> Vec<CodeNode> {
|
|
18
|
+
let mut out = vec![];
|
|
19
|
+
for pf in files {
|
|
20
|
+
if pf.is_test_file {
|
|
21
|
+
let mut n = file_node(&pf.rel_path, pf.line_count, NODE_TEST);
|
|
22
|
+
let meta = n
|
|
23
|
+
.metadata
|
|
24
|
+
.entry("testCases".to_string())
|
|
25
|
+
.or_insert_with(|| Value::Array(vec![]));
|
|
26
|
+
if let Value::Array(arr) = meta {
|
|
27
|
+
for t in &pf.test_fns {
|
|
28
|
+
arr.push(Value::from(t.clone()));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
out.push(n);
|
|
32
|
+
} else {
|
|
33
|
+
out.push(file_node(&pf.rel_path, pf.line_count, NODE_FILE));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
out
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Code nodes (FUNCTION/METHOD/STRUCT/ENUM/TRAIT/IMPL_BLOCK) — test fns are
|
|
40
|
+
/// NOT emitted (leaf rule); they feed metadata.testCases only.
|
|
41
|
+
pub fn collect_code_nodes(files: &[ParsedFile]) -> Vec<CodeNode> {
|
|
42
|
+
let mut out = vec![];
|
|
43
|
+
for pf in files {
|
|
44
|
+
if pf.is_test_file {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
for item in &pf.items {
|
|
48
|
+
if item.is_test {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
out.push(item_node(pf, item));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
out
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fn item_node(pf: &ParsedFile, item: &ParsedItem) -> CodeNode {
|
|
58
|
+
let mut meta = base_metadata();
|
|
59
|
+
meta.insert("isPublic".to_string(), Value::from(item.is_pub));
|
|
60
|
+
if item.is_async {
|
|
61
|
+
meta.insert("isAsync".to_string(), Value::from(true));
|
|
62
|
+
}
|
|
63
|
+
if item.receiver.contains("self") {
|
|
64
|
+
meta.insert("receiver".to_string(), Value::from(item.receiver.clone()));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let (id, node_type, name) = match item.kind {
|
|
68
|
+
crate::parser::KIND_FUNCTION => (
|
|
69
|
+
crate::lookup::func_node_id(&pf.rel_path, &item.name),
|
|
70
|
+
"FUNCTION",
|
|
71
|
+
item.name.clone(),
|
|
72
|
+
),
|
|
73
|
+
crate::parser::KIND_METHOD => {
|
|
74
|
+
let owner = item.impl_target.clone();
|
|
75
|
+
let trait_path = item.impl_trait_path.clone();
|
|
76
|
+
let id = crate::lookup::method_node_id(
|
|
77
|
+
&pf.rel_path,
|
|
78
|
+
&owner,
|
|
79
|
+
&item.name,
|
|
80
|
+
trait_path.as_deref(),
|
|
81
|
+
);
|
|
82
|
+
meta.insert("parentStruct".to_string(), Value::from(owner.clone()));
|
|
83
|
+
if let Some(t) = &trait_path {
|
|
84
|
+
meta.insert("traitPath".to_string(), Value::from(t.clone()));
|
|
85
|
+
}
|
|
86
|
+
(id, "METHOD", item.name.clone())
|
|
87
|
+
}
|
|
88
|
+
crate::parser::KIND_STRUCT => {
|
|
89
|
+
let id = crate::lookup::struct_node_id(&pf.rel_path, &item.name);
|
|
90
|
+
if !item.fields.is_empty() {
|
|
91
|
+
let fields: Vec<Value> = item
|
|
92
|
+
.fields
|
|
93
|
+
.iter()
|
|
94
|
+
.map(|f| Value::from(f.name.clone()))
|
|
95
|
+
.collect();
|
|
96
|
+
meta.insert("fields".to_string(), Value::Array(fields));
|
|
97
|
+
}
|
|
98
|
+
if !item.derives.is_empty() {
|
|
99
|
+
let d: Vec<Value> = item
|
|
100
|
+
.derives
|
|
101
|
+
.iter()
|
|
102
|
+
.map(|d| Value::from(d.clone()))
|
|
103
|
+
.collect();
|
|
104
|
+
meta.insert("derives".to_string(), Value::Array(d));
|
|
105
|
+
}
|
|
106
|
+
(id, "STRUCT", item.name.clone())
|
|
107
|
+
}
|
|
108
|
+
crate::parser::KIND_ENUM => {
|
|
109
|
+
let id = crate::lookup::enum_node_id(&pf.rel_path, &item.name);
|
|
110
|
+
if !item.variants.is_empty() {
|
|
111
|
+
let v: Vec<Value> = item
|
|
112
|
+
.variants
|
|
113
|
+
.iter()
|
|
114
|
+
.map(|v| Value::from(v.clone()))
|
|
115
|
+
.collect();
|
|
116
|
+
meta.insert("variants".to_string(), Value::Array(v));
|
|
117
|
+
}
|
|
118
|
+
(id, "ENUM", item.name.clone())
|
|
119
|
+
}
|
|
120
|
+
crate::parser::KIND_TRAIT => {
|
|
121
|
+
let id = crate::lookup::trait_node_id(&pf.rel_path, &item.name);
|
|
122
|
+
if !item.supertraits.is_empty() {
|
|
123
|
+
let s: Vec<Value> = item
|
|
124
|
+
.supertraits
|
|
125
|
+
.iter()
|
|
126
|
+
.map(|s| Value::from(s.clone()))
|
|
127
|
+
.collect();
|
|
128
|
+
meta.insert("supertraits".to_string(), Value::Array(s));
|
|
129
|
+
}
|
|
130
|
+
(id, "TRAIT", item.name.clone())
|
|
131
|
+
}
|
|
132
|
+
crate::parser::KIND_IMPL_BLOCK => {
|
|
133
|
+
let label = if item.impl_trait.is_some() {
|
|
134
|
+
format!(
|
|
135
|
+
"impl {} for {}",
|
|
136
|
+
item.impl_trait_path.as_deref().unwrap_or(""),
|
|
137
|
+
item.impl_target
|
|
138
|
+
)
|
|
139
|
+
} else {
|
|
140
|
+
format!("impl {}", item.impl_target)
|
|
141
|
+
};
|
|
142
|
+
let id = crate::lookup::impl_block_node_id(&pf.rel_path, &label, item.start_line);
|
|
143
|
+
if let Some(t) = &item.impl_trait_path {
|
|
144
|
+
meta.insert("traitPath".to_string(), Value::from(t.clone()));
|
|
145
|
+
}
|
|
146
|
+
if !item.impl_target.is_empty() {
|
|
147
|
+
meta.insert(
|
|
148
|
+
"implTarget".to_string(),
|
|
149
|
+
Value::from(item.impl_target.clone()),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
(id, "IMPL_BLOCK", label)
|
|
153
|
+
}
|
|
154
|
+
_ => return code_node("", "", "", "", 0, 0, "", Map::new()),
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
let mut n = code_node(
|
|
158
|
+
&id,
|
|
159
|
+
&pf.rel_path,
|
|
160
|
+
&name,
|
|
161
|
+
node_type,
|
|
162
|
+
item.start_line,
|
|
163
|
+
item.end_line,
|
|
164
|
+
&item.raw_code,
|
|
165
|
+
meta,
|
|
166
|
+
);
|
|
167
|
+
// metadata.calls — contract compliance + LLM context (the orchestrator
|
|
168
|
+
// never resolves them for subprocess languages, but they must be present)
|
|
169
|
+
if !item.calls.is_empty() {
|
|
170
|
+
let calls: Vec<Value> = item.calls.iter().map(|c| Value::from(c.clone())).collect();
|
|
171
|
+
n.metadata.insert("calls".to_string(), Value::Array(calls));
|
|
172
|
+
}
|
|
173
|
+
n
|
|
174
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// orm_edges.rs — Diesel data layer: table! concepts + model structs →
|
|
2
|
+
// READS_FROM / WRITES_TO edges (consumer → store, mirroring stateEdges.ts).
|
|
3
|
+
//
|
|
4
|
+
// Model detection (documented diesel API):
|
|
5
|
+
// - struct with #[derive(Queryable, Insertable, ...)] and/or
|
|
6
|
+
// #[diesel(table_name = users)] (legacy #[table_name = "users"]) → model
|
|
7
|
+
// R/W grammar (diesel query DSL):
|
|
8
|
+
// READ — users::table.filter/.first/.get_result/.get_results/.load/.find
|
|
9
|
+
// WRITE — diesel::insert_into(users::table) / users::table.insert(...) /
|
|
10
|
+
// diesel::update(users::table) / diesel::delete(...)
|
|
11
|
+
// Target: the model STRUCT whose table_name matches; fallback = the FILE
|
|
12
|
+
// containing the table! decl (no TABLE node type in types.ts — documented).
|
|
13
|
+
// sqlx raw SQL → V2 (documented). isModel/tableName metadata enrichment
|
|
14
|
+
// happens in enrich.rs (same facts, single source).
|
|
15
|
+
|
|
16
|
+
use crate::contract::{edge, CodeEdge, EDGE_READS_FROM, EDGE_WRITES_TO};
|
|
17
|
+
use crate::extractor::{Options, ParsedRepo};
|
|
18
|
+
use std::collections::HashMap;
|
|
19
|
+
|
|
20
|
+
/// table name → model struct node id
|
|
21
|
+
pub fn table_to_model_map(repo: &ParsedRepo) -> HashMap<String, String> {
|
|
22
|
+
let mut map = HashMap::new();
|
|
23
|
+
for pf in &repo.files {
|
|
24
|
+
if pf.is_test_file {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
for item in &pf.items {
|
|
28
|
+
if item.kind != crate::parser::KIND_STRUCT || item.is_test {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
let is_diesel_model = item.derives.iter().any(|d| {
|
|
32
|
+
d == "Queryable"
|
|
33
|
+
|| d == "Insertable"
|
|
34
|
+
|| d == "QueryableByName"
|
|
35
|
+
|| d == "Associations"
|
|
36
|
+
|| d == "Identifiable"
|
|
37
|
+
}) || item.attrs.iter().any(|a| {
|
|
38
|
+
(a.path == "diesel" && a.args.contains("table_name")) || a.path == "table_name"
|
|
39
|
+
});
|
|
40
|
+
if !is_diesel_model {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
let table_name = diesel_table_name(item);
|
|
44
|
+
if !table_name.is_empty() {
|
|
45
|
+
let id = crate::lookup::struct_node_id(&pf.rel_path, &item.name);
|
|
46
|
+
map.insert(table_name, id);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
map
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// table name → file containing the table! decl (fallback store target).
|
|
54
|
+
pub fn table_to_schema_file_map(repo: &ParsedRepo) -> HashMap<String, String> {
|
|
55
|
+
let mut map = HashMap::new();
|
|
56
|
+
for pf in &repo.files {
|
|
57
|
+
for t in &pf.tables {
|
|
58
|
+
map.insert(t.name.clone(), pf.rel_path.clone());
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
map
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Parse the table name from #[diesel(table_name = users)] /
|
|
65
|
+
/// #[table_name = "users"] (legacy diesel 1.x).
|
|
66
|
+
pub fn diesel_table_name(item: &crate::parser::ParsedItem) -> String {
|
|
67
|
+
for a in &item.attrs {
|
|
68
|
+
if a.path == "diesel" && a.args.contains("table_name") {
|
|
69
|
+
if let Some((_, v)) = a.args.split_once('=') {
|
|
70
|
+
return v.trim().trim_matches('"').to_string();
|
|
71
|
+
}
|
|
72
|
+
} else if a.path == "table_name" {
|
|
73
|
+
return a.args.trim().trim_matches('"').to_string();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
String::new()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
pub fn detect_orm_edges(
|
|
80
|
+
repo: &ParsedRepo,
|
|
81
|
+
_lookup: &crate::lookup::LookupMaps,
|
|
82
|
+
_opts: &Options,
|
|
83
|
+
) -> Vec<CodeEdge> {
|
|
84
|
+
let mut edges = vec![];
|
|
85
|
+
if repo.files.is_empty() {
|
|
86
|
+
return edges;
|
|
87
|
+
}
|
|
88
|
+
let table_to_model = table_to_model_map(repo);
|
|
89
|
+
let table_to_schema_file = table_to_schema_file_map(repo);
|
|
90
|
+
|
|
91
|
+
for pf in &repo.files {
|
|
92
|
+
if pf.is_test_file {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
for item in &pf.items {
|
|
96
|
+
if item.is_test || item.orm_calls.is_empty() {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if item.kind != crate::parser::KIND_FUNCTION && item.kind != crate::parser::KIND_METHOD
|
|
100
|
+
{
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
let from = crate::lookup::node_id_for_item(&pf.rel_path, item);
|
|
104
|
+
for oc in &item.orm_calls {
|
|
105
|
+
// normalize the table ref ("users::table" → "users") — top-
|
|
106
|
+
// level fn args keep the raw path (insert_into(users::table))
|
|
107
|
+
let table = crate::parser::table_name_from_ref(&oc.table_ref);
|
|
108
|
+
let target = table_to_model.get(&table).cloned().or_else(|| {
|
|
109
|
+
table_to_schema_file
|
|
110
|
+
.get(&table)
|
|
111
|
+
.map(|f| format!("file::{}", f))
|
|
112
|
+
});
|
|
113
|
+
if let Some(to) = target {
|
|
114
|
+
let etype = if oc.kind == "read" {
|
|
115
|
+
EDGE_READS_FROM
|
|
116
|
+
} else {
|
|
117
|
+
EDGE_WRITES_TO
|
|
118
|
+
};
|
|
119
|
+
edges.push(edge(&from, &to, etype));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
edges
|
|
125
|
+
}
|