<commit_before>#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate getopts;
use std::env;
use std::fs;
use std::io;
use std::io::prelude::*;
use std::io::Write;
use std::path;
use std::process;
use getopts::Options;
use getopts::ParsingStyle;

const DEFAULT_CONF_PATH: &str = "~/.config/openby/config";

#[derive(Debug)]
enum AppError {
    Io(io::Error),
    Toml(toml::de::Error),
}

#[allow(dead_code)]
#[derive(Deserialize)]
struct Config {
    version: f64,
    tools: Vec<Tools>,
}

#[allow(dead_code)]
#[derive(Deserialize)]
struct Tools {
    command: String,
    extentions: Vec<String>,
}

fn main() {
    let exit_code = match run() {
        Ok(_) => 0,
        Err(e) => {
            writeln!(&mut io::stderr(), "{:?}", e).unwrap();
            1
        }
    };
    process::exit(exit_code);
}

fn run() -> Result<(), AppError> {
    let args: Vec<String> = env::args().collect();
    let args_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
    let (file_name, conf_name) = parse_options(args_str)?;
    println!("Ok: {} {}", file_name, conf_name);

    let conf = load_config(&conf_name)?;

    open_by(&conf, &file_name)?;

    Ok(())
}

fn parse_options(args: Vec<&str>) -> Result<(String, String), AppError> {
    let ref program = &args[0];

    let mut opts = Options::new();
    opts.parsing_style(ParsingStyle::FloatingFrees);
    opts.optopt("c", "config", "specify configure file name", "CONFIG");
    opts.optflag("h", "help", "print this help menu");

    let matches = opts.parse(&args[1..]).unwrap();

    if matches.opt_present("h") {
        print_usage(&program, &opts);
        process::exit(0);
    }

    let file_name = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, &opts);
        return Result::Err(AppError::Io(
            io::Error::new(io::ErrorKind::NotFound, "FILE is not found"),
        ));
    };

    let conf_name = if let Some(c) = matches.opt_str("c") {
        c
    } else {
        DEFAULT_CONF_PATH.to_string()
    };

    Ok((file_name, conf_name))
}

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

fn load_config(conf_name: &str) -> Result<Config, AppError> {
    let path = path::Path::new(conf_name);
    if path.exists() == false {
        return Result::Err(AppError::Io(io::Error::new(
            io::ErrorKind::NotFound,
            format!("{} is not exist", path.to_str().unwrap_or("")),
        )));
    }

    let mut reader = io::BufReader::new(fs::File::open(path).map_err(AppError::Io)?);
    let mut s = String::new();
    reader.read_to_string(&mut s).map_err(AppError::Io)?;

    let conf: Config = toml::from_str(s.as_str()).map_err(AppError::Toml)?;

    Ok(conf)
}

fn open_by(conf: &Config, file_name: &str) -> Result<(), AppError> {
    let path = path::Path::new(file_name);
    if path.exists() == false {
        return Result::Err(AppError::Io(io::Error::new(
            io::ErrorKind::NotFound,
            format!("{} is not exist", path.to_str().unwrap_or("")),
        )));
    }

    let ext = path.extension().ok_or(AppError::Io(io::Error::new(
        io::ErrorKind::NotFound,
        format!(
            "{} has not extension",
            path.to_str().unwrap_or("")
        ),
    )))?;

    match get_commnad_from_extension(conf, ext.to_str().unwrap()) {
        Some(cmd) => println!("command = {}", cmd),
        None => println!("command = None"),<commit_msg>execute commands according to extensions<commit_after>#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate getopts;
use std::env;
use std::fs;
use std::io;
use std::io::prelude::*;
use std::io::Write;
use std::path;
use std::process;
use getopts::Options;
use getopts::ParsingStyle;

const DEFAULT_CONF_PATH: &str = "~/.config/openby/config";

#[derive(Debug)]
enum AppError {
    Io(io::Error),
    Toml(toml::de::Error),
}

#[allow(dead_code)]
#[derive(Deserialize)]
struct Config {
    version: f64,
    tools: Vec<Tools>,
}

#[allow(dead_code)]
#[derive(Deserialize)]
struct Tools {
    command: String,
    extentions: Vec<String>,
}

fn main() {
    let exit_code = match run() {
        Ok(_) => 0,
        Err(e) => {
            writeln!(&mut io::stderr(), "{:?}", e).unwrap();
            1
        }
    };
    process::exit(exit_code);
}

fn run() -> Result<(), AppError> {
    let args: Vec<String> = env::args().collect();
    let args_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
    let (file_name, conf_name) = parse_options(args_str)?;
    println!("Ok: {} {}", file_name, conf_name);

    let conf = load_config(&conf_name)?;

    open_by(&conf, &file_name)?;

    Ok(())
}

fn parse_options(args: Vec<&str>) -> Result<(String, String), AppError> {
    let ref program = &args[0];

    let mut opts = Options::new();
    opts.parsing_style(ParsingStyle::FloatingFrees);
    opts.optopt("c", "config", "specify configure file name", "CONFIG");
    opts.optflag("h", "help", "print this help menu");

    let matches = opts.parse(&args[1..]).unwrap();

    if matches.opt_present("h") {
        print_usage(&program, &opts);
        process::exit(0);
    }

    let file_name = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, &opts);
        return Result::Err(AppError::Io(
            io::Error::new(io::ErrorKind::NotFound, "FILE is not found"),
        ));
    };

    let conf_name = if let Some(c) = matches.opt_str("c") {
        c
    } else {
        DEFAULT_CONF_PATH.to_string()
    };

    Ok((file_name, conf_name))
}

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

fn load_config(conf_name: &str) -> Result<Config, AppError> {
    let path = path::Path::new(conf_name);
    if path.exists() == false {
        return Result::Err(AppError::Io(io::Error::new(
            io::ErrorKind::NotFound,
            format!("{} is not exist", path.to_str().unwrap_or("")),
        )));
    }

    let mut reader = io::BufReader::new(fs::File::open(path).map_err(AppError::Io)?);
    let mut s = String::new();
    reader.read_to_string(&mut s).map_err(AppError::Io)?;

    let conf: Config = toml::from_str(s.as_str()).map_err(AppError::Toml)?;

    Ok(conf)
}

fn open_by(conf: &Config, file_name: &str) -> Result<(), AppError> {
    let file_path = path::Path::new(file_name);
    if file_path.exists() == false {
        return Result::Err(AppError::Io(io::Error::new(
            io::ErrorKind::NotFound,
            format!("{} is not exist", file_path.to_str().unwrap_or("")),
        )));
    }

    let ext = file_path.extension().ok_or(AppError::Io(io::Error::new(
        io::ErrorKind::NotFound,
        format!(
            "{} has not extension",
            file_path.to_str().unwrap_or("")
        ),
    )))?;

    match get_commnad_from_extension(conf, ext.to_str().unwrap()) {
        Some(cmd) => {
            println!("file = {:?}", file_path);
            process::Command::new(cmd).arg(file_path).status().expect(
                "failed to run",
            );
        }
        None => {
            println!("command = None");
            // TODO: register command about this extension.
        }
{'fraction_non_alphanumeric': 0.19018653690186538, 'fraction_numerical': 0.0021627466882941336, 'mean_word_length': 3.094631986718318, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3541775', 'n_tokens_mistral': 2520, 'n_tokens_neox': 2530, 'n_words': 495}