fakejpeg 0.2.0

Rust port of Alun Jones' fakejpeg library
Documentation
// SPDX-FileCopyrightText: Alun Jones
// SPDX-FileCopyrightText: Gergely Nagy
// SPDX-FileContributor: Gergely Nagy
//
// SPDX-License-Identifier: MIT

pub mod chunk;
pub mod image_template;
pub mod pickle;

use crate::{Error, Result};
use image_template::ImageTemplate;

use rand::{Rng, seq::IndexedRandom};

/// A Template holds information about a number of JPEG images, and serves as an
/// input to [`ImageGenerator`](crate::ImageGenerator).
///
/// Use [`Template::from_input`] or [`Template::from_inputs`] if you know your
/// inputs ahead of time, otherwise create an empty template with
/// [`Template::default`] and fill it up with [`Template::learn`] or [`Template::learn_many`].
///
/// # Examples
///
/// ```
/// use fakejpeg::Template;
///
/// # static EXAMPLE_IMAGE: &'static [u8] = include_bytes!("../../benches/data/64x64.jpg");
/// #
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
///     let images = [
///       // images come here...
/// #      EXAMPLE_IMAGE,
///     ];
///     let template = Template::from_inputs(&images)?;
///
///     // Do something with `template`, maybe generate new fakes from it!
///
///     Ok(())
/// }
/// ```
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Template {
    pub(crate) _version: u32,
    pub(crate) image_templates: Vec<ImageTemplate>,
}

impl Template {
    /// Parses and learns the structure of a single JPEG images, returning a [`Template`].
    ///
    /// # Errors
    ///
    /// Returns [`Error::ParseError`] when parsing fails.
    pub fn from_input<I: AsRef<[u8]>>(input: I) -> Result<Self> {
        Self::from_inputs([input])
    }

    /// Parses and learns the structure of multiple JPEG images, returning a [`Template`].
    ///
    /// Similar to [`Template::from_input`], but rather than taking parsing and
    /// learning a single JPEG image, it does so over multiple of them.
    ///
    /// # Errors
    ///
    /// Returns [`Error::ParseError`] when parsing fails.
    pub fn from_inputs<A, I>(inputs: I) -> Result<Self>
    where
        A: AsRef<[u8]>,
        I: IntoIterator<Item = A>,
    {
        let mut this = Self::default();
        this.learn_many(inputs).and(Ok(this))
    }

    /// Parse and learn the structure of a single JPEG image.
    ///
    /// This parses the JPEG, and extracts the parts required to generate
    /// randomized fakes, and updates the template.
    ///
    /// # Errors
    ///
    /// Returns [`Error::ParseError`] when parsing fails.
    pub fn learn<A: AsRef<[u8]>>(&mut self, input: A) -> Result<()> {
        self.image_templates.push(input.as_ref().try_into()?);
        Ok(())
    }

    /// Parses and learns the structure of multiple JPEG images.
    ///
    /// Similar to [`Template::learn`], but rather than taking parsing and
    /// learning a single JPEG image, it does so over multiple of them.
    ///
    /// # Errors
    ///
    /// Returns [`Error::ParseError`] when parsing fails.
    pub fn learn_many<A, I>(&mut self, inputs: I) -> Result<()>
    where
        A: AsRef<[u8]>,
        I: IntoIterator<Item = A>,
    {
        inputs.into_iter().try_for_each(|item| self.learn(item))
    }

    pub(crate) fn choose<T: Rng>(&self, rng: &mut T) -> Result<&ImageTemplate> {
        self.image_templates.choose(rng).ok_or(Error::EmptyTemplate)
    }
}

impl TryFrom<&[u8]> for Template {
    type Error = Error;

    /// Try and convert a slice of bytes to a [`Template`].
    fn try_from(input: &[u8]) -> Result<Self> {
        Self::from_input(input)
    }
}

impl TryFrom<Vec<u8>> for Template {
    type Error = Error;

    /// Try and convert a [`Vec`] of bytes to a [`Template`].
    fn try_from(input: Vec<u8>) -> Result<Self> {
        Self::from_input(input)
    }
}

impl TryFrom<&Vec<u8>> for Template {
    type Error = Error;

    /// Try and convert a reference to a [`Vec`] of bytes to a [`Template`].
    fn try_from(input: &Vec<u8>) -> Result<Self> {
        Self::from_input(input)
    }
}