plumers 1.0.2

Multi-format image library with first-class support for paletted images
Documentation
use std::{ffi::c_int, iter::FusedIterator, ptr::NonNull};

#[derive(Debug)]
pub struct MetadataNode<'node> {
    pub kind: c_int,
    pub data: &'node [u8],
}

#[derive(Debug)]
pub struct MetadataNodeMut<'node> {
    pub kind: c_int,
    pub data: &'node mut [u8],
}

#[derive(Debug, Clone)]
pub struct Metadata<'img>(Option<&'img libplum_sys::plum_metadata>);

impl<'img> Metadata<'img> {
    pub(super) fn new(img: &'img libplum_sys::plum_image) -> Self {
        // SAFETY: the pointer will remain valid for as long as `img` lives,
        //         and `self` is guaranteed to outlive it.
        Self(unsafe { Self::make_inner(img.metadata) })
    }

    /// # Safety
    ///
    /// `ptr` must be valid for `pointer::as_ref()` for the lifetime `'img`.
    unsafe fn make_inner(
        ptr: *const libplum_sys::plum_metadata,
    ) -> Option<&'img libplum_sys::plum_metadata> {
        // SAFETY: deferred to the caller.
        unsafe { ptr.as_ref() }
    }
}

impl<'img> Iterator for Metadata<'img> {
    type Item = MetadataNode<'img>;

    fn next(&mut self) -> Option<Self::Item> {
        let raw_node = self.0?;
        // SAFETY: the node's `next` pointer is valid for as long as this node itself.
        self.0 = unsafe { Self::make_inner(raw_node.next) }; // Iterate to the next node.

        // The pointer may be NULL if the size is zero. Rust doesn't like that.
        let data_ptr = NonNull::new(raw_node.data as *mut u8).unwrap_or(NonNull::dangling());
        assert!(raw_node.size <= isize::MAX as usize); // Another constraint of Rust's.
        let slice_ptr = NonNull::slice_from_raw_parts(data_ptr, raw_node.size);
        // SAFETY: libplum guarantees that the slice is valid.
        //         It will not be mutated, because we hold a ref on the owning image.
        let data = unsafe { slice_ptr.as_ref() };
        Some(MetadataNode {
            kind: raw_node.type_,
            data,
        })
    }
}
impl FusedIterator for Metadata<'_> {}

#[derive(Debug)]
pub struct MetadataMut<'img>(Option<&'img mut libplum_sys::plum_metadata>);

impl<'img> MetadataMut<'img> {
    pub(super) fn new(img: &'img mut libplum_sys::plum_image) -> Self {
        // SAFETY: the pointer will remain valid for as long as `img` lives,
        //         and `self` is guaranteed to outlive it.
        Self(unsafe { Self::make_inner(img.metadata) })
    }

    /// # Safety
    ///
    /// `ptr` must be valid for `pointer::as_mut()` for the lifetime `'img`.
    unsafe fn make_inner(
        ptr: *mut libplum_sys::plum_metadata,
    ) -> Option<&'img mut libplum_sys::plum_metadata> {
        // SAFETY: deferred to the caller.
        unsafe { ptr.as_mut() }
    }
}

impl<'img> Iterator for MetadataMut<'img> {
    type Item = MetadataNodeMut<'img>;

    fn next(&mut self) -> Option<Self::Item> {
        let raw_node = self.0.take()?;
        // SAFETY: the node's `next` pointer is valid for as long as this node itself.
        self.0 = unsafe { Self::make_inner(raw_node.next) }; // Iterate to the next node.

        // The pointer may be NULL if the size is zero. Rust doesn't like that.
        let data_ptr = NonNull::new(raw_node.data as *mut u8).unwrap_or(NonNull::dangling());
        assert!(raw_node.size <= isize::MAX as usize); // Another constraint of Rust's.
        let mut slice_ptr = NonNull::slice_from_raw_parts(data_ptr, raw_node.size);
        // SAFETY: libplum guarantees that the slice is valid.
        //         It will not be mutated, because we hold a ref on the owning image.
        let data = unsafe { slice_ptr.as_mut() };
        Some(MetadataNodeMut {
            kind: raw_node.type_,
            data,
        })
    }
}
impl FusedIterator for MetadataMut<'_> {}