Skip to main content

runmat_package/source/
vendor.rs

1use 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}