Skip to main content

pedant_core/resolution/rust/
project.rs

1//! The project view: every package, target, and dependency beneath one
2//! canonical repository root.
3
4use std::path::Path;
5
6use super::dependency::RustDependency;
7use super::error::RustProjectError;
8use super::identity::{PackageId, ProjectAuthority, TargetId};
9use super::limits::ResolutionLimits;
10use super::load;
11use super::manifest::ManifestFingerprint;
12use super::package::RustPackage;
13use super::snapshot::{
14    RustPackageSnapshot, RustPackageSnapshotError, RustResolutionSnapshot, RustSnapshotError,
15    RustTargetSnapshot, build_package_snapshot, build_resolution_snapshot, build_target_snapshot,
16};
17use super::target::RustTarget;
18
19/// A factual Cargo project index rooted at one canonical repository root.
20///
21/// Views are borrowed and deterministically ordered by stable structural keys,
22/// so reordering filesystem enumeration cannot change what a caller reads.
23#[derive(Debug)]
24pub struct RustProject {
25    pub(super) root: Box<Path>,
26    pub(super) limits: ResolutionLimits,
27    pub(super) authority: ProjectAuthority,
28    pub(super) manifests: Box<[ManifestFingerprint]>,
29    pub(super) packages: Box<[RustPackage]>,
30    pub(super) targets: Box<[RustTarget]>,
31    pub(super) dependencies: Box<[RustDependency]>,
32}
33
34impl RustProject {
35    /// Read every manifest beneath `root` and index the project it declares.
36    ///
37    /// Reads `Cargo.toml` files and directory listings only: no Rust source is
38    /// opened and Cargo is never invoked.
39    pub fn load(root: &Path, limits: ResolutionLimits) -> Result<Self, RustProjectError> {
40        load::load_project(root, limits)
41    }
42
43    /// The canonical repository root this project was loaded from.
44    pub fn root(&self) -> &Path {
45        &self.root
46    }
47
48    /// The limits this project was loaded under and retains for snapshots.
49    pub fn limits(&self) -> ResolutionLimits {
50        self.limits
51    }
52
53    /// Every package in the project, workspace members and eligible in-root
54    /// path dependencies alike, ordered by manifest path.
55    pub fn packages(&self) -> &[RustPackage] {
56        &self.packages
57    }
58
59    /// Only the packages the workspace declares as members.
60    pub fn workspace_members(&self) -> impl Iterator<Item = &RustPackage> {
61        self.packages
62            .iter()
63            .filter(|package| package.is_workspace_member())
64    }
65
66    /// The package an identity issued by this project selects.
67    pub fn package(&self, id: PackageId) -> Option<&RustPackage> {
68        self.select(id.authority(), id.index())
69            .and_then(|index| self.packages.get(index))
70    }
71
72    /// Every target in the project, ordered by package, kind, then name.
73    pub fn targets(&self) -> &[RustTarget] {
74        &self.targets
75    }
76
77    /// The target an identity issued by this project selects.
78    pub fn target(&self, id: TargetId) -> Option<&RustTarget> {
79        self.select(id.authority(), id.index())
80            .and_then(|index| self.targets.get(index))
81    }
82
83    /// Every target one package declares.
84    pub fn package_targets(&self, package: PackageId) -> impl Iterator<Item = &RustTarget> {
85        self.targets
86            .iter()
87            .filter(move |target| target.package() == package)
88    }
89
90    /// Every dependency edge in the project, ordered by package, kind, then
91    /// local dependency name.
92    pub fn dependencies(&self) -> &[RustDependency] {
93        &self.dependencies
94    }
95
96    /// Snapshot only this target's Rust module closure.
97    ///
98    /// The target's authority is validated before any source is read, and the
99    /// closure is all-or-error: a caller receives every reachable source or a
100    /// typed failure, never a partial set.
101    pub fn snapshot_target(
102        &self,
103        target: TargetId,
104    ) -> Result<RustTargetSnapshot, RustSnapshotError> {
105        build_target_snapshot(self, target)
106    }
107
108    /// Snapshot every library, binary, and build-script target of one package.
109    ///
110    /// Target closures remain separate views, but all of them share one source
111    /// store so an overlapping source is read, hashed, and parsed once. Any
112    /// incomplete target refuses the entire package snapshot.
113    pub fn snapshot_package_primary_targets(
114        &self,
115        package: PackageId,
116    ) -> Result<RustPackageSnapshot, RustPackageSnapshotError> {
117        build_package_snapshot(self, package)
118    }
119
120    /// Snapshot this target together with its same-package library, when Cargo
121    /// exposes one, and the in-repository dependency libraries its namespace
122    /// resolves names against.
123    pub fn snapshot_resolution(
124        &self,
125        target: TargetId,
126    ) -> Result<RustResolutionSnapshot, RustSnapshotError> {
127        build_resolution_snapshot(self, target)
128    }
129
130    /// Every dependency edge one package declares.
131    pub fn package_dependencies(
132        &self,
133        package: PackageId,
134    ) -> impl Iterator<Item = &RustDependency> {
135        self.dependencies
136            .iter()
137            .filter(move |dependency| dependency.source() == package)
138    }
139
140    /// Reject an identity issued by another project before it selects a record.
141    fn select(&self, authority: ProjectAuthority, index: u32) -> Option<usize> {
142        match authority == self.authority {
143            true => usize::try_from(index).ok(),
144            false => None,
145        }
146    }
147}