Skip to main content

oca_bundle/
controller.rs

1use std::io::Read;
2
3use log::info;
4use overlay_file::overlay_registry::OverlayRegistry;
5
6use crate::state::oca_bundle::{OCABundle, OCABundleModel, OCABundleWithRegistry};
7
8pub type GenericError = Box<dyn std::error::Error + Sync + Send>;
9pub type GenericResult<T> = Result<T, GenericError>;
10
11/// Loads an OCA Bundle JSON representation from a reader.
12/// validate created OCA Bundle Model with overlay definitions provided by overlay overlay_registry
13/// Return error or validated OCA Bundle Model
14pub fn load_oca(
15    source: &mut dyn Read,
16    overlay_registry: &overlay_file::overlay_registry::OverlayLocalRegistry,
17) -> GenericResult<OCABundleModel> {
18    let mut oca: OCABundle = serde_json::from_reader(source)?;
19
20    // fill overlay_def
21    for overlay in &mut oca.overlays {
22        let overlay_type = &overlay.model.name;
23        match overlay_registry.get_overlay(overlay_type) {
24            Ok(overlay_def) => {
25                overlay.model.overlay_def = Some(overlay_def.clone());
26            }
27            Err(e) => {
28                return Err(format!(
29                    "Failed to find overlay definition for {}: {}",
30                    overlay_type, e
31                )
32                .into());
33            }
34        }
35    }
36
37    let bundle_with_registry = OCABundleWithRegistry {
38        bundle: oca,
39        registry: overlay_registry.clone(),
40    };
41    // Convert OCABundle to OCABundleModel
42    let oca_bundle_model = OCABundleModel::from(bundle_with_registry);
43
44    match validate_bundle(&oca_bundle_model) {
45        Ok(()) => {
46            // When validation passes convert to Oca Bundle model
47            Ok(oca_bundle_model)
48        }
49        Err(e) => Err(e),
50    }
51}
52
53/// Validates an OCA bundle against provided overlay definitions.
54/// This should be called after `load_oca` when overlay definitions are available.
55fn validate_bundle(bundle: &OCABundleModel) -> GenericResult<()> {
56    let said = bundle.digest.clone();
57    let mut mut_bundle = bundle.clone();
58
59    // Validate each overlay against its definition
60    match mut_bundle.compute_and_fill_digest() {
61        Ok(_) => info!("Digest filled successfully"),
62        Err(e) => panic!("Failed to fill digest: {}", e),
63    }
64    if said == mut_bundle.digest {
65        Ok(())
66    } else {
67        Err("Digests do not match".into())
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use overlay_file::overlay_registry::OverlayLocalRegistry;
74
75    use super::load_oca;
76
77    #[test]
78    fn load_oca_bundle_json_from_str() {
79        let data = r#"
80{"v":"OCAS02JSON0009e3_","digest":"EP79WPhSehW5kVwy67UR-bJEoGMUWcN5cK99THUtnBIm","capture_base":{"digest":"EMDyoUr57UN7-Wy3kmF0WyG0xiQieckUdW18VGdEuve9","type":"capture_base/2.0.0","attributes":{"age":"Numeric","car":["refs:EJeWVGxkqxWrdGi0efOzwg1YQK8FrA-ZmtegiVEtAVcu"],"d":"Text","el":"Text","i":"Text","incidentals_spare_parts":[["refs:EJeWVGxkqxWrdGi0efOzwg1YQK8FrA-ZmtegiVEtAVcu"]],"list":["Text"],"name":"Text","passed":"Boolean"}},"overlays":[{"digest":"EEk6wQBfPuqddeVOPFLgSY9qv1ZorGCvip_oQtFdD9GV","capture_base":"EMDyoUr57UN7-Wy3kmF0WyG0xiQieckUdW18VGdEuve9","type":"overlay/meta/2.0.0","language":"en","description":"Entrance credential","name":"Entrance credential"},{"digest":"EPVOc4fR5Nwe2yHzFS-4wBf3kcm7C5D4XNjY9cxnFaQh","capture_base":"EMDyoUr57UN7-Wy3kmF0WyG0xiQieckUdW18VGdEuve9","type":"overlay/character_encoding/2.0.0","attribute_character_encodings":{"d":"utf-8","i":"utf-8","passed":"utf-8"}},{"digest":"EIHoDc5WM8Yxxhvqnc9348DL-OU1FCb9K5eXUuiISztT","capture_base":"EMDyoUr57UN7-Wy3kmF0WyG0xiQieckUdW18VGdEuve9","type":"overlay/conformance/2.0.0","attribute_conformances":{"d":"M","i":"M","passed":"M"}},{"digest":"EEy4mJ4SIxauAyk8FI1QqBa26qG1Fqn2uhN_Vf4RMIbL","capture_base":"EMDyoUr57UN7-Wy3kmF0WyG0xiQieckUdW18VGdEuve9","type":"overlay/label/2.0.0","language":"en","attribute_labels":{"d":"Schema digest","i":"Credential Issuee","passed":"Passed"}},{"digest":"EJi35V6qV5tUhnjDR3qiB2irAKLkbQVu-rU_hehkhop1","capture_base":"EMDyoUr57UN7-Wy3kmF0WyG0xiQieckUdW18VGdEuve9","type":"overlay/format/2.0.0","attribute_formats":{"d":"image/jpeg"}},{"digest":"EPdl6CuC9i9IszrkqvEkv9qZPM-WnX47DOD80dwGiHpL","capture_base":"EMDyoUr57UN7-Wy3kmF0WyG0xiQieckUdW18VGdEuve9","type":"overlay/unit/2.0.0","metric_system":"SI","attribute_units":{"i":"m^2","d":"°"}},{"digest":"EFbS7GQMBi_RCk2Q8cJKR2ohCE--248bH1OQnwiFzmer","capture_base":"EMDyoUr57UN7-Wy3kmF0WyG0xiQieckUdW18VGdEuve9","type":"overlay/cardinality/2.0.0","attribute_cardinalities":{"list":"1-2"}},{"digest":"ED6ktKLPYEmJfYTEo7-YR-xyPwHUgpEOdEwOe_Kr6c22","capture_base":"EMDyoUr57UN7-Wy3kmF0WyG0xiQieckUdW18VGdEuve9","type":"overlay/entry_code/2.0.0","attribute_entry_codes":{"list":"refs:EJeWVGxkqxWrdGi0efOzwg1YQK8FrA-ZmtegiVEtAVcu","el":["o1","o2","o3"]}},{"digest":"EIMaWbfJ98gO1sTucmYdgaZu_u94djMa75BYl8lzkvfc","capture_base":"EMDyoUr57UN7-Wy3kmF0WyG0xiQieckUdW18VGdEuve9","type":"overlay/entry/2.0.0","language":"en","attribute_entries":{"list":"refs:EJeWVGxkqxWrdGi0efOzwg1YQK8FrA-ZmtegiVEtAVcu","el":{"o1":"o1_label","o2":"o2_label","o3":"o3_label"}}}]}
81        "#;
82
83        let registry = OverlayLocalRegistry::from_dir("../overlay-file/core_overlays/").unwrap();
84        let oca_bundle = load_oca(&mut data.as_bytes(), &registry);
85        println!(">>>> {:?}", oca_bundle);
86        assert!(oca_bundle.is_ok());
87        let oca = oca_bundle.unwrap();
88        assert_eq!(
89            oca.digest.as_ref().map(|d| d.to_string()).as_deref(),
90            Some("EP79WPhSehW5kVwy67UR-bJEoGMUWcN5cK99THUtnBIm")
91        );
92        assert_eq!(oca.capture_base.attributes.len(), 9);
93    }
94}