1use super::validate::{canonicalized, compute_graph_digest, validate_lock};
2use crate::{
3 CanonicalPackageId, ContentDigest, DependencyGroup, HostCapability, LockError, PackageAlias,
4 PackageInstanceId, TargetPredicate,
5};
6use serde::{Deserialize, Serialize};
7use std::collections::BTreeSet;
8
9pub const LOCK_SCHEMA_VERSION: u32 = 1;
10pub const RESOLVER_FORMAT_VERSION: &str = "1";
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(deny_unknown_fields)]
14pub struct RootLock {
15 pub manifest_digest: ContentDigest,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub package: Option<CanonicalPackageId>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21#[serde(deny_unknown_fields)]
22pub struct LockSelection {
23 pub target: String,
24 pub groups: BTreeSet<DependencyGroup>,
25 pub root_features: BTreeSet<String>,
26 pub host_capabilities: BTreeSet<HostCapability>,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30#[serde(deny_unknown_fields)]
31pub struct LockedPackage {
32 pub instance: PackageInstanceId,
33 pub features: BTreeSet<String>,
34 pub required_capabilities: BTreeSet<HostCapability>,
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub runmat_version: Option<String>,
37 pub singleton: bool,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41#[serde(deny_unknown_fields)]
42pub struct LockedEdge {
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub from: Option<ContentDigest>,
45 pub alias: PackageAlias,
46 pub to: ContentDigest,
47 pub group: DependencyGroup,
48 pub optional: bool,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub target: Option<TargetPredicate>,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(deny_unknown_fields)]
55pub struct PackageLock {
56 pub schema_version: u32,
57 pub resolver_version: String,
58 pub root: RootLock,
59 pub selection: LockSelection,
60 pub packages: Vec<LockedPackage>,
61 pub edges: Vec<LockedEdge>,
62 pub graph_digest: ContentDigest,
63}
64
65impl PackageLock {
66 pub fn from_graph(
67 graph: &crate::PackageGraph,
68 selection: LockSelection,
69 ) -> Result<Self, LockError> {
70 Self::from_graph_with_features(graph, selection, &Default::default())
71 }
72
73 pub fn from_graph_with_features(
74 graph: &crate::PackageGraph,
75 selection: LockSelection,
76 features: &std::collections::BTreeMap<ContentDigest, BTreeSet<String>>,
77 ) -> Result<Self, LockError> {
78 let root = graph.packages.get(&graph.root).ok_or_else(|| {
79 LockError::Invalid("package graph root instance is missing".to_string())
80 })?;
81 let manifest_digest = match &root.instance.source {
82 crate::SourceId::Path(source) => source.manifest_digest.clone(),
83 _ => {
84 return Err(LockError::Invalid(
85 "root package must currently use a path source".to_string(),
86 ));
87 }
88 };
89 let packages = graph
90 .packages
91 .iter()
92 .filter(|(identity, _)| *identity != &graph.root)
93 .map(|(identity, package)| LockedPackage {
94 instance: package.instance.clone(),
95 features: features.get(identity).cloned().unwrap_or_default(),
96 required_capabilities: package.required_capabilities.clone(),
97 runmat_version: None,
98 singleton: package.singleton,
99 })
100 .collect();
101 let edges = graph
102 .edges
103 .iter()
104 .map(|edge| LockedEdge {
105 from: (edge.from != graph.root).then(|| edge.from.clone()),
106 alias: edge.alias.clone(),
107 to: edge.to.clone(),
108 group: edge.group,
109 optional: edge.optional,
110 target: edge.target.clone(),
111 })
112 .collect();
113 Self::new(
114 RootLock {
115 manifest_digest,
116 package: Some(root.instance.package.clone()),
117 },
118 selection,
119 packages,
120 edges,
121 )
122 }
123
124 pub fn new(
125 root: RootLock,
126 selection: LockSelection,
127 packages: Vec<LockedPackage>,
128 edges: Vec<LockedEdge>,
129 ) -> Result<Self, LockError> {
130 let mut lock = Self {
131 schema_version: LOCK_SCHEMA_VERSION,
132 resolver_version: RESOLVER_FORMAT_VERSION.to_string(),
133 root,
134 selection,
135 packages,
136 edges,
137 graph_digest: ContentDigest::sha256([]),
138 };
139 lock = canonicalized(lock);
140 lock.graph_digest = compute_graph_digest(&lock)?;
141 validate_lock(&lock)?;
142 Ok(lock)
143 }
144
145 pub fn validate(&self) -> Result<(), LockError> {
146 validate_lock(self)
147 }
148}