//! # Iris //! A locking plugin manager for vim use iris::{Config, Result}; use log::info; use std::{env, process::exit}; const USAGE: &str = "\ Usage: iris [OPTION]... COMMAND [FILE}"; const HELP: &str = "\ A locking plugin manager for vim Commands: switch checkout plugins using their current refs and generate autoload file lock update plugin refs to point to the latest commit Options: -v, --verbose enable verbose output -q, --quiet disable output -h, --help print the help message -V, --version print the program version "; /// print the program version fn version() -> ! { println!("iris {}", env!("CARGO_PKG_VERSION")); exit(0); } /// print the program's usage fn usage() -> ! { eprintln!("{USAGE}"); exit(1); } /// print the program's help message fn help() -> ! { println!("{USAGE}\n{HELP}"); exit(0); } struct Options { /// the command to run command: String, /// the file to load instead of defaults file: Option, /// print verbose output verbose: bool, /// silence output quiet: bool, } // parse options fn opts() -> Options { let mut args = env::args().into_iter().peekable(); let mut quiet = false; let mut verbose = false; // skip program name _ = args.next(); // options while let Some(arg) = args.peek() { // arg is not an option if !arg.starts_with("-") { break; }; // grab next let arg = args.next().expect("no arg?!"); match arg.as_str() { // stop parsing options "--" => break, // quiet "-q" | "--quiet" => { quiet = true; } // verbose "-v" | "--verbose" => { verbose = true; } // version "-V" | "--version" => version(), // help "-h" | "--help" => help(), // invalid _ => { eprintln!("invalid options: '{arg}'"); exit(1); } }; } // command let Some(command) = args.next() else { usage(); }; // file let file = args.next(); // force end of arguments if args.next().is_some() { usage(); }; Options { command, file, quiet, verbose, } } fn log(opts: &Options) { if !opts.quiet { let level = if opts.verbose { log::LevelFilter::Trace } else { log::LevelFilter::Info }; env_logger::Builder::from_default_env() .filter_level(level) .format_timestamp(None) .format_module_path(false) .format_source_path(false) .init(); } } fn switch(opts: &Options) -> Result<()> { let cfg = match &opts.file { Some(path) => Config::read_from_file(path), None => Config::read_from_lock_file() .or_else(|_| Config::read_from_plugin_file()), }?; for plugin in &cfg.plugins { plugin.switch()?; info!("switched {}", plugin.id); } cfg.write_autoload_file()?; Ok(()) } fn lock(opts: &Options) -> Result<()> { let mut cfg = match &opts.file { Some(path) => Config::read_from_file(path), None => Config::read_from_plugin_file(), }?; for plugin in &mut cfg.plugins { plugin.lock()?; info!("locked {}", plugin.id); } // save lock file cfg.write_to_lock_file()?; Ok(()) } fn inner() -> Result<()> { // parse opts let opts = opts(); // initalize logger (if not quiet) log(&opts); // handle command match opts.command.as_str() { "switch" => switch(&opts)?, "lock" => lock(&opts)?, cmd => { eprintln!("unknown command: '{cmd}'"); exit(1); } }; Ok(()) } fn main() { if let Err(err) = inner() { log::error!("{err}"); exit(1); } }