greentic_component/
provenance.rs

1use once_cell::sync::Lazy;
2use regex::Regex;
3use serde::{Deserialize, Serialize};
4use thiserror::Error;
5use time::OffsetDateTime;
6
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
8pub struct Provenance {
9    pub builder: String,
10    pub git_commit: String,
11    pub toolchain: String,
12    #[serde(with = "time::serde::rfc3339")]
13    pub built_at_utc: OffsetDateTime,
14}
15
16impl Provenance {
17    pub fn validate(&self) -> Result<(), ProvenanceError> {
18        if self.builder.trim().is_empty() {
19            return Err(ProvenanceError::EmptyField("builder"));
20        }
21        if self.toolchain.trim().is_empty() {
22            return Err(ProvenanceError::EmptyField("toolchain"));
23        }
24        if !GIT_COMMIT_RE.is_match(&self.git_commit) {
25            return Err(ProvenanceError::InvalidGit(self.git_commit.clone()));
26        }
27        Ok(())
28    }
29}
30
31#[derive(Debug, Error)]
32pub enum ProvenanceError {
33    #[error("provenance field `{0}` cannot be empty")]
34    EmptyField(&'static str),
35    #[error("git commit `{0}` must be lowercase hex (min 7 chars)")]
36    InvalidGit(String),
37}
38
39static GIT_COMMIT_RE: Lazy<Regex> = Lazy::new(|| {
40    Regex::new(r"^[0-9a-f]{7,40}$").expect("git commit regex compile should never fail")
41});