greentic_component/
provenance.rs1use 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});