form 0.4.0

A small script to move inline modules into the proper directory structure
use getopts::Options;
use std::env;
use std::fs::File;
use std::io::{self, Read};
use std::path::Path;

use failure::*;

pub struct FormOpts {
    pub input: String,
    pub output_dir: String,
}

impl FormOpts {
    pub fn from_args() -> Result<Option<Self>, Error> {
        const SHORT_INPUT: &'static str = "i";
        const SHORT_OUTDIR: &'static str = "o";
        const SHORT_HELP: &'static str = "h";

        let args: Vec<String> = env::args().collect();
        let program = args[0].clone();
        let mut opts = Options::new();
        opts.optopt(
            SHORT_INPUT,
            "input",
            "input file to read instead of stdin",
            "FILE",
        );
        opts.optopt(SHORT_OUTDIR, "outdir", "set output directory", "DIR");
        opts.optflag(SHORT_HELP, "help", "print this help menu");

        let matches = opts.parse(&args[1..]).unwrap();
        if matches.opt_present("h") {
            print_usage(&program, opts);
            return Ok(None);
        }
        let output_dir = matches.opt_str(SHORT_OUTDIR).ok_or(err_msg("Output directory missing"))?;
        let input = read_input(matches.opt_str(SHORT_INPUT))?;

        Ok(Some(FormOpts {
            output_dir,
            input,
        }))
    }
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    println!("{}", opts.usage(&brief));
}

fn read_input<P: AsRef<Path>>(input_file: Option<P>) -> Result<String, Error> {
    let mut input = String::new();
    match input_file {
        Some(file_name) => {
            let mut file = File::open(file_name)?;
            file.read_to_string(&mut input)?;
        }
        None => {
            io::stdin().read_to_string(&mut input)?;
        }
    }
    Ok(input)
}