qoi 0.3.1

An implementation of Phoboslab's QOI image format.
Documentation
use qoi::{QoiDecode, QoiEncode, QoiError, QoiHeader};
use std::{
    ffi::OsStr,
    path::{Path, PathBuf},
};
use walkdir::WalkDir;

fn compare_bytes(actual: &[u8], expected: &[u8]) {
    for i in 0..actual.len() {
        if actual[i] != expected[i] {
            panic!("Byte {} doesn't match: {} != {}", i, actual[i], expected[i]);
        }
    }

    assert_eq!(actual.len(), expected.len());
}

struct TestCase {
    path: PathBuf,
    encoded: Vec<u8>,
    raw: Vec<u8>,
    header: QoiHeader,
}

impl From<&Path> for TestCase {
    fn from(path: &Path) -> Self {
        let encoded = std::fs::read(path).unwrap();
        let header = encoded.load_qoi_header().unwrap();
        Self {
            path: path.into(),
            encoded,
            raw: std::fs::read(path.with_extension("raw")).unwrap(),
            header,
        }
    }
}

fn for_all_qoi_files(f: impl Fn(&TestCase)) {
    let root = format!("{}/{}", env!("CARGO_MANIFEST_DIR"), "images",);

    for entry in WalkDir::new(root).max_depth(5).into_iter() {
        let entry = entry.unwrap();

        if entry.path().extension() == Some(OsStr::new("qoi")) {
            let test_case = entry.path().into();
            f(&test_case)
        }
    }
}

#[test]
fn decode() {
    for_all_qoi_files(|case| {
        println!("Testing {}", case.path.display());
        let decoded = case.encoded.qoi_decode_to_vec(None).unwrap();
        compare_bytes(&decoded, &case.raw);
    });
}

#[test]
fn encode() {
    for_all_qoi_files(|case| {
        println!("Testing {}", case.path.display());

        let encoded = case
            .raw
            .qoi_encode_to_vec(
                case.header.width(),
                case.header.height(),
                case.header.channels(),
                0,
            )
            .unwrap();

        compare_bytes(&encoded, &case.encoded);
    });
}

#[test]
fn header_magic() {
    assert!(matches!(
        b"boif1234123412".qoi_decode_to_vec(None).unwrap_err(),
        QoiError::IncorrectHeaderMagic
    ));
}

#[test]
fn buffer_size_errors() {
    let mut buffer = Vec::new();
    buffer.resize(1024, 0);

    let error = b"qoif123412341".qoi_decode(None, &mut buffer).unwrap_err();
    assert!(matches!(error, QoiError::InputSmallerThanHeader));
}