greentic-pack 0.4.115

Greentic pack builder CLI
Documentation
use crate::flows::FlowAsset;
use crate::manifest::SpecBundle;
use crate::templates::TemplateAsset;
use serde::Serialize;
use time::OffsetDateTime;
use time::format_description::well_known::Rfc3339;

#[derive(Debug, Serialize)]
pub struct CycloneDxBom {
    #[serde(rename = "bomFormat")]
    pub bom_format: &'static str,
    #[serde(rename = "specVersion")]
    pub spec_version: &'static str,
    pub version: u32,
    pub metadata: Metadata,
    pub components: Vec<Component>,
}

#[derive(Debug, Serialize)]
pub struct Metadata {
    pub timestamp: String,
    pub component: ComponentSummary,
}

#[derive(Debug, Serialize)]
pub struct ComponentSummary {
    pub name: String,
    pub version: String,
    #[serde(rename = "type")]
    pub component_type: &'static str,
}

#[derive(Debug, Serialize)]
pub struct Component {
    pub name: String,
    #[serde(rename = "type")]
    pub component_type: &'static str,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub hashes: Option<Vec<HashEntry>>,
}

#[derive(Debug, Serialize)]
pub struct HashEntry {
    pub alg: &'static str,
    pub content: String,
}

pub fn generate(
    spec: &SpecBundle,
    flows: &[FlowAsset],
    templates: &[TemplateAsset],
) -> CycloneDxBom {
    let timestamp = OffsetDateTime::now_utc()
        .format(&Rfc3339)
        .unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_string());

    let mut components = Vec::new();
    for flow in flows {
        components.push(Component {
            name: flow.bundle.id.clone(),
            component_type: "file",
            version: None,
            hashes: Some(vec![HashEntry {
                alg: "SHA-256",
                content: flow.sha256.clone(),
            }]),
        });
    }

    for template in templates {
        components.push(Component {
            name: template.logical_path.clone(),
            component_type: "file",
            version: None,
            hashes: Some(vec![HashEntry {
                alg: "SHA-256",
                content: template.sha256.clone(),
            }]),
        });
    }

    CycloneDxBom {
        bom_format: "CycloneDX",
        spec_version: "1.5",
        version: 1,
        metadata: Metadata {
            timestamp,
            component: ComponentSummary {
                name: spec.spec.id.clone(),
                version: spec.spec.version.clone(),
                component_type: "application",
            },
        },
        components,
    }
}