rx-editor 0.3.0

a modern, extensible pixel editor
Documentation
use rx;
use rx::execution::{DigestMode, ExecutionMode, GifMode};

use env_logger;
use pico_args;

use std::io;
use std::path::PathBuf;
use std::process;

const HEADER: &str = r#"
Alexis Sellier <self@cloudhead.io>
A Modern & Extensible Pixel Editor
"#;

const HELP: &str = r#"
USAGE
    rx [OPTIONS] [<path>..]

OPTIONS
    -h, --help           Prints help
    -V, --version        Prints version

    -v                   Verbose mode (verbosity=2)
    -u <script>          Use the commands in <script> for initialization

    --verbosity <level>  Set verbosity level (0-5)
    --record <dir>       Record user input to a directory
    --replay <dir>       Replay user input from a directory
    --width <width>      Set the window width
    --height <height>    Set the window height
    --debug              Set debug mode
"#;

fn main() {
    if let Err(e) = self::execute(pico_args::Arguments::from_env()) {
        eprintln!("rx: {}", e);
        process::exit(1);
    }
}

fn execute(mut args: pico_args::Arguments) -> Result<(), Box<dyn std::error::Error>> {
    rx::ALLOCATOR.reset();

    let default = rx::Options::default();

    if args.contains(["-h", "--help"]) {
        println!("rx v{}{}{}", rx::VERSION, HEADER, HELP);
        return Ok(());
    }

    if args.contains(["-V", "--version"]) {
        println!("rx v{}", rx::VERSION);
        return Ok(());
    }

    let verbose = args.contains("-v");
    let debug = args.contains("--debug");
    let width = args.opt_value_from_str("--width")?;
    let height = args.opt_value_from_str("--height")?;
    let record_digests = args.contains("--record-digests");
    let record_gif = args.contains("--record-gif");
    let verify_digests = args.contains("--verify-digests");
    let headless = args.contains("--headless");
    let source = args.opt_value_from_str::<_, PathBuf>("-u")?;
    let replay = args.opt_value_from_str::<_, PathBuf>("--replay")?;
    let record = args.opt_value_from_str::<_, PathBuf>("--record")?;
    let resizable = width.is_none() && height.is_none() && replay.is_none() && record.is_none();

    if replay.is_some() && record.is_some() {
        return Err("'--replay' and '--record' can't both be specified".into());
    }

    let digest_mode = if record_digests && !verify_digests {
        DigestMode::Record
    } else if verify_digests && !record_digests {
        DigestMode::Verify
    } else if !verify_digests && !record_digests {
        DigestMode::Ignore
    } else {
        return Err("'--record-digests' and '--verify-digests' can't both be specified".into());
    };

    let gif_mode = if record_gif {
        GifMode::Record
    } else {
        GifMode::Ignore
    };

    if record_gif && record.is_none() && replay.is_none() {
        return Err("'--record-gif' has no effect without '--record' or '--replay'".into());
    }
    if record_digests && record.is_none() && replay.is_none() {
        return Err("'--record-digests' has no effect without '--record' or '--replay'".into());
    }

    let log = match args
        .opt_value_from_str("--verbosity")?
        .unwrap_or(if verbose { 2 } else { 0 })
    {
        0 => "rx=info",
        1 => "rx=info,error",
        2 => "rx=debug,error",
        3 => "rx=debug,info",
        _ => "debug",
    };

    let mut logger = env_logger::Builder::new();
    logger.parse_filters(log);
    logger.init();

    let width = width.unwrap_or(default.width);
    let height = height.unwrap_or(default.height);

    let exec = if let Some(path) = replay {
        ExecutionMode::Replay(path, digest_mode)
    } else if let Some(path) = record {
        ExecutionMode::Record(path, digest_mode, gif_mode)
    } else {
        ExecutionMode::Normal
    };

    let options = rx::Options {
        width,
        height,
        headless,
        resizable,
        source,
        exec,
        debug,
    };

    match args.free() {
        Ok(paths) => rx::init(&paths, options).map_err(|e| e.into()),
        Err(e) => {
            Err(io::Error::new(io::ErrorKind::InvalidInput, format!("{}\n{}", e, HELP)).into())
        }
    }
}