grimp 3.6__tar.gz → 3.7__tar.gz
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.
- {grimp-3.6 → grimp-3.7}/PKG-INFO +1 -1
- {grimp-3.6 → grimp-3.7}/pyproject.toml +1 -1
- {grimp-3.6 → grimp-3.7}/rust/Cargo.lock +107 -3
- {grimp-3.6 → grimp-3.7}/rust/Cargo.toml +3 -0
- {grimp-3.6 → grimp-3.7}/rust/src/errors.rs +7 -1
- {grimp-3.6 → grimp-3.7}/rust/src/exceptions.rs +5 -0
- grimp-3.7/rust/src/graph/direct_import_queries.rs +126 -0
- {grimp-3.6 → grimp-3.7}/rust/src/graph/graph_manipulation.rs +13 -7
- grimp-3.7/rust/src/graph/hierarchy_queries.rs +105 -0
- {grimp-3.6 → grimp-3.7}/rust/src/graph/import_chain_queries.rs +2 -2
- {grimp-3.6 → grimp-3.7}/rust/src/graph/mod.rs +22 -11
- {grimp-3.6 → grimp-3.7}/rust/src/graph/pathfinding.rs +2 -1
- {grimp-3.6 → grimp-3.7}/rust/src/lib.rs +81 -36
- grimp-3.7/rust/src/module_expressions.rs +196 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/__init__.py +3 -2
- {grimp-3.6 → grimp-3.7}/src/grimp/adaptors/graph.py +32 -4
- {grimp-3.6 → grimp-3.7}/src/grimp/application/ports/graph.py +77 -2
- {grimp-3.6 → grimp-3.7}/src/grimp/exceptions.py +8 -0
- grimp-3.6/rust/src/graph/direct_import_queries.rs +0 -57
- grimp-3.6/rust/src/graph/hierarchy_queries.rs +0 -50
- {grimp-3.6 → grimp-3.7}/README.rst +0 -0
- {grimp-3.6 → grimp-3.7}/rust/.cargo/config.toml +0 -0
- {grimp-3.6 → grimp-3.7}/rust/src/graph/higher_order_queries.rs +0 -0
- {grimp-3.6 → grimp-3.7}/rust/tests/large.rs +0 -0
- {grimp-3.6 → grimp-3.7}/rust/tests/large_graph.json +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/adaptors/__init__.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/adaptors/caching.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/adaptors/filesystem.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/adaptors/importscanner.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/adaptors/modulefinder.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/adaptors/packagefinder.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/adaptors/timing.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/algorithms/__init__.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/algorithms/shortest_path.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/application/__init__.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/application/config.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/application/ports/__init__.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/application/ports/caching.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/application/ports/filesystem.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/application/ports/importscanner.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/application/ports/modulefinder.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/application/ports/packagefinder.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/application/ports/timing.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/application/usecases.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/domain/__init__.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/domain/analysis.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/domain/valueobjects.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/helpers.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/main.py +0 -0
- {grimp-3.6 → grimp-3.7}/src/grimp/py.typed +0 -0
{grimp-3.6 → grimp-3.7}/PKG-INFO
RENAMED
|
@@ -7,13 +7,16 @@ name = "_rustgrimp"
|
|
|
7
7
|
version = "0.1.0"
|
|
8
8
|
dependencies = [
|
|
9
9
|
"bimap",
|
|
10
|
+
"const_format",
|
|
10
11
|
"derive-new",
|
|
11
12
|
"getset",
|
|
12
|
-
"indexmap",
|
|
13
|
+
"indexmap 2.7.1",
|
|
13
14
|
"itertools",
|
|
14
15
|
"lazy_static",
|
|
16
|
+
"parameterized",
|
|
15
17
|
"pyo3",
|
|
16
18
|
"rayon",
|
|
19
|
+
"regex",
|
|
17
20
|
"rustc-hash",
|
|
18
21
|
"serde_json",
|
|
19
22
|
"slotmap",
|
|
@@ -22,6 +25,15 @@ dependencies = [
|
|
|
22
25
|
"thiserror",
|
|
23
26
|
]
|
|
24
27
|
|
|
28
|
+
[[package]]
|
|
29
|
+
name = "aho-corasick"
|
|
30
|
+
version = "1.1.3"
|
|
31
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
32
|
+
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
|
33
|
+
dependencies = [
|
|
34
|
+
"memchr",
|
|
35
|
+
]
|
|
36
|
+
|
|
25
37
|
[[package]]
|
|
26
38
|
name = "autocfg"
|
|
27
39
|
version = "1.4.0"
|
|
@@ -40,6 +52,26 @@ version = "1.0.0"
|
|
|
40
52
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
41
53
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
42
54
|
|
|
55
|
+
[[package]]
|
|
56
|
+
name = "const_format"
|
|
57
|
+
version = "0.2.34"
|
|
58
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
59
|
+
checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd"
|
|
60
|
+
dependencies = [
|
|
61
|
+
"const_format_proc_macros",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[[package]]
|
|
65
|
+
name = "const_format_proc_macros"
|
|
66
|
+
version = "0.2.34"
|
|
67
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
68
|
+
checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744"
|
|
69
|
+
dependencies = [
|
|
70
|
+
"proc-macro2",
|
|
71
|
+
"quote",
|
|
72
|
+
"unicode-xid",
|
|
73
|
+
]
|
|
74
|
+
|
|
43
75
|
[[package]]
|
|
44
76
|
name = "crossbeam-deque"
|
|
45
77
|
version = "0.8.6"
|
|
@@ -106,6 +138,12 @@ dependencies = [
|
|
|
106
138
|
"syn",
|
|
107
139
|
]
|
|
108
140
|
|
|
141
|
+
[[package]]
|
|
142
|
+
name = "hashbrown"
|
|
143
|
+
version = "0.12.3"
|
|
144
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
145
|
+
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
|
146
|
+
|
|
109
147
|
[[package]]
|
|
110
148
|
name = "hashbrown"
|
|
111
149
|
version = "0.15.2"
|
|
@@ -121,6 +159,16 @@ version = "0.5.0"
|
|
|
121
159
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
122
160
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
123
161
|
|
|
162
|
+
[[package]]
|
|
163
|
+
name = "indexmap"
|
|
164
|
+
version = "1.9.3"
|
|
165
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
166
|
+
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
|
167
|
+
dependencies = [
|
|
168
|
+
"autocfg",
|
|
169
|
+
"hashbrown 0.12.3",
|
|
170
|
+
]
|
|
171
|
+
|
|
124
172
|
[[package]]
|
|
125
173
|
name = "indexmap"
|
|
126
174
|
version = "2.7.1"
|
|
@@ -128,7 +176,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
128
176
|
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
|
129
177
|
dependencies = [
|
|
130
178
|
"equivalent",
|
|
131
|
-
"hashbrown",
|
|
179
|
+
"hashbrown 0.15.2",
|
|
132
180
|
]
|
|
133
181
|
|
|
134
182
|
[[package]]
|
|
@@ -185,6 +233,27 @@ version = "1.20.2"
|
|
|
185
233
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
186
234
|
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
|
187
235
|
|
|
236
|
+
[[package]]
|
|
237
|
+
name = "parameterized"
|
|
238
|
+
version = "2.0.0"
|
|
239
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
240
|
+
checksum = "194bf497674dda552d4f1bd24d325f828f425876c9d522fcb1810cd527e0bd4e"
|
|
241
|
+
dependencies = [
|
|
242
|
+
"parameterized-macro",
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
[[package]]
|
|
246
|
+
name = "parameterized-macro"
|
|
247
|
+
version = "2.0.0"
|
|
248
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
249
|
+
checksum = "1374bca5edab7a317c4ffbc9df1e239ceb7dcf5426b6b403474408442a9777ac"
|
|
250
|
+
dependencies = [
|
|
251
|
+
"indexmap 1.9.3",
|
|
252
|
+
"proc-macro2",
|
|
253
|
+
"quote",
|
|
254
|
+
"syn",
|
|
255
|
+
]
|
|
256
|
+
|
|
188
257
|
[[package]]
|
|
189
258
|
name = "portable-atomic"
|
|
190
259
|
version = "1.10.0"
|
|
@@ -314,6 +383,35 @@ dependencies = [
|
|
|
314
383
|
"crossbeam-utils",
|
|
315
384
|
]
|
|
316
385
|
|
|
386
|
+
[[package]]
|
|
387
|
+
name = "regex"
|
|
388
|
+
version = "1.11.1"
|
|
389
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
390
|
+
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
|
391
|
+
dependencies = [
|
|
392
|
+
"aho-corasick",
|
|
393
|
+
"memchr",
|
|
394
|
+
"regex-automata",
|
|
395
|
+
"regex-syntax",
|
|
396
|
+
]
|
|
397
|
+
|
|
398
|
+
[[package]]
|
|
399
|
+
name = "regex-automata"
|
|
400
|
+
version = "0.4.9"
|
|
401
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
402
|
+
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
|
403
|
+
dependencies = [
|
|
404
|
+
"aho-corasick",
|
|
405
|
+
"memchr",
|
|
406
|
+
"regex-syntax",
|
|
407
|
+
]
|
|
408
|
+
|
|
409
|
+
[[package]]
|
|
410
|
+
name = "regex-syntax"
|
|
411
|
+
version = "0.8.5"
|
|
412
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
413
|
+
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
|
414
|
+
|
|
317
415
|
[[package]]
|
|
318
416
|
name = "rustc-hash"
|
|
319
417
|
version = "2.1.0"
|
|
@@ -373,7 +471,7 @@ version = "0.18.0"
|
|
|
373
471
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
374
472
|
checksum = "1a3275464d7a9f2d4cac57c89c2ef96a8524dba2864c8d6f82e3980baf136f9b"
|
|
375
473
|
dependencies = [
|
|
376
|
-
"hashbrown",
|
|
474
|
+
"hashbrown 0.15.2",
|
|
377
475
|
"serde",
|
|
378
476
|
]
|
|
379
477
|
|
|
@@ -426,6 +524,12 @@ version = "1.0.14"
|
|
|
426
524
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
427
525
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
|
428
526
|
|
|
527
|
+
[[package]]
|
|
528
|
+
name = "unicode-xid"
|
|
529
|
+
version = "0.2.6"
|
|
530
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
531
|
+
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
|
532
|
+
|
|
429
533
|
[[package]]
|
|
430
534
|
name = "unindent"
|
|
431
535
|
version = "0.2.3"
|
|
@@ -20,6 +20,8 @@ itertools = "0.14.0"
|
|
|
20
20
|
tap = "1.0.1"
|
|
21
21
|
rustc-hash = "2.1.0"
|
|
22
22
|
indexmap = "2.7.1"
|
|
23
|
+
regex = "1.11.1"
|
|
24
|
+
const_format = "0.2.34"
|
|
23
25
|
|
|
24
26
|
[dependencies.pyo3]
|
|
25
27
|
version = "0.23.4"
|
|
@@ -29,4 +31,5 @@ extension-module = ["pyo3/extension-module"]
|
|
|
29
31
|
default = ["extension-module"]
|
|
30
32
|
|
|
31
33
|
[dev-dependencies]
|
|
34
|
+
parameterized = "2.0.0"
|
|
32
35
|
serde_json = "1.0.137"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use crate::exceptions::{ModuleNotPresent, NoSuchContainer};
|
|
1
|
+
use crate::exceptions::{InvalidModuleExpression, ModuleNotPresent, NoSuchContainer};
|
|
2
2
|
use pyo3::exceptions::PyValueError;
|
|
3
3
|
use pyo3::PyErr;
|
|
4
4
|
use thiserror::Error;
|
|
@@ -13,6 +13,9 @@ pub enum GrimpError {
|
|
|
13
13
|
|
|
14
14
|
#[error("Modules have shared descendants.")]
|
|
15
15
|
SharedDescendants,
|
|
16
|
+
|
|
17
|
+
#[error("{0} is not a valid module expression.")]
|
|
18
|
+
InvalidModuleExpression(String),
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
pub type GrimpResult<T> = Result<T, GrimpError>;
|
|
@@ -24,6 +27,9 @@ impl From<GrimpError> for PyErr {
|
|
|
24
27
|
GrimpError::ModuleNotPresent(_) => ModuleNotPresent::new_err(value.to_string()),
|
|
25
28
|
GrimpError::NoSuchContainer(_) => NoSuchContainer::new_err(value.to_string()),
|
|
26
29
|
GrimpError::SharedDescendants => PyValueError::new_err(value.to_string()),
|
|
30
|
+
GrimpError::InvalidModuleExpression(_) => {
|
|
31
|
+
InvalidModuleExpression::new_err(value.to_string())
|
|
32
|
+
}
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
35
|
}
|
|
@@ -2,3 +2,8 @@ use pyo3::create_exception;
|
|
|
2
2
|
|
|
3
3
|
create_exception!(_rustgrimp, ModuleNotPresent, pyo3::exceptions::PyException);
|
|
4
4
|
create_exception!(_rustgrimp, NoSuchContainer, pyo3::exceptions::PyException);
|
|
5
|
+
create_exception!(
|
|
6
|
+
_rustgrimp,
|
|
7
|
+
InvalidModuleExpression,
|
|
8
|
+
pyo3::exceptions::PyException
|
|
9
|
+
);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
use crate::errors::{GrimpError, GrimpResult};
|
|
2
|
+
use crate::graph::{
|
|
3
|
+
ExtendWithDescendants, Graph, ImportDetails, ModuleToken, EMPTY_IMPORT_DETAILS,
|
|
4
|
+
EMPTY_MODULE_TOKENS, MODULE_NAMES,
|
|
5
|
+
};
|
|
6
|
+
use crate::module_expressions::ModuleExpression;
|
|
7
|
+
use rustc_hash::FxHashSet;
|
|
8
|
+
|
|
9
|
+
impl Graph {
|
|
10
|
+
pub fn count_imports(&self) -> usize {
|
|
11
|
+
self.imports.values().map(|imports| imports.len()).sum()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
pub fn direct_import_exists(
|
|
15
|
+
&self,
|
|
16
|
+
importer: ModuleToken,
|
|
17
|
+
imported: ModuleToken,
|
|
18
|
+
as_packages: bool,
|
|
19
|
+
) -> GrimpResult<bool> {
|
|
20
|
+
let mut importer: FxHashSet<_> = importer.into();
|
|
21
|
+
let mut imported: FxHashSet<_> = imported.into();
|
|
22
|
+
if as_packages {
|
|
23
|
+
importer.extend_with_descendants(self);
|
|
24
|
+
imported.extend_with_descendants(self);
|
|
25
|
+
if !(&importer & &imported).is_empty() {
|
|
26
|
+
return Err(GrimpError::SharedDescendants);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let direct_imports = importer
|
|
31
|
+
.iter()
|
|
32
|
+
.flat_map(|module| self.imports.get(*module).unwrap().iter().cloned())
|
|
33
|
+
.collect::<FxHashSet<ModuleToken>>();
|
|
34
|
+
|
|
35
|
+
Ok(!(&direct_imports & &imported).is_empty())
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
pub fn modules_directly_imported_by(&self, importer: ModuleToken) -> &FxHashSet<ModuleToken> {
|
|
39
|
+
self.imports.get(importer).unwrap_or(&EMPTY_MODULE_TOKENS)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
pub fn modules_that_directly_import(&self, imported: ModuleToken) -> &FxHashSet<ModuleToken> {
|
|
43
|
+
self.reverse_imports
|
|
44
|
+
.get(imported)
|
|
45
|
+
.unwrap_or(&EMPTY_MODULE_TOKENS)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub fn get_import_details(
|
|
49
|
+
&self,
|
|
50
|
+
importer: ModuleToken,
|
|
51
|
+
imported: ModuleToken,
|
|
52
|
+
) -> &FxHashSet<ImportDetails> {
|
|
53
|
+
match self.import_details.get(&(importer, imported)) {
|
|
54
|
+
Some(import_details) => import_details,
|
|
55
|
+
None => &EMPTY_IMPORT_DETAILS,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
pub fn find_matching_direct_imports(
|
|
60
|
+
&self,
|
|
61
|
+
importer_expression: &ModuleExpression,
|
|
62
|
+
imported_expression: &ModuleExpression,
|
|
63
|
+
) -> FxHashSet<(ModuleToken, ModuleToken)> {
|
|
64
|
+
let interner = MODULE_NAMES.read().unwrap();
|
|
65
|
+
self.imports
|
|
66
|
+
.iter()
|
|
67
|
+
.flat_map(|(importer, imports)| {
|
|
68
|
+
imports
|
|
69
|
+
.iter()
|
|
70
|
+
.cloned()
|
|
71
|
+
.map(move |imported| (importer, imported))
|
|
72
|
+
})
|
|
73
|
+
.filter(|(importer, imported)| {
|
|
74
|
+
let importer = self.get_module(*importer).unwrap();
|
|
75
|
+
let importer_name = interner.resolve(importer.interned_name).unwrap();
|
|
76
|
+
let imported = self.get_module(*imported).unwrap();
|
|
77
|
+
let imported_name = interner.resolve(imported.interned_name).unwrap();
|
|
78
|
+
importer_expression.is_match(importer_name)
|
|
79
|
+
&& imported_expression.is_match(imported_name)
|
|
80
|
+
})
|
|
81
|
+
.collect()
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
#[cfg(test)]
|
|
86
|
+
mod test {
|
|
87
|
+
use super::*;
|
|
88
|
+
|
|
89
|
+
#[test]
|
|
90
|
+
fn test_find_matching_direct_imports() {
|
|
91
|
+
let mut graph = Graph::default();
|
|
92
|
+
|
|
93
|
+
let _ = graph.get_or_add_module("pkg.animals").token;
|
|
94
|
+
let dog = graph.get_or_add_module("pkg.animals.dog").token;
|
|
95
|
+
let cat = graph.get_or_add_module("pkg.animals.cat").token;
|
|
96
|
+
let _ = graph.get_or_add_module("pkg.food").token;
|
|
97
|
+
let chicken = graph.get_or_add_module("pkg.food.chicken").token;
|
|
98
|
+
let fish = graph.get_or_add_module("pkg.food.fish").token;
|
|
99
|
+
let _ = graph.get_or_add_module("pkg.colors").token;
|
|
100
|
+
let golden = graph.get_or_add_module("pkg.colors.golden").token;
|
|
101
|
+
let ginger = graph.get_or_add_module("pkg.colors.ginger").token;
|
|
102
|
+
let _ = graph.get_or_add_module("pkg.shops").token;
|
|
103
|
+
let tesco = graph.get_or_add_module("pkg.shops.tesco").token;
|
|
104
|
+
let coop = graph.get_or_add_module("pkg.shops.coop").token;
|
|
105
|
+
|
|
106
|
+
// Should match
|
|
107
|
+
graph.add_import(dog, chicken);
|
|
108
|
+
graph.add_import(cat, fish);
|
|
109
|
+
// Should not match: Imported does not match
|
|
110
|
+
graph.add_import(dog, golden);
|
|
111
|
+
graph.add_import(cat, ginger);
|
|
112
|
+
// Should not match: Importer does not match
|
|
113
|
+
graph.add_import(tesco, chicken);
|
|
114
|
+
graph.add_import(coop, fish);
|
|
115
|
+
|
|
116
|
+
let importer_expression = "pkg.animals.*".parse().unwrap();
|
|
117
|
+
let imported_expression = "pkg.food.*".parse().unwrap();
|
|
118
|
+
let matching_imports =
|
|
119
|
+
graph.find_matching_direct_imports(&importer_expression, &imported_expression);
|
|
120
|
+
|
|
121
|
+
assert_eq!(
|
|
122
|
+
matching_imports,
|
|
123
|
+
FxHashSet::from_iter([(dog, chicken), (cat, fish)])
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
use crate::graph::{
|
|
1
|
+
use crate::graph::{
|
|
2
|
+
Graph, ImportDetails, Module, ModuleIterator, ModuleToken, IMPORT_LINE_CONTENTS, MODULE_NAMES,
|
|
3
|
+
};
|
|
2
4
|
use rustc_hash::FxHashSet;
|
|
3
5
|
use slotmap::secondary::Entry;
|
|
4
6
|
|
|
@@ -31,7 +33,7 @@ impl Graph {
|
|
|
31
33
|
} else {
|
|
32
34
|
let module = self.modules.insert_with_key(|token| Module {
|
|
33
35
|
token,
|
|
34
|
-
name,
|
|
36
|
+
interned_name: name,
|
|
35
37
|
is_invisible: !ancestor_names.is_empty(),
|
|
36
38
|
is_squashed: false,
|
|
37
39
|
});
|
|
@@ -116,7 +118,7 @@ impl Graph {
|
|
|
116
118
|
&mut self,
|
|
117
119
|
importer: ModuleToken,
|
|
118
120
|
imported: ModuleToken,
|
|
119
|
-
line_number:
|
|
121
|
+
line_number: u32,
|
|
120
122
|
line_contents: &str,
|
|
121
123
|
) {
|
|
122
124
|
self.imports
|
|
@@ -129,10 +131,14 @@ impl Graph {
|
|
|
129
131
|
.unwrap()
|
|
130
132
|
.or_default()
|
|
131
133
|
.insert(importer);
|
|
132
|
-
|
|
133
|
-
.
|
|
134
|
-
.
|
|
135
|
-
.
|
|
134
|
+
{
|
|
135
|
+
let mut interner = IMPORT_LINE_CONTENTS.write().unwrap();
|
|
136
|
+
let line_contents = interner.get_or_intern(line_contents);
|
|
137
|
+
self.import_details
|
|
138
|
+
.entry((importer, imported))
|
|
139
|
+
.or_default()
|
|
140
|
+
.insert(ImportDetails::new(line_number, line_contents));
|
|
141
|
+
}
|
|
136
142
|
}
|
|
137
143
|
|
|
138
144
|
pub fn remove_import(&mut self, importer: ModuleToken, imported: ModuleToken) {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
use crate::graph::{Graph, Module, ModuleIterator, ModuleToken, MODULE_NAMES};
|
|
2
|
+
use crate::module_expressions::ModuleExpression;
|
|
3
|
+
use rustc_hash::FxHashSet;
|
|
4
|
+
|
|
5
|
+
impl Graph {
|
|
6
|
+
pub fn get_module_by_name(&self, name: &str) -> Option<&Module> {
|
|
7
|
+
let interner = MODULE_NAMES.read().unwrap();
|
|
8
|
+
let name = interner.get(name)?;
|
|
9
|
+
match self.modules_by_name.get_by_left(&name) {
|
|
10
|
+
Some(token) => self.get_module(*token),
|
|
11
|
+
None => None,
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
pub fn get_module(&self, module: ModuleToken) -> Option<&Module> {
|
|
16
|
+
self.modules.get(module)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// TODO(peter) Guarantee order?
|
|
20
|
+
pub fn all_modules(&self) -> impl ModuleIterator {
|
|
21
|
+
self.modules.values()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
pub fn get_module_parent(&self, module: ModuleToken) -> Option<&Module> {
|
|
25
|
+
match self.module_parents.get(module) {
|
|
26
|
+
Some(parent) => parent.map(|parent| self.get_module(parent).unwrap()),
|
|
27
|
+
None => None,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pub fn get_module_children(&self, module: ModuleToken) -> impl ModuleIterator {
|
|
32
|
+
let children = match self.module_children.get(module) {
|
|
33
|
+
Some(children) => children
|
|
34
|
+
.iter()
|
|
35
|
+
.map(|child| self.get_module(*child).unwrap())
|
|
36
|
+
.collect(),
|
|
37
|
+
None => Vec::new(),
|
|
38
|
+
};
|
|
39
|
+
children.into_iter()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Returns an iterator over the passed modules descendants.
|
|
43
|
+
///
|
|
44
|
+
/// Parent modules will be yielded before their child modules.
|
|
45
|
+
pub fn get_module_descendants(&self, module: ModuleToken) -> impl ModuleIterator {
|
|
46
|
+
let mut descendants = self.get_module_children(module).collect::<Vec<_>>();
|
|
47
|
+
for child in descendants.clone() {
|
|
48
|
+
descendants.extend(self.get_module_descendants(child.token).collect::<Vec<_>>())
|
|
49
|
+
}
|
|
50
|
+
descendants.into_iter()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
pub fn find_matching_modules(&self, expression: &ModuleExpression) -> impl ModuleIterator {
|
|
54
|
+
let interner = MODULE_NAMES.read().unwrap();
|
|
55
|
+
let modules: FxHashSet<_> = self
|
|
56
|
+
.modules
|
|
57
|
+
.values()
|
|
58
|
+
.filter(|m| expression.is_match(interner.resolve(m.interned_name).unwrap()))
|
|
59
|
+
.collect();
|
|
60
|
+
modules.into_iter()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#[cfg(test)]
|
|
65
|
+
mod tests {
|
|
66
|
+
use super::*;
|
|
67
|
+
use crate::graph::ModuleIterator;
|
|
68
|
+
use derive_new::new;
|
|
69
|
+
use parameterized::parameterized;
|
|
70
|
+
|
|
71
|
+
#[derive(Debug, new)]
|
|
72
|
+
struct FindMatchingModulesTestCase<'a> {
|
|
73
|
+
expression: &'a str,
|
|
74
|
+
#[new(into)]
|
|
75
|
+
expected_matching_modules: Vec<&'a str>,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#[parameterized(case = {
|
|
79
|
+
FindMatchingModulesTestCase::new("foo", ["foo"]),
|
|
80
|
+
FindMatchingModulesTestCase::new("foo.*", ["foo.bar"]),
|
|
81
|
+
FindMatchingModulesTestCase::new("foo.**", ["foo.bar", "foo.bar.baz"]),
|
|
82
|
+
})]
|
|
83
|
+
fn test_find_matching_modules(case: FindMatchingModulesTestCase) {
|
|
84
|
+
// The test here is just a sense check - we don't need to check again here how all the
|
|
85
|
+
// different types of module expressions work since that is already tested
|
|
86
|
+
// thoroughly elsewhere.
|
|
87
|
+
let mut graph = Graph::default();
|
|
88
|
+
graph.get_or_add_module("foo");
|
|
89
|
+
graph.get_or_add_module("foo.bar");
|
|
90
|
+
graph.get_or_add_module("foo.bar.baz");
|
|
91
|
+
|
|
92
|
+
let expression = case.expression.parse().unwrap();
|
|
93
|
+
|
|
94
|
+
let matching_modules: FxHashSet<_> =
|
|
95
|
+
graph.find_matching_modules(&expression).tokens().collect();
|
|
96
|
+
|
|
97
|
+
assert_eq!(
|
|
98
|
+
matching_modules,
|
|
99
|
+
case.expected_matching_modules
|
|
100
|
+
.into_iter()
|
|
101
|
+
.map(|module| graph.get_module_by_name(module).unwrap().token)
|
|
102
|
+
.collect()
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
use crate::errors::GrimpResult;
|
|
2
|
-
use crate::graph::pathfinding::{find_reach,
|
|
2
|
+
use crate::graph::pathfinding::{find_reach, find_shortest_path};
|
|
3
3
|
use crate::graph::{ExtendWithDescendants, Graph, ModuleToken};
|
|
4
4
|
use itertools::Itertools;
|
|
5
5
|
use rustc_hash::{FxHashMap, FxHashSet};
|
|
@@ -62,7 +62,7 @@ impl Graph {
|
|
|
62
62
|
excluded_modules: &FxHashSet<ModuleToken>,
|
|
63
63
|
excluded_imports: &FxHashMap<ModuleToken, FxHashSet<ModuleToken>>,
|
|
64
64
|
) -> GrimpResult<Option<Vec<ModuleToken>>> {
|
|
65
|
-
|
|
65
|
+
find_shortest_path(
|
|
66
66
|
self,
|
|
67
67
|
from_modules,
|
|
68
68
|
to_modules,
|
|
@@ -19,6 +19,8 @@ pub(crate) mod pathfinding;
|
|
|
19
19
|
lazy_static! {
|
|
20
20
|
static ref MODULE_NAMES: RwLock<StringInterner<StringBackend>> =
|
|
21
21
|
RwLock::new(StringInterner::default());
|
|
22
|
+
static ref IMPORT_LINE_CONTENTS: RwLock<StringInterner<StringBackend>> =
|
|
23
|
+
RwLock::new(StringInterner::default());
|
|
22
24
|
static ref EMPTY_MODULE_TOKENS: FxHashSet<ModuleToken> = FxHashSet::default();
|
|
23
25
|
static ref EMPTY_IMPORT_DETAILS: FxHashSet<ImportDetails> = FxHashSet::default();
|
|
24
26
|
static ref EMPTY_IMPORTS: FxHashSet<(ModuleToken, ModuleToken)> = FxHashSet::default();
|
|
@@ -32,7 +34,7 @@ pub struct Module {
|
|
|
32
34
|
token: ModuleToken,
|
|
33
35
|
|
|
34
36
|
#[getset(get_copy = "pub")]
|
|
35
|
-
|
|
37
|
+
interned_name: DefaultSymbol,
|
|
36
38
|
|
|
37
39
|
// Invisible modules exist in the hierarchy but haven't been explicitly added to the graph.
|
|
38
40
|
#[getset(get_copy = "pub")]
|
|
@@ -43,9 +45,9 @@ pub struct Module {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
impl Module {
|
|
46
|
-
pub fn
|
|
48
|
+
pub fn name(&self) -> String {
|
|
47
49
|
let interner = MODULE_NAMES.read().unwrap();
|
|
48
|
-
interner.resolve(self.
|
|
50
|
+
interner.resolve(self.interned_name).unwrap().to_owned()
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -102,13 +104,13 @@ pub trait ModuleIterator<'a>: Iterator<Item = &'a Module> + Sized {
|
|
|
102
104
|
self.map(|m| m.token)
|
|
103
105
|
}
|
|
104
106
|
|
|
105
|
-
fn
|
|
106
|
-
self.map(|m| m.
|
|
107
|
+
fn interned_names(self) -> impl Iterator<Item = DefaultSymbol> {
|
|
108
|
+
self.map(|m| m.interned_name)
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
fn
|
|
111
|
+
fn names(self) -> impl Iterator<Item = String> {
|
|
110
112
|
let interner = MODULE_NAMES.read().unwrap();
|
|
111
|
-
self.map(move |m| interner.resolve(m.
|
|
113
|
+
self.map(move |m| interner.resolve(m.interned_name).unwrap().to_owned())
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
fn visible(self) -> impl ModuleIterator<'a> {
|
|
@@ -129,9 +131,18 @@ impl<'a, T: Iterator<Item = &'a ModuleToken>> ModuleTokenIterator<'a> for T {}
|
|
|
129
131
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, new, Getters, CopyGetters)]
|
|
130
132
|
pub struct ImportDetails {
|
|
131
133
|
#[getset(get_copy = "pub")]
|
|
132
|
-
line_number:
|
|
134
|
+
line_number: u32,
|
|
133
135
|
|
|
134
|
-
#[
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
#[getset(get_copy = "pub")]
|
|
137
|
+
interned_line_contents: DefaultSymbol,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
impl ImportDetails {
|
|
141
|
+
pub fn line_contents(&self) -> String {
|
|
142
|
+
let interner = IMPORT_LINE_CONTENTS.read().unwrap();
|
|
143
|
+
interner
|
|
144
|
+
.resolve(self.interned_line_contents)
|
|
145
|
+
.unwrap()
|
|
146
|
+
.to_owned()
|
|
147
|
+
}
|
|
137
148
|
}
|
|
@@ -28,7 +28,8 @@ pub fn find_reach(
|
|
|
28
28
|
&seen.into_iter().collect::<FxHashSet<_>>() - from_modules
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
/// Finds the shortest path, via a bidirectional BFS.
|
|
32
|
+
pub fn find_shortest_path(
|
|
32
33
|
graph: &Graph,
|
|
33
34
|
from_modules: &FxHashSet<ModuleToken>,
|
|
34
35
|
to_modules: &FxHashSet<ModuleToken>,
|