1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! The project view: the main module, every admitted local replacement, and
//! every directive the root manifest states.
use std::path::Path;
use super::exclusion::GoExclusion;
use super::identity::{GoModuleId, ProjectAuthority, index_of};
use super::limits::GoResolutionLimits;
use super::module::GoModule;
use super::replacement::GoReplacement;
use super::requirement::GoRequirement;
use super::snapshot::GoResolutionSnapshot;
use super::snapshot_error::GoSnapshotError;
use super::{error::GoProjectError, load, snapshot};
/// A factual Go module project rooted at one canonical repository root.
///
/// Views are borrowed and deterministically ordered by stable structural keys,
/// so reordering filesystem enumeration cannot change what a caller reads. The
/// main module is a field rather than the first element of a list, because a
/// loaded project always has exactly one and no caller should have to ask.
#[derive(Debug)]
pub struct GoProject {
pub(super) root: Box<Path>,
pub(super) limits: GoResolutionLimits,
pub(super) authority: ProjectAuthority,
pub(super) main: GoModule,
pub(super) required: Box<[GoModule]>,
pub(super) requirements: Box<[GoRequirement]>,
pub(super) replacements: Box<[GoReplacement]>,
pub(super) exclusions: Box<[GoExclusion]>,
}
impl GoProject {
/// Read the `go.mod` at `root` and index the module project it declares.
///
/// Reads `go.mod` files only: no Go source is opened, and no process,
/// toolchain, code generator, or network client is invoked.
pub fn load(root: &Path, limits: GoResolutionLimits) -> Result<Self, GoProjectError> {
load::load_project(root, limits)
}
/// Walk every admitted module's packages and snapshot what they state.
///
/// Reads the `.go` sources the walk admits and nothing else: no process,
/// toolchain, code generator, or network client is invoked, and no
/// environment or host selection state decides which sources exist.
pub fn snapshot_resolution(&self) -> Result<GoResolutionSnapshot, GoSnapshotError> {
snapshot::build(self)
}
/// The canonical repository root this project was loaded from.
pub fn root(&self) -> &Path {
&self.root
}
/// The limits this project was loaded under and retains for snapshots.
pub fn limits(&self) -> GoResolutionLimits {
self.limits
}
/// The main module, which the root `go.mod` declares.
pub fn root_module(&self) -> &GoModule {
&self.main
}
/// Every admitted module: the main module first, then each local
/// replacement in admission order.
pub fn modules(&self) -> impl Iterator<Item = &GoModule> {
std::iter::once(&self.main).chain(self.required.iter())
}
/// The module an identity issued by this project selects.
pub fn module(&self, id: GoModuleId) -> Option<&GoModule> {
let index = self.select(id)?;
match index.checked_sub(1) {
None => Some(&self.main),
Some(offset) => self.required.get(offset),
}
}
/// Every requirement every admitted module declares, ordered by declaring
/// module and then by required path and version.
pub fn requirements(&self) -> &[GoRequirement] {
&self.requirements
}
/// Every requirement one admitted module declares.
pub fn module_requirements(&self, module: GoModuleId) -> impl Iterator<Item = &GoRequirement> {
self.requirements
.iter()
.filter(move |requirement| requirement.module() == module)
}
/// Every `replace` directive the root manifest states, ordered by replaced
/// path and version, together with whether a requirement selected it.
pub fn replacements(&self) -> &[GoReplacement] {
&self.replacements
}
/// Every `exclude` directive the root manifest states, ordered by excluded
/// path and version.
pub fn exclusions(&self) -> &[GoExclusion] {
&self.exclusions
}
/// Reject an identity issued by another project before it selects a record.
fn select(&self, id: GoModuleId) -> Option<usize> {
match id.authority() == self.authority {
true => Some(index_of(id.index())),
false => None,
}
}
}