image4 0.8.2

A no_std-friendly library for parsing and generation of Image4 images written in pure Rust.
Documentation
use crate::{util, ManifestRef, PayloadRef, RestoreInfoRef, Tag};
use der::{
    asn1::Ia5StringRef, Decode, DecodeValue, Encode, EncodeValue, FixedTag, Header, Length, Reader,
    TagNumber, Writer,
};
#[cfg(any(feature = "alloc", test))]
use {
    super::Image,
    der::referenced::{OwnedToRef, RefToOwned},
};

/// An Image4 image that doesn't own its data.
#[derive(Clone, Debug)]
pub struct ImageRef<'a> {
    pub(super) payload: PayloadRef<'a>,
    pub(super) manifest: Option<ManifestRef<'a>>,
    pub(super) restore_info: Option<RestoreInfoRef<'a>>,
}

impl<'a> ImageRef<'a> {
    /// Creates a new [`ImageRef`] which only contains a payload.
    pub fn new(tag: Tag, builder_string: &'a str, data: &'a [u8]) -> der::Result<Self> {
        Ok(Self {
            payload: PayloadRef::new(tag, builder_string, data)?,
            manifest: None,
            restore_info: None,
        })
    }

    /// Creates a new image from a payload.
    pub fn from_payload(payload: PayloadRef<'a>) -> Self {
        Self {
            payload,
            manifest: None,
            restore_info: None,
        }
    }

    /// Parses a DER encoded payload and creates an [`ImageRef`] that contains it.
    pub fn from_payload_bytes(bytes: &'a [u8]) -> der::Result<Self> {
        Ok(Self {
            payload: PayloadRef::from_der(bytes)?,
            manifest: None,
            restore_info: None,
        })
    }

    /// Parses a DER encoded Image4 manifest and stores the parsed [`ManifestRef`] in the image.
    pub fn parse_manifest(&mut self, bytes: &'a [u8]) -> der::Result<()> {
        self.manifest = Some(ManifestRef::from_der(bytes)?);

        Ok(())
    }

    /// Returns an immutable reference to the payload of the image.
    pub fn payload(&self) -> &PayloadRef<'a> {
        &self.payload
    }

    /// Returns a mutable reference to the payload of the image.
    pub fn payload_mut(&mut self) -> &mut PayloadRef<'a> {
        &mut self.payload
    }

    /// Returns an immutable reference to the manifest of the image if any.
    pub fn manifest(&self) -> Option<&ManifestRef<'a>> {
        self.manifest.as_ref()
    }

    /// Returns a mutable reference to the manifest of the image if any.
    pub fn manifest_mut(&mut self) -> Option<&mut ManifestRef<'a>> {
        self.manifest.as_mut()
    }

    /// Sets a new manifest for the image.
    pub fn set_manifest(&mut self, manifest: Option<ManifestRef<'a>>) {
        self.manifest = manifest;
    }

    /// Returns an immutable reference to the restore info of the image if any.
    pub fn restore_info(&self) -> Option<&RestoreInfoRef<'a>> {
        self.restore_info.as_ref()
    }

    /// Returns a mutable reference to the restore info of the image if any.
    pub fn restore_info_mut(&mut self) -> Option<&mut RestoreInfoRef<'a>> {
        self.restore_info.as_mut()
    }

    /// Sets restore info of the image.
    pub fn set_restore_info(&mut self, restore_info: Option<RestoreInfoRef<'a>>) {
        self.restore_info = restore_info;
    }

    /// Decodes the part of an image after the magic string.
    ///
    /// May be used when it is required to first identify what kind of buffer you're looking at by
    /// checking the magic string.
    pub fn decode_after_magic<R: Reader<'a>>(decoder: &mut R) -> der::Result<Self> {
        let payload = decoder.decode()?;
        let manifest = util::decode_opt_context_specific_field(decoder, TagNumber::N0, true)?;
        let restore_info = util::decode_opt_context_specific_field(decoder, TagNumber::N1, true)?;

        Ok(ImageRef {
            payload,
            manifest,
            restore_info,
        })
    }
}

impl FixedTag for ImageRef<'_> {
    const TAG: der::Tag = der::Tag::Sequence;
}

impl<'a> DecodeValue<'a> for ImageRef<'a> {
    fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
        debug_assert_eq!(header.tag, der::Tag::Sequence);

        reader.read_nested(header.length, |reader| {
            util::decode_and_check_magic(reader, b"IMG4")?;
            ImageRef::decode_after_magic(reader)
        })
    }
}

impl EncodeValue for ImageRef<'_> {
    fn value_len(&self) -> der::Result<Length> {
        let magic = Ia5StringRef::new(b"IMG4")?;
        let magic_len = magic.encoded_len()?;
        let payload_len = self.payload.encoded_len()?;
        let manifest_len =
            util::encoded_opt_context_specific_field_len(self.manifest(), TagNumber::N0, true)?;
        let restore_info_len = util::encoded_opt_context_specific_field_len(
            self.restore_info.as_ref(),
            TagNumber::N1,
            true,
        )?;

        magic_len + payload_len + manifest_len + restore_info_len
    }

    fn encode_value(&self, encoder: &mut impl Writer) -> der::Result<()> {
        let magic = Ia5StringRef::new(b"IMG4")?;

        magic.encode(encoder)?;
        self.payload.encode(encoder)?;
        util::encode_opt_context_specific_field(encoder, self.manifest(), TagNumber::N0, true)?;
        util::encode_opt_context_specific_field(
            encoder,
            self.restore_info.as_ref(),
            TagNumber::N1,
            true,
        )
    }
}

#[cfg(any(feature = "alloc", test))]
impl<'a> From<&'a Image> for ImageRef<'a> {
    fn from(value: &'a Image) -> Self {
        Self {
            payload: value.payload.owned_to_ref(),
            manifest: value.manifest.as_ref().map(|m| m.owned_to_ref()),
            restore_info: value.restore_info.as_ref().map(OwnedToRef::owned_to_ref),
        }
    }
}

#[cfg(any(feature = "alloc", test))]
impl<'a> RefToOwned<'a> for ImageRef<'a> {
    type Owned = Image;

    fn ref_to_owned(&self) -> Self::Owned {
        self.into()
    }
}