Skip to content

Commit 548fe8a

Browse files
committed
Consolidate stale-related utils in oak_scan
1 parent c2f6d54 commit 548fe8a

11 files changed

Lines changed: 115 additions & 102 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/oak_db/src/db.rs

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,6 @@ pub trait Db: DbInputs {
6060
/// (mirroring `.libPaths()`).
6161
fn package_by_name(&self, name: &str) -> Option<Package>;
6262

63-
/// Look up a `Package` by its `DESCRIPTION` URL.
64-
///
65-
/// Walks workspace packages, then library packages, then falls back
66-
/// to [`StaleRoot`]. Stale matches are intentional: scanner upserts
67-
/// use this to find a `Package` entity whose live container was
68-
/// dropped on a previous `set_*_paths` call, so the entity gets
69-
/// reused on re-add. Analysis paths should not call this — they use
70-
/// [`Db::package_by_name`] which is stale-blind.
71-
fn package_by_url(&self, url: &UrlId) -> Option<Package>;
72-
7363
/// Resolve the live `Root` that contains `pkg`, if any.
7464
///
7565
/// Returns `None` when the package is only in [`StaleRoot`] (its live
@@ -147,19 +137,6 @@ pub fn package_by_name_query(db: &dyn Db, name: &str) -> Option<Package> {
147137
None
148138
}
149139

150-
/// Implementation of [`Db::package_by_url`]. Walks live roots' packages
151-
/// by `description_url`, then falls back to the stale bucket.
152-
pub fn package_by_url_query(db: &dyn Db, url: &UrlId) -> Option<Package> {
153-
for &root in db.live_roots() {
154-
if let LiveRoot::Workspace(r) | LiveRoot::Library(r) = root {
155-
if let Some(&pkg) = root_package_url_index(db, r).get(url) {
156-
return Some(pkg);
157-
}
158-
}
159-
}
160-
stale_package_url_index(db).get(url).copied()
161-
}
162-
163140
/// Implementation of [`Db::root_by_package`]. Walks all live roots looking for
164141
/// `pkg` in their `packages` vec, picking the longest-path root on ties.
165142
pub fn root_by_package_query(db: &dyn Db, pkg: Package) -> Option<Root> {
@@ -232,45 +209,3 @@ fn root_package_index(db: &dyn Db, root: Root) -> FxHashMap<String, Package> {
232209
}
233210
map
234211
}
235-
236-
/// Per-root DESCRIPTION URL -> Package index. Used by
237-
/// [`package_by_url_query`] for entity-reuse lookups across rescans;
238-
/// salsa cache invalidates only when this root's packages change.
239-
#[salsa::tracked(returns(ref))]
240-
fn root_package_url_index(db: &dyn Db, root: Root) -> FxHashMap<UrlId, Package> {
241-
let mut map = FxHashMap::default();
242-
for &pkg in root.packages(db) {
243-
map.insert(pkg.description_url(db).clone(), pkg);
244-
}
245-
map
246-
}
247-
248-
/// Stale file URL -> File index. Reads only `stale_root().files`. Not
249-
/// consulted by [`file_by_url_query`] — analysis is stale-blind by
250-
/// design. Scanner upserts use [`stale_file_by_url`] when re-adding a
251-
/// path.
252-
#[salsa::tracked(returns(ref))]
253-
fn stale_url_index(db: &dyn Db) -> FxHashMap<UrlId, File> {
254-
let mut map = FxHashMap::default();
255-
for &file in db.stale_root().files(db) {
256-
map.insert(file.url(db).clone(), file);
257-
}
258-
map
259-
}
260-
261-
/// Look up a stale `File` by URL. Public so scanner upsert helpers in
262-
/// `oak_scan` can fall back to stale after [`Db::file_by_url`] misses.
263-
pub fn stale_file_by_url(db: &dyn Db, url: &UrlId) -> Option<File> {
264-
stale_url_index(db).get(url).copied()
265-
}
266-
267-
/// Stale DESCRIPTION URL -> Package index. Same role as
268-
/// [`stale_url_index`] for packages.
269-
#[salsa::tracked(returns(ref))]
270-
fn stale_package_url_index(db: &dyn Db) -> FxHashMap<UrlId, Package> {
271-
let mut map = FxHashMap::default();
272-
for &pkg in db.stale_root().packages(db) {
273-
map.insert(pkg.description_url(db).clone(), pkg);
274-
}
275-
map
276-
}

crates/oak_db/src/inputs.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,9 @@ impl OrphanRoot {
150150
/// completions, goto-def, etc. — they correspond to folders the user
151151
/// has explicitly removed.
152152
///
153-
/// **Consulted by scanners.** `Db::package_by_url` walks live roots
154-
/// then falls back to stale. Scanner upsert helpers do the same for
155-
/// files. On reuse, the entity is moved out of stale back into a live
153+
/// **Consulted by scanners.** The scanner's package-by-URL lookup walks
154+
/// live roots then falls back to stale. Scanner upsert helpers do the same
155+
/// for files. On reuse, the entity is moved out of stale back into a live
156156
/// container.
157157
///
158158
/// Singleton like `OrphanRoot`. The `files` and `packages` fields are
@@ -177,9 +177,9 @@ impl StaleRoot {
177177
pub struct Package {
178178
/// URL of the package's `DESCRIPTION` file. Stable identity across
179179
/// rescans and workspace / library churn: scanners look up an
180-
/// existing `Package` by this URL before creating a new one (see
181-
/// [`Db::package_by_url`]). Two packages with the same `Package:`
182-
/// name can coexist on disk and the URL distinguishes them.
180+
/// existing `Package` by this URL before creating a new one. Two
181+
/// packages with the same `Package:` name can coexist on disk and the
182+
/// URL distinguishes them.
183183
///
184184
/// The package's owning [`Root`] is not stored as a field. It is
185185
/// derived from live-graph containment via [`Db::root_by_package`]: a

crates/oak_db/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ mod storage;
1313
#[cfg(test)]
1414
mod tests;
1515

16-
pub use db::stale_file_by_url;
1716
pub use db::Db;
1817
pub use db::DbInputs;
1918
pub use definition::Definition;

crates/oak_db/src/storage.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,6 @@ impl Db for OakDatabase {
6262
crate::db::package_by_name_query(self, name)
6363
}
6464

65-
fn package_by_url(&self, url: &aether_url::UrlId) -> Option<crate::Package> {
66-
crate::db::package_by_url_query(self, url)
67-
}
68-
6965
fn root_by_package(&self, pkg: crate::Package) -> Option<crate::Root> {
7066
crate::db::root_by_package_query(self, pkg)
7167
}

crates/oak_db/src/tests/test_db.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,6 @@ impl Db for TestDb {
108108
crate::db::package_by_name_query(self, name)
109109
}
110110

111-
fn package_by_url(&self, url: &UrlId) -> Option<crate::Package> {
112-
crate::db::package_by_url_query(self, url)
113-
}
114-
115111
fn root_by_package(&self, pkg: crate::Package) -> Option<crate::Root> {
116112
crate::db::root_by_package_query(self, pkg)
117113
}

crates/oak_scan/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ log.workspace = true
2121
oak_core.workspace = true
2222
oak_db.workspace = true
2323
oak_package_metadata.workspace = true
24+
rustc-hash.workspace = true
2425
salsa.workspace = true
2526
stdext.workspace = true
2627
url.workspace = true

crates/oak_scan/src/inputs.rs

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ use std::collections::HashSet;
2020
use std::path::PathBuf;
2121

2222
use aether_url::UrlId;
23-
use oak_db::stale_file_by_url;
2423
use oak_db::Db;
2524
use oak_db::DbInputs;
2625
use oak_db::File;
@@ -29,6 +28,11 @@ use oak_db::Root;
2928
use oak_package_metadata::namespace::Namespace;
3029
use salsa::Setter;
3130

31+
use crate::lookup::package_by_url;
32+
use crate::stale::remove_from_stale_files;
33+
use crate::stale::remove_from_stale_packages;
34+
use crate::stale::stale_file_by_url;
35+
3236
/// Description of one R file the scanner wants to register.
3337
///
3438
/// `contents` is the on-disk snapshot at scan time. It's used as the
@@ -132,7 +136,7 @@ impl RootExt for Root {
132136
// in `self.packages` (rescan, common path) or in
133137
// `stale_root.packages` (resurrection after a previous eviction).
134138
// Either way we reuse the entity and refresh its metadata fields.
135-
let pkg = match db.package_by_url(&description_url) {
139+
let pkg = match package_by_url(db, &description_url) {
136140
Some(p) => {
137141
p.set_name(db).to(name);
138142
p.set_version(db).to(version);
@@ -222,23 +226,3 @@ fn remove_from_orphan<DB: Db + DbInputs>(db: &mut DB, file: File) {
222226
files.retain(|f| *f != file);
223227
orphan.set_files(db).to(files);
224228
}
225-
226-
fn remove_from_stale_files<DB: Db + DbInputs>(db: &mut DB, file: File) {
227-
let stale = db.stale_root();
228-
if !stale.files(db).contains(&file) {
229-
return;
230-
}
231-
let mut files = stale.files(db).clone();
232-
files.retain(|f| *f != file);
233-
stale.set_files(db).to(files);
234-
}
235-
236-
fn remove_from_stale_packages<DB: Db + DbInputs>(db: &mut DB, pkg: Package) {
237-
let stale = db.stale_root();
238-
if !stale.packages(db).contains(&pkg) {
239-
return;
240-
}
241-
let mut packages = stale.packages(db).clone();
242-
packages.retain(|p| *p != pkg);
243-
stale.set_packages(db).to(packages);
244-
}

crates/oak_scan/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
2525
mod inputs;
2626
mod library;
27+
mod lookup;
2728
mod packages;
2829
mod stale;
2930

crates/oak_scan/src/lookup.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//! Package lookup by `DESCRIPTION` URL.
2+
//!
3+
//! `oak_db` exposes `file_by_url` and `package_by_name` for analysis, but no
4+
//! by-URL package lookup. Analysis never needs one: it keys packages by name
5+
//! and is stale-blind. The scanner does need one, to find an existing
6+
//! `Package` entity to reuse across rescans and eviction cycles. So the lookup
7+
//! lives here on the write side, next to its only caller, rather than on
8+
//! `oak_db`'s public `Db` trait.
9+
10+
use aether_url::UrlId;
11+
use oak_db::Db;
12+
use oak_db::LiveRoot;
13+
use oak_db::Package;
14+
use oak_db::Root;
15+
use rustc_hash::FxHashMap;
16+
17+
use crate::stale::stale_package_url_index;
18+
19+
/// Find the `Package` registered at `url` (a `DESCRIPTION` URL), searching live
20+
/// roots first and the [`oak_db::StaleRoot`] eviction bucket second.
21+
///
22+
/// Live roots are walked in lookup order (workspace then library) and the first
23+
/// hit wins. A stale hit means the package's live container was dropped on an
24+
/// earlier `set_*_paths` call. The scanner reuses that entity and moves it back
25+
/// into a live container, which is why this lookup deliberately sees stale
26+
/// packages where analysis (via `oak_db::Db::package_by_name`) does not.
27+
pub(crate) fn package_by_url(db: &dyn Db, url: &UrlId) -> Option<Package> {
28+
for &root in db.live_roots() {
29+
if let LiveRoot::Workspace(r) | LiveRoot::Library(r) = root {
30+
if let Some(&pkg) = root_package_url_index(db, r).get(url) {
31+
return Some(pkg);
32+
}
33+
}
34+
}
35+
stale_package_url_index(db).get(url).copied()
36+
}
37+
38+
/// Per-root DESCRIPTION URL -> Package index. Salsa caches one map per `Root`
39+
/// and invalidates only when that root's packages change.
40+
#[salsa::tracked(returns(ref))]
41+
fn root_package_url_index(db: &dyn Db, root: Root) -> FxHashMap<UrlId, Package> {
42+
let mut map = FxHashMap::default();
43+
for &pkg in root.packages(db) {
44+
map.insert(pkg.description_url(db).clone(), pkg);
45+
}
46+
map
47+
}

0 commit comments

Comments
 (0)