use anyhow::{Context, Result}; use serde::Deserialize; use std::collections::HashMap; use std::path::Path; #[derive(Debug, Deserialize)] struct PackageFile { package: HashMap, } #[derive(Debug, Deserialize, Clone)] pub struct Package { pub repo: String, pub asset_pattern: String, pub verification: Verification, } #[derive(Debug, Deserialize, Clone)] #[serde(tag = "method", rename_all = "kebab-case")] pub enum Verification { /// Tier 4: proves transport integrity only, not authorship. See /// SPEC.md > Verification trust tiers. SameOriginSha256 { checksum_asset_pattern: String }, /// Tier 2: GitHub build-provenance attestation, verified via `gh /// attestation verify` rather than reimplementing sigstore in Rust. GithubAttestation, } impl Verification { pub fn tier(&self) -> u8 { match self { Verification::SameOriginSha256 { .. } => 4, Verification::GithubAttestation => 2, } } } /// Loads every `*.toml` file in `dir` (the `packages.d/` layout from /// SPEC.md > Scaling to many packages), keyed by package name. pub fn load_packages_dir(dir: &Path) -> Result> { let mut out = Vec::new(); for entry in std::fs::read_dir(dir).with_context(|| format!("reading {}", dir.display()))? { let path = entry?.path(); if path.extension().and_then(|e| e.to_str()) != Some("toml") { continue; } let text = std::fs::read_to_string(&path) .with_context(|| format!("reading {}", path.display()))?; let file: PackageFile = toml::from_str(&text) .with_context(|| format!("parsing {}", path.display()))?; out.extend(file.package); } Ok(out) }