icon-pie 0.1.4-beta

A simple command-line tool for generating application icons.
use std::{io::{self, stdout}, fs, path::PathBuf, collections::HashMap};
use crate::{error::Error, Output, IconType, ResamplingFilter, Entries};
use icon_baker::{resample, Icon, Ico, Icns, PngSequence, SourceImage};

macro_rules! resample {
    ($r:expr) => {
        match $r {
            ResamplingFilter::Nearest => resample::nearest,
            ResamplingFilter::Linear  => resample::linear,
            ResamplingFilter::Cubic   => resample::cubic
        }
    };
}

macro_rules! io {
    ($err:expr) => {
        Error::Io($err, Output::Stdout)
    };
    ($err:expr, $path:expr) => {
        Error::Io($err, Output::Path($path))
    };
}

macro_rules! not_found {
    () => {
        io::Error::from(io::ErrorKind::NotFound)
    };
}

pub fn icon(entries: &Entries, icon_type: IconType, output: &Output) -> Result<(), Error> {
    match icon_type {
        IconType::Ico         => write(&mut get_icon::<Ico >(entries)?,        output),
        IconType::Icns        => write(&mut get_icon::<Icns>(entries)?,        output),
        IconType::PngSequence => write(&mut get_icon::<PngSequence>(entries)?, output)
    }
}

fn get_icon<I: Icon>(entries: &Entries) -> Result<I, Error> {
    let mut icon = I::new();
    let source_map = get_source_map(entries)?;

    for (size, path, filter) in entries {
        let src = source_map.get(path)
            .expect("Variable 'source_map' should have a key for String 'path'");

        match icon.add_entry(resample!(filter), src, *size) {
            Ok(()) => continue,
            Err(icon_baker::Error::Io(err)) => return Err(io!(err, path.clone())),
            Err(err) => return Err(Error::IconBaker(err))
        }
    }

    Ok(icon)
}

fn get_source_map(
    entries: &Entries
) -> Result<HashMap<&PathBuf, SourceImage>, Error> {
    let mut source_map = HashMap::with_capacity(entries.len());

    for (_, path, _) in entries {
        if let None = source_map.get(path) {
            if let Some(source) = SourceImage::from_path(path) {
                source_map.insert(path, source);
            } else {
                return Err(io!(not_found!(), path.clone()));
            }
        }
    }

    Ok(source_map)
}

fn write<I: Icon>(icon: &mut I, output: &Output) -> Result<(), Error> {
    match output {
        Output::Path(path) => match fs::File::create(path.clone()) {
            Ok(mut file) => icon.write(&mut file)
                .map_err(|err| io!(err, path.clone())),

            Err(err) => Err(io!(err, path.clone()))
        },
        Output::Stdout => icon.write(&mut stdout())
            .map_err(|err| io!(err))
    }
}