use std::fmt; use cursive::view::Resizable; use cursive::views::{Dialog, EditView}; use cursive::Cursive; use crate::app::App; pub fn open_command_window(s: &mut Cursive) { let command_window = Dialog::around(EditView::new().on_submit(call_on_app).fixed_width(40)); s.add_layer(command_window); } fn call_on_app(s: &mut Cursive, input: &str) { s.call_on_name("Main", |view: &mut App| { let cmd = Command::from_string(input); view.parse_command(cmd); }); // special command that requires access to // our main cursive object, has to be parsed again // here // TODO: fix this somehow if let Ok(Command::Quit) = Command::from_string(input) { s.quit(); } s.pop_layer(); } #[derive(PartialEq)] pub enum Command { Add(String, Option, bool), MonthPrev, MonthNext, Delete(String), TrackUp(String), TrackDown(String), Quit, Blank, } #[derive(Debug)] pub enum CommandLineError { InvalidCommand(String), InvalidArg(u32), // position NotEnoughArgs(String, u32), } impl std::error::Error for CommandLineError {} impl fmt::Display for CommandLineError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { CommandLineError::InvalidCommand(s) => write!(f, "Invalid command: `{}`", s), CommandLineError::InvalidArg(p) => write!(f, "Invalid argument at position {}", p), CommandLineError::NotEnoughArgs(s, n) => { write!(f, "Command `{}` requires atleast {} argument(s)!", s, n) } } } } type Result = std::result::Result; impl Command { pub fn from_string>(input: P) -> Result { let mut strings: Vec<&str> = input.as_ref().trim().split(' ').collect(); if strings.is_empty() { return Ok(Command::Blank); } let first = strings.first().unwrap().to_string(); let mut args: Vec = strings.iter_mut().skip(1).map(|s| s.to_string()).collect(); match first.as_ref() { "add" | "a" => { if args.is_empty() { return Err(CommandLineError::NotEnoughArgs(first, 1)); } let goal = args .get(1) .map(|x| { x.parse::() .map_err(|_| CommandLineError::InvalidArg(1)) }) .transpose()?; return Ok(Command::Add( args.get_mut(0).unwrap().to_string(), goal, false, )); } "add-auto" | "aa" => { if args.is_empty() { return Err(CommandLineError::NotEnoughArgs(first, 1)); } let goal = args .get(1) .map(|x| { x.parse::() .map_err(|_| CommandLineError::InvalidArg(1)) }) .transpose()?; return Ok(Command::Add( args.get_mut(0).unwrap().to_string(), goal, true, )); } "delete" | "d" => { if args.is_empty() { return Err(CommandLineError::NotEnoughArgs(first, 1)); } return Ok(Command::Delete(args[0].to_string())); } "track-up" | "tup" => { if args.is_empty() { return Err(CommandLineError::NotEnoughArgs(first, 1)); } return Ok(Command::TrackUp(args[0].to_string())); } "track-down" | "tdown" => { if args.is_empty() { return Err(CommandLineError::NotEnoughArgs(first, 1)); } return Ok(Command::TrackDown(args[0].to_string())); } "mprev" | "month-prev" => return Ok(Command::MonthPrev), "mnext" | "month-next" => return Ok(Command::MonthNext), "q" | "quit" => return Ok(Command::Quit), s => return Err(CommandLineError::InvalidCommand(s.into())), } } }