pub(crate) mod cli; pub(crate) mod manifest; use clap::Parser; use cli::{Cli, Commands, Target}; use comfy_table::Table; use manifest::Manifest; use std::collections::HashMap; use std::process; use thiserror::Error as TeError; use wfassoc::{Program, Scope, Token, View}; // region: Basic Types /// All errors occurs in this executable. #[derive(TeError, Debug)] enum Error { /// Error from wfassoc core. #[error("{0}")] Core(#[from] wfassoc::Error), /// Error when parsing manifest TOML file. #[error("invalid manifest file: {0}")] Manifest(#[from] manifest::Error), /// Error when specifying invalid manner name for extension. #[error("extension {ext} associated manner {manner} is invalid in manifest file")] InvalidMannerName { manner: String, ext: String }, } /// Result type used in this executable. type Result = std::result::Result; // endregion // region: Correponding Runner struct Composition { program: Program, ext_tokens: Vec, } impl Composition { pub fn new(cli: &Cli) -> Result { // Open file and read manifest TOML file let mf = Manifest::from_file(&cli.config_file)?; // Create instance let mut program = Program::new(&mf.identifier, &cli.config_file)?; // Setup manner let mut manner_token_map: HashMap<&str, Token> = HashMap::new(); for (k, v) in mf.manners.iter() { let token = program.add_manner(v.as_str())?; manner_token_map.insert(k.as_str(), token); } // Setup extension let mut ext_tokens = Vec::new(); for (k, v) in mf.exts.iter() { let token = match manner_token_map.get(v.as_str()) { Some(v) => v, None => { return Err(Error::InvalidMannerName { manner: v.to_string(), ext: k.clone(), }); } }; let token = program.add_ext(k.as_str(), *token)?; ext_tokens.push(token); } // Okey Ok(Self { program, ext_tokens, }) } pub fn get_program(&self) -> &Program { &self.program } pub fn iter_ext_tokens(&self) -> impl Iterator { self.ext_tokens.iter().copied() } } fn run_register(cli: &Cli, target: &Target) -> Result<()> { let composition = Composition::new(cli)?; let scope = target.clone().into(); // Register program let program = composition.get_program(); program.register(scope)?; // Link all file extensions for token in composition.iter_ext_tokens() { program.link_ext(token, scope)?; } println!("Register OK."); Ok(()) } fn run_unregister(cli: &Cli, target: &Target) -> Result<()> { let composition = Composition::new(cli)?; let scope = target.clone().into(); // Unlink all file extensions let program = composition.get_program(); for token in composition.iter_ext_tokens() { program.unlink_ext(token, scope)?; } // Unregister prorgam program.unregister(scope)?; println!("Unregister OK."); Ok(()) } fn run_query(cli: &Cli) -> Result<()> { let composition = Composition::new(cli)?; let mut table = Table::new(); table.set_header(["Extension", "Hybrid", "User", "System"]); let program = composition.get_program(); for token in composition.iter_ext_tokens() { let cell_ext = program.get_ext_str(token).unwrap(); let cell_hybrid = program .query_ext(token, View::Hybrid)? .map(|pi| pi.to_string()) .unwrap_or_default(); let cell_user = program .query_ext(token, View::User)? .map(|pi| pi.to_string()) .unwrap_or_default(); let cell_system = program .query_ext(token, View::System)? .map(|pi| pi.to_string()) .unwrap_or_default(); table.add_row([cell_ext, cell_hybrid, cell_user, cell_system]); } println!("{table}"); Ok(()) } // endregion fn main() { let cli = Cli::parse(); let rv = match &cli.command { Commands::Register { target } => run_register(&cli, target), Commands::Unregister { target } => run_unregister(&cli, target), Commands::Query => run_query(&cli), }; rv.unwrap_or_else(|e| { eprintln!("Runtime error: {}.", e); process::exit(1) }); }