image4 0.8.2

A no_std-friendly library for parsing and generation of Image4 images written in pure Rust.
Documentation
use super::{ComprInfo, PayloadProps, PayloadRef};
use crate::Tag;
use alloc::vec::Vec;
use der::{
    asn1::{Ia5String, OctetString},
    referenced::{OwnedToRef, RefToOwned},
    Decode, DecodeValue, Encode, EncodeValue, FixedTag, Header, Length, Reader, Writer,
};

/// An Image4 payload that owns its data.
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct Payload {
    pub(super) tag_4cc: Tag,
    pub(super) builder_string: Ia5String,
    pub(super) data: OctetString,
    pub(super) keybags: Option<OctetString>,
    pub(super) compr_info: Option<ComprInfo>,
    pub(super) props: Option<PayloadProps>,
}

impl<'a> Payload {
    /// Creates a new payload from a tag, builder string and its contents.
    pub fn new(tag: Tag, builder_string: &str, data: Vec<u8>) -> der::Result<Self> {
        Ok(Self {
            tag_4cc: tag,
            builder_string: Ia5String::new(builder_string)?,
            data: OctetString::new(data)?,
            keybags: None,
            compr_info: None,
            props: None,
        })
    }

    /// Parses a DER-encoded payload into a [`Payload`].
    pub fn parse(bytes: &'a [u8]) -> der::Result<Self> {
        Self::from_der(bytes)
    }

    /// Returns the 4CC tag of the payload.
    pub fn tag(&self) -> Tag {
        self.tag_4cc
    }

    /// Sets the 4CC tag of the payload.
    pub fn set_tag(&mut self, tag: Tag) {
        self.tag_4cc = tag;
    }

    /// Returns the string identifying the program used to build the payload.
    pub fn builder_string(&self) -> &str {
        self.builder_string.as_str()
    }

    /// Sets the string identifying the program used to build the payload.
    ///
    /// # Panics
    /// This function will panic in case either the string contains characters that do not belong
    /// to the IA5 alphabet (ASCII characters larger than 127) or the length of the string is larger
    /// than the maximum length supported by the [`der`] crate ([`Length::MAX`]).
    pub fn set_builder_string(&mut self, s: &'a str) -> der::Result<()> {
        self.builder_string = Ia5String::new(s)?;
        Ok(())
    }

    /// Returns a reference to the payload's data.
    pub fn data(&self) -> &[u8] {
        self.data.as_bytes()
    }

    /// Sets the payload's data.
    ///
    /// # Panics
    /// This function will panic in case the length of the data is larger than the maximum length
    /// supported by the [`der`] crate ([`Length::MAX`]).
    pub fn set_data(&mut self, data: Vec<u8>) -> der::Result<()> {
        self.data = OctetString::new(data)?;
        Ok(())
    }

    /// Returns a reference to the payload's encoded keybags if present.
    pub fn keybags(&self) -> Option<&[u8]> {
        self.keybags.as_ref().map(OctetString::as_bytes)
    }

    /// Sets new encoded keybags for the payload.
    pub fn set_keybags(&mut self, bytes: Option<Vec<u8>>) -> der::Result<()> {
        self.keybags = bytes.map(OctetString::new).transpose()?;
        Ok(())
    }

    /// Returns a reference to the payload's compression info if present.
    pub fn compr_info(&self) -> Option<&ComprInfo> {
        self.compr_info.as_ref()
    }

    /// Sets new compression info for the payload.
    pub fn set_compr_info(&mut self, compr_info: Option<ComprInfo>) {
        self.compr_info = compr_info;
    }

    /// Returns a reference to the payload's properties if present.
    pub fn props(&self) -> Option<&PayloadProps> {
        self.props.as_ref()
    }

    /// Sets new properties for the payload.
    pub fn set_props(&mut self, props: Option<PayloadProps>) {
        self.props = props;
    }

    /// Decodes the part of an image payload 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> {
        PayloadRef::decode_after_magic(decoder).map(Payload::from)
    }
}

impl From<PayloadRef<'_>> for Payload {
    fn from(value: PayloadRef<'_>) -> Self {
        (&value).into()
    }
}

impl From<&'_ PayloadRef<'_>> for Payload {
    fn from(value: &'_ PayloadRef<'_>) -> Self {
        Self {
            tag_4cc: value.tag_4cc,
            builder_string: value.builder_string.ref_to_owned(),
            data: value.data.ref_to_owned(),
            keybags: value.keybags.ref_to_owned(),
            compr_info: value.compr_info.clone(),
            props: value.props.ref_to_owned(),
        }
    }
}

impl OwnedToRef for Payload {
    type Borrowed<'a> = PayloadRef<'a>;

    fn owned_to_ref(&self) -> Self::Borrowed<'_> {
        self.into()
    }
}

impl<'a> DecodeValue<'a> for Payload {
    fn decode_value<R: Reader<'a>>(decoder: &mut R, header: Header) -> der::Result<Self> {
        PayloadRef::decode_value(decoder, header).map(Payload::from)
    }
}

impl EncodeValue for Payload {
    fn value_len(&self) -> der::Result<Length> {
        PayloadRef::from(self).encoded_len()
    }

    fn encode_value(&self, encoder: &mut impl Writer) -> der::Result<()> {
        PayloadRef::from(self).encode_value(encoder)
    }
}

impl FixedTag for Payload {
    const TAG: der::Tag = der::Tag::Sequence;
}