Skip to main content

runmat_package/lock/
model.rs

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}