Skip to main content

treetop_bundle/
builder.rs

1use crate::validation::compile_manifest;
2use crate::{
3    BundleArchive, BundleError, BundleManifest, DiagnosticSeverity, PolicyCheck, Result, SigningKey,
4};
5use std::path::Path;
6
7/// Compiler entry point for an organization bundle manifest.
8pub struct BundleBuilder {
9    manifest: BundleManifest,
10    deny_warnings: bool,
11}
12
13impl BundleBuilder {
14    pub fn from_manifest(path: impl AsRef<Path>) -> Result<Self> {
15        Ok(Self {
16            manifest: BundleManifest::from_path(path)?,
17            deny_warnings: false,
18        })
19    }
20
21    pub fn manifest(&self) -> &BundleManifest {
22        &self.manifest
23    }
24
25    pub fn deny_warnings(mut self, deny: bool) -> Self {
26        self.deny_warnings = deny;
27        self
28    }
29
30    /// Validate the complete source bundle without emitting an archive.
31    pub fn check(&self) -> Result<PolicyCheck> {
32        let parts = compile_manifest(&self.manifest)?;
33        if self.deny_warnings
34            && parts
35                .diagnostics
36                .iter()
37                .any(|diagnostic| diagnostic.severity == DiagnosticSeverity::Warning)
38        {
39            return Err(BundleError::Validation(parts.diagnostics));
40        }
41        Ok(PolicyCheck {
42            diagnostics: parts.diagnostics,
43        })
44    }
45
46    /// Validate and compile a deterministic archive, optionally signing it.
47    pub fn build(&self, signing_key: Option<&SigningKey>) -> Result<BundleArchive> {
48        let parts = compile_manifest(&self.manifest)?;
49        if self.deny_warnings
50            && parts
51                .diagnostics
52                .iter()
53                .any(|diagnostic| diagnostic.severity == DiagnosticSeverity::Warning)
54        {
55            return Err(BundleError::Validation(parts.diagnostics));
56        }
57        BundleArchive::build(parts, signing_key)
58    }
59}