Skip to content

Commit 731eb41

Browse files
committed
Move ResolutionResult to core
1 parent fe0681c commit 731eb41

File tree

13 files changed

+188
-160
lines changed

13 files changed

+188
-160
lines changed

crates/pcb-zen-core/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ pub trait FileProvider: Send + Sync {
101101
/// Canonicalize a path (make it absolute)
102102
fn canonicalize(&self, path: &std::path::Path)
103103
-> Result<std::path::PathBuf, FileProviderError>;
104+
105+
/// Global package cache directory (e.g. `~/.pcb/cache`).
106+
/// Returns empty path by default (WASM / in-memory providers).
107+
fn cache_dir(&self) -> std::path::PathBuf {
108+
std::path::PathBuf::new()
109+
}
104110
}
105111

106112
/// Blanket implementation of FileProvider for Arc<T> where T: FileProvider
@@ -134,6 +140,10 @@ impl<T: FileProvider + ?Sized> FileProvider for Arc<T> {
134140
) -> Result<std::path::PathBuf, FileProviderError> {
135141
(**self).canonicalize(path)
136142
}
143+
144+
fn cache_dir(&self) -> std::path::PathBuf {
145+
(**self).cache_dir()
146+
}
137147
}
138148

139149
#[derive(Debug, Clone, thiserror::Error)]
@@ -281,6 +291,12 @@ impl FileProvider for DefaultFileProvider {
281291

282292
result
283293
}
294+
295+
fn cache_dir(&self) -> std::path::PathBuf {
296+
dirs::home_dir()
297+
.expect("Cannot determine home directory")
298+
.join(".pcb/cache")
299+
}
284300
}
285301

286302
/// Information about a package alias including its target and source

crates/pcb-zen-core/src/resolution.rs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! - Native: checks patches, vendor/, then ~/.pcb/cache
99
//! - WASM: only checks vendor/ (everything must be pre-vendored)
1010
11-
use std::collections::{BTreeMap, HashMap};
11+
use std::collections::{BTreeMap, HashMap, HashSet};
1212
use std::path::{Path, PathBuf};
1313

1414
use semver::Version;
@@ -360,6 +360,92 @@ impl PackagePathResolver for NativePathResolver {
360360
}
361361
}
362362

363+
/// Result of dependency resolution.
364+
///
365+
/// This is a data-only type defined in core so it can be referenced by
366+
/// `EvalContext` / `EvalOutput`. Construction happens in `pcb-zen` which
367+
/// performs the actual resolution.
368+
#[derive(Debug, Clone)]
369+
pub struct ResolutionResult {
370+
/// Snapshot of workspace info at the time of resolution
371+
pub workspace_info: WorkspaceInfo,
372+
/// Map from Package Root (Absolute Path) -> Import URL -> Resolved Absolute Path
373+
pub package_resolutions: HashMap<PathBuf, BTreeMap<String, PathBuf>>,
374+
/// Package dependencies in the build closure: ModuleLine -> Version
375+
pub closure: HashMap<ModuleLine, Version>,
376+
/// Asset dependencies: (module_path, ref) -> resolved_path
377+
pub assets: HashMap<(String, String), PathBuf>,
378+
/// Whether the lockfile (pcb.sum) was updated during resolution
379+
pub lockfile_changed: bool,
380+
}
381+
382+
/// Transitive dependency closure for a package
383+
#[derive(Debug, Clone, Default)]
384+
pub struct PackageClosure {
385+
pub local_packages: HashSet<String>,
386+
pub remote_packages: HashSet<(String, String)>,
387+
pub assets: HashSet<(String, String)>,
388+
}
389+
390+
impl ResolutionResult {
391+
/// Compute the transitive dependency closure for a package.
392+
pub fn package_closure(&self, package_url: &str) -> PackageClosure {
393+
let workspace_info = &self.workspace_info;
394+
let mut closure = PackageClosure::default();
395+
let mut visited: HashSet<String> = HashSet::new();
396+
let mut stack: Vec<String> = vec![package_url.to_string()];
397+
398+
let cache = &workspace_info.cache_dir;
399+
let vendor_base = workspace_info.root.join("vendor");
400+
401+
let get_pkg_root = |module_path: &str, version: &str| -> PathBuf {
402+
let vendor_path = vendor_base.join(module_path).join(version);
403+
if vendor_path.exists() {
404+
vendor_path
405+
} else {
406+
cache.join(module_path).join(version)
407+
}
408+
};
409+
410+
while let Some(url) = stack.pop() {
411+
if !visited.insert(url.clone()) {
412+
continue;
413+
}
414+
415+
if let Some(pkg) = workspace_info.packages.get(&url) {
416+
closure.local_packages.insert(url.clone());
417+
for dep_url in pkg.config.dependencies.keys() {
418+
stack.push(dep_url.clone());
419+
}
420+
for (asset_url, asset_spec) in &pkg.config.assets {
421+
if let Ok(ref_str) = crate::extract_asset_ref_strict(asset_spec) {
422+
closure.assets.insert((asset_url.clone(), ref_str));
423+
}
424+
}
425+
} else if let Some((line, version)) = self.closure.iter().find(|(l, _)| l.path == url) {
426+
let version_str = version.to_string();
427+
closure
428+
.remote_packages
429+
.insert((url.clone(), version_str.clone()));
430+
let pkg_root = get_pkg_root(&line.path, &version_str);
431+
if let Some(deps) = self.package_resolutions.get(&pkg_root) {
432+
for dep_url in deps.keys() {
433+
stack.push(dep_url.clone());
434+
}
435+
}
436+
}
437+
}
438+
439+
for (asset_path, asset_ref) in self.assets.keys() {
440+
closure
441+
.assets
442+
.insert((asset_path.clone(), asset_ref.clone()));
443+
}
444+
445+
closure
446+
}
447+
}
448+
363449
#[cfg(test)]
364450
mod tests {
365451
use super::*;
@@ -432,6 +518,7 @@ mod tests {
432518

433519
let workspace = WorkspaceInfo {
434520
root: workspace_root.clone(),
521+
cache_dir: PathBuf::new(),
435522
config: None,
436523
packages,
437524
lockfile: None,

crates/pcb-zen-core/src/workspace.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ pub struct DiscoveryError {
7878
pub struct WorkspaceInfo {
7979
/// Workspace root directory
8080
pub root: PathBuf,
81+
/// Global package cache directory (e.g. `~/.pcb/cache`).
82+
/// Set by native workspace discovery; empty on WASM.
83+
#[serde(skip)]
84+
pub cache_dir: PathBuf,
8185
/// Root pcb.toml config (if present)
8286
#[serde(skip_serializing_if = "Option::is_none")]
8387
pub config: Option<PcbToml>,
@@ -488,6 +492,7 @@ pub fn get_workspace_info<F: FileProvider>(
488492

489493
Ok(WorkspaceInfo {
490494
root: workspace_root,
495+
cache_dir: file_provider.cache_dir(),
491496
config,
492497
packages,
493498
lockfile,

crates/pcb-zen/src/cache_index.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use anyhow::{Context, Result};
44
use pcb_zen_core::config::Lockfile;
5+
use pcb_zen_core::FileProvider;
56
use r2d2::{Pool, PooledConnection};
67
use r2d2_sqlite::SqliteConnectionManager;
78
use rusqlite::{params, OptionalExtension};
@@ -318,9 +319,7 @@ fn index_path() -> PathBuf {
318319
}
319320

320321
pub fn cache_base() -> PathBuf {
321-
dirs::home_dir()
322-
.expect("Cannot determine home directory")
323-
.join(".pcb/cache")
322+
pcb_zen_core::DefaultFileProvider::new().cache_dir()
324323
}
325324

326325
/// Ensure the workspace cache symlink exists.

crates/pcb-zen/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,18 @@ use std::sync::Arc;
2020

2121
use pcb_sch::Schematic;
2222
use pcb_zen_core::config::find_workspace_root;
23+
use pcb_zen_core::resolution::ResolutionResult;
2324
use pcb_zen_core::FileProvider;
2425
use pcb_zen_core::{CoreLoadResolver, DefaultFileProvider, EvalContext, EvalOutput, LoadResolver};
2526

2627
pub use pcb_zen_core::file_extensions;
2728
pub use pcb_zen_core::{Diagnostic, Diagnostics, WithDiagnostics};
2829
pub use resolve::{
29-
copy_dir_all, ensure_sparse_checkout, resolve_dependencies, vendor_deps, ResolutionResult,
30+
copy_dir_all, ensure_sparse_checkout, print_dep_tree, resolve_dependencies, vendor_deps,
3031
VendorResult,
3132
};
3233
pub use starlark::errors::EvalSeverity;
33-
pub use workspace::{get_workspace_info, MemberPackage, PackageClosure, WorkspaceInfo};
34+
pub use workspace::{get_workspace_info, MemberPackage, WorkspaceInfo};
3435

3536
pub use tags::get_all_versions_for_repo;
3637

0 commit comments

Comments
 (0)