icon-pie 0.1.4-beta

A simple command-line tool for generating application icons.
extern crate icon_baker;
extern crate crossterm;

mod parse;
mod eval;
mod error;

use std::{env, io, path::{PathBuf}};
use icon_baker::Size;
use crossterm::{style, Color};

pub enum Command {
    Help,
    Version,
    Icon(Entries, IconType, Output)
}

#[derive(Clone, Debug)]
pub enum Output {
    Path(PathBuf),
    Stdout
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum IconType {
    Ico,
    Icns,
    PngSequence
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ResamplingFilter {
    Nearest,
    Linear,
    Cubic
}

pub type Entries = Vec<(Size, PathBuf, ResamplingFilter)>;

#[macro_export]
macro_rules! syntax {
    ($err:expr) => { Err(Error::Syntax($err)) };
}

const VERSION: &str = "0.1.4-beta";
const TITLE: &str = r"
 _____               ______ _      
|_   _|              | ___ (_)     
  | |  ___ ___  _ __ | |_/ /_  ___ 
  | | / __/ _ \| '_ \|  __/| |/ _ \
 _| || (_| (_) | | | | |   | |  __/
 \___/\___\___/|_| |_\_|   |_|\___|";
const USAGE: &str = "$ icon-pie ((-e <file path> <size>... [-r (nearest | linear | cubic)])... (-ico | -icns | -png) [<output path>]) | -h | --help | -v | --version";
const EXAMPLES: [&str;3] = [
    "$ icon-pie -e big.svg 32 64 128 -ico icon.ico",
    "$ icon-pie -e small.png 32 64 -e big.svg 128 -icns icon.icns",
    "$ icon-pie -e small.png 32 64 -r linear -e big.svg 128 -png icon.tar"
];

const COMMANDS: [&str;7] = [
    "Specify an entry's source image, target sizes and resampling filter (optional).",
    "Specify a re-sampling filter: `nearest`, `linear` or `cubic`. If no filter is specified the app defaults to `nearest`.",
    "Outputs to an `.ico` file. If no output path is specified the app outputs to `stdout`.",
    "Outputs to an `.icns` file. If no output path is specified the app outputs to `stdout`.",
    "Outputs a `.png` sequence as a `.tar` file. If no output path is specified the app outputs to `stdout`.",
    "Help.",
    "Display version information.",
];

fn main() -> io::Result<()> {
    match parse::args() {
        Ok(cmd)  => command(cmd),
        Err(err) => Err(err.exit_with())
    }
}

fn command(cmd: Command) -> io::Result<()> {
    match cmd {
        Command::Icon(entries, icon_type, output) => icon(&entries, icon_type, output)?,
        Command::Help    => help(),
        Command::Version => version()
    }

    Ok(())
}

fn icon(entries: &Entries, icon_type: IconType, output: Output) -> io::Result<()> {
    eval::icon(&entries, icon_type, &output)
        .map_err(|err| err.exit_with())?;

    if let Output::Path(path) = output {
        println!(
            "{} Output saved at {}.",
            style("[Success]").with(Color::Green),
            style(path.display()).with(Color::Blue)
        );
    }

    Ok(())
}

#[inline]
fn help() {
    println!(
        "{}\n{}{}",
        style(TITLE).with(Color::Green),
        style("v").with(Color::Green),
        style(VERSION).with(Color::Green)
    );

    println!("\n{} {}\n\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}",
        style("Usage:").with(Color::Blue),
        style(USAGE).with(Color::Green),
        style("   -e <options>          ").with(Color::Green),
        COMMANDS[0],
        style("   -r <filter>           ").with(Color::Green),
        COMMANDS[1],
        style("   -ico [<output path>]  ").with(Color::Green),
        COMMANDS[2],
        style("   -icns [<output path>] ").with(Color::Green),
        COMMANDS[3],
        style("   -png [<output path>]  ").with(Color::Green),
        COMMANDS[4],
        style("   -h, --help            ").with(Color::Green),
        COMMANDS[5],
        style("   -v, --version         ").with(Color::Green),
        COMMANDS[6]
    );

    println!("\n{}\n   {}\n   {}\n   {}\n",
        style("Examples:").with(Color::Blue),
        style(EXAMPLES[0]).with(Color::Green),
        style(EXAMPLES[1]).with(Color::Green),
        style(EXAMPLES[2]).with(Color::Green)
    );
}

#[inline]
fn version() {
    println!("icon-pie v{}", VERSION);
}

fn args() -> Vec<String> {
    let output: Vec<String> = env::args_os()
        .map(|os_str| String::from(os_str.to_string_lossy()))
        .collect();

    // Refactor this
    if output.len() > 0 {
        if let parse::Token::Path(_) = parse::Token::from(output[0].as_ref()) {
            return Vec::from(&output[1..]);
        }
    }

    output
}