cri-ref 0.0.2

Embedded-friendly equivalents of URIs
Documentation
use serde::Deserialize;

#[derive(Deserialize, Debug)]
#[serde(rename_all="kebab-case")]
struct VectorFile {
    base_uri: String,
    #[serde(with = "hex")]
    base_cri: Vec<u8>,
    test_vectors: Vec<VectorItem>,
}

#[derive(Deserialize)]
#[serde(rename_all="kebab-case")]
struct VectorItem {
    uri: Option<String>,
    #[serde(with = "hex")]
    cri: Vec<u8>,
    uri_from_cri: Option<String>,
    #[serde(with = "hex")]
    resolved_cri: Vec<u8>,
    resolved_uri: String,
    description: Option<String>,
    invalid: Option<String>,
}

impl core::fmt::Debug for VectorItem {
    fn fmt(&self, w: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
        fn pretty(data: &[u8]) -> String {
            cbor_diag::parse_bytes(data)
                .map(|parsed| parsed.to_diag_pretty())
                .unwrap_or_else(|_| hex::encode(&data))
        }

        writeln!(w, "VectorItem {{")?;
        if let Some(description) = &self.description {
            writeln!(w, "    description: {description}")?;
        }
        if let Some(uri) = &self.uri {
            writeln!(w, "    uri: {uri}")?;
        }
        writeln!(w, "    cri: {}", pretty(&self.cri))?;
        if let Some(uri_from_cri) = &self.uri_from_cri {
            writeln!(w, "    uri_from_cri: {uri_from_cri}")?;
        }
        writeln!(w, "    resolved_cri: {}", pretty(&self.resolved_cri))?;
        writeln!(w, "    resolved_uri: {}", self.resolved_uri)?;
        if let Some(invalid) = &self.invalid {
            writeln!(w, "    invalid: {invalid}")?;
        }
        writeln!(w, "}}")?;
        Ok(())
    }
}

#[test]
fn test_vectors() -> Result<(), std::io::Error> {
    use crate::native_cbor::{NativeCBORCriRef, NativeCBORCri};
    use crate::accessor::{CriRef, Cri};

    let mut parse_failures = 0;
    let mut rebuild_failures = 0;

    let testfiles = ["test/tests.json"];

    for f in testfiles {
        let v: VectorFile = serde_json::from_reader(
            std::fs::File::open(f)
                .expect("Test file not found"))
            .expect("Failed to load test file");

        let base_cri: NativeCBORCriRef = v.base_cri[..].try_into().expect("Failed to parse base CRI");
        let base_cri: NativeCBORCri = base_cri.try_into().expect("Base CRI is not absolute");
        assert_eq!(v.base_uri, base_cri.render_uri(), "Base CRI of file {} mismatched with base URI", f);

        for i in v.test_vectors {
            match (i.cri[..].try_into(), i.resolved_cri[..].try_into()) {
                (Err(e), _) => {
                    println!("Failed to parse CRI reference {:?}: {:?}", i, e);
                    parse_failures += 1;
                }
                (_, Err(e)) => {
                    println!("Failed to parse resolved CRI {:?}: {:?}", i, e);
                    parse_failures += 1;
                }
                (Ok(cri @ NativeCBORCriRef { .. }), Ok(resolved_cri @ NativeCBORCriRef { .. })) => {
                    let rebuilt = cri.render_uri_ref_like();
                    if let Some(uri_from_cri) = &i.uri_from_cri {
                        // We don't have any expectations on the outcome of invalid input yet
                        if i.invalid.is_none() {
                            if &rebuilt != uri_from_cri {
                                rebuild_failures += 1;
                                println!("Failed to rebuild {:?}:\nCRI is {:?},\nexpected <{}>,\nproduced <{}>", i, cri, uri_from_cri, rebuilt);
                            }
                        }
                    } else {
                        println!("Warning: Rebuilt URI {} from CRI even though test vector says there is no corresponding CRI reference.\n{:?}", rebuilt, i);
                    }

                    if let Ok(absolute @ NativeCBORCri { .. }) = cri.clone().try_into() {
                        let rebuilt_absolute = absolute.render_uri();
                        if rebuilt_absolute != rebuilt {
                            println!("Failed to rebuild URI form of {i:?}\nURI <{rebuilt_absolute}>\nref <{rebuilt}>.");
                            rebuild_failures += 1;
                        }
                    }

                    let resolved_cri: NativeCBORCri = resolved_cri.try_into()
                        .expect("Resolved CRI should not just be a reference");
                    let built_from_preresolved = resolved_cri.render_uri();
                    if i.invalid.is_none() && built_from_preresolved != i.resolved_uri {
                        println!("Failed to rebuild resolved URI from preresolved CRI {:?}\nCRI is {:?}\nexpected <{}>\nproduced <{}>", i, resolved_cri, i.resolved_uri, built_from_preresolved);
                        rebuild_failures += 1;
                    }

                    let resolved = base_cri.resolve(cri);
                    let built_resolved_uri = resolved.render_uri();
                    if i.invalid.is_none() {
                        if built_resolved_uri != i.resolved_uri {
                            println!("URI form of resolved value differs for {i:?}\nexpected <{}>\nproduced <{}>", i.resolved_uri, built_resolved_uri);
                            rebuild_failures += 1;
                        } else {
                            // passed ... and now check that their equality passses too (FIXME:
                            // Could be an "if bad rebuild_failure += 1 and print", but really this
                            // is unlikely to trigger unless the resolved URIs differ too)
                            assert!(resolved.equals(&resolved_cri), "Equality check failed");
                        }
                    }

                    if i.uri_from_cri.map(|a| a == "") != Some(true) {
                        assert!(!resolved.equals(&base_cri), "Resolved should not equal base CRI");
                    }

                    // We can't compare CRIs yet, but at least both the preresolved and the one we
                    // resolved are checked against the same URI output.
                }
            };
        }
    }

    assert_eq!(parse_failures, 0, "Some vectors could not be parsed");
    assert_eq!(rebuild_failures, 0, "Some vectors could not be rebuilt");

    Ok(())
}