runmat_package/source/
vendor.rs1use crate::{ContentDigest, NormalizedRelativePath, SourceId};
2use serde::{Deserialize, Serialize};
3use std::collections::BTreeSet;
4
5pub const VENDOR_MANIFEST_FILENAME: &str = "runmat-vendor.json";
6pub const VENDOR_SCHEMA_VERSION: u32 = 1;
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(deny_unknown_fields)]
10pub struct VendorManifest {
11 pub schema_version: u32,
12 pub lock_digest: ContentDigest,
13 pub packages: Vec<VendoredPackage>,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(deny_unknown_fields)]
18pub struct VendoredPackage {
19 pub identity: ContentDigest,
20 pub source: SourceId,
21 pub path: NormalizedRelativePath,
22}
23
24impl VendorManifest {
25 pub fn new(
26 lock_digest: ContentDigest,
27 mut packages: Vec<VendoredPackage>,
28 ) -> Result<Self, String> {
29 packages.sort_by(|left, right| left.identity.cmp(&right.identity));
30 let manifest = Self {
31 schema_version: VENDOR_SCHEMA_VERSION,
32 lock_digest,
33 packages,
34 };
35 manifest.validate()?;
36 Ok(manifest)
37 }
38
39 pub fn validate(&self) -> Result<(), String> {
40 if self.schema_version != VENDOR_SCHEMA_VERSION {
41 return Err(format!(
42 "unsupported vendor schema {}; supported schema is {}",
43 self.schema_version, VENDOR_SCHEMA_VERSION
44 ));
45 }
46 if self
47 .packages
48 .windows(2)
49 .any(|pair| pair[0].identity >= pair[1].identity)
50 {
51 return Err(
52 "vendor packages must be strictly sorted by unique package identity".to_string(),
53 );
54 }
55 let mut paths = BTreeSet::new();
56 for package in &self.packages {
57 if !paths.insert(package.path.clone()) {
58 return Err(format!(
59 "vendor path `{}` is selected by multiple packages",
60 package.path
61 ));
62 }
63 }
64 Ok(())
65 }
66
67 pub fn package(&self, identity: &ContentDigest) -> Option<&VendoredPackage> {
68 self.packages
69 .binary_search_by(|package| package.identity.cmp(identity))
70 .ok()
71 .map(|index| &self.packages[index])
72 }
73}