oci-zero 0.3.0

A no-std, allocation-free streaming core for OCI registries
Documentation
use oci_zero::{
    metadata::{Document, MetadataError},
    registry::{basic_authorization, Credentials, RegistryError},
};

fn manifest(data: &str) -> String {
    format!(
        r#"{{"schemaVersion":2,"config":{{"mediaType":"x","size":0,"digest":"sha256:0000000000000000000000000000000000000000000000000000000000000000","data":"{data}"}},"layers":[]}}"#
    )
}

#[test]
fn inline_data_requires_canonical_padded_base64() {
    for data in [
        "Zh==", "Zm9=", "=m9v", "Zg=", "Zg===", "Zg==AAAA", "_w==", "Z g==",
    ] {
        let json = manifest(data);
        let descriptor = Document::parse(json.as_bytes())
            .unwrap()
            .manifest()
            .unwrap()
            .config()
            .unwrap();
        assert_eq!(
            descriptor.decode_data(&mut [0; 32]),
            Err(MetadataError::InvalidBase64),
            "{data}"
        );
    }
    for (data, expected) in [
        ("", b"".as_slice()),
        ("Zg==", b"f"),
        ("Zm8=", b"fo"),
        ("Zm9v", b"foo"),
        ("Zm9vYg==", b"foob"),
        ("Zm9vYmE=", b"fooba"),
        ("Zm9vYmFy", b"foobar"),
        (r"Z\u0067==", b"f"),
    ] {
        let json = manifest(data);
        let descriptor = Document::parse(json.as_bytes())
            .unwrap()
            .manifest()
            .unwrap()
            .config()
            .unwrap();
        let mut buffer = [0; 32];
        assert_eq!(
            descriptor.decode_data(&mut buffer).unwrap(),
            Some(expected),
            "{data}"
        );
    }
}

#[test]
fn basic_authorization_handles_every_padding_length_and_exact_buffers() {
    for (username, password, expected) in [
        ("", "", "Basic Og=="),
        ("a", "", "Basic YTo="),
        ("a", "b", "Basic YTpi"),
        ("ab", "cd", "Basic YWI6Y2Q="),
        ("a", "b:c", "Basic YTpiOmM="),
    ] {
        let credentials = Credentials { username, password };
        let mut buffer = vec![0; expected.len()];
        assert_eq!(
            basic_authorization(credentials, &mut buffer).unwrap(),
            expected
        );
        assert_eq!(
            basic_authorization(credentials, &mut buffer[..expected.len() - 1]),
            Err(RegistryError::BufferTooSmall)
        );
    }
}

#[test]
fn deterministic_tar_headers_preserve_zero_fields_and_checksum() {
    let mut bytes = Vec::new();
    oci_zero::tar::TarWriter::new()
        .begin_file(b"example.txt", 5, 0o644, |chunk| {
            bytes.extend_from_slice(chunk);
            Ok::<_, ()>(())
        })
        .unwrap();
    assert_eq!(&bytes[108..116], b"0000000\0");
    assert_eq!(&bytes[116..124], b"0000000\0");
    assert_eq!(&bytes[136..148], b"00000000000\0");
    let checksum: u64 = bytes
        .iter()
        .enumerate()
        .map(|(index, byte)| {
            u64::from(if (148..156).contains(&index) {
                b' '
            } else {
                *byte
            })
        })
        .sum();
    assert_eq!(&bytes[148..156], format!("{checksum:06o}\0 ").as_bytes());
}