feat: refactor architecture #4
+167
@@ -0,0 +1,167 @@
|
|||||||
|
use std::env;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct CliConfig {
|
||||||
|
pub work_mins: u32,
|
||||||
|
pub break_mins: u32,
|
||||||
|
pub enable_notifications: bool,
|
||||||
|
pub enable_visualizer: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum CliAction {
|
||||||
|
Run(CliConfig),
|
||||||
|
PrintHelp,
|
||||||
|
PrintVersion,
|
||||||
|
Error(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cli;
|
||||||
|
|
||||||
|
impl Cli {
|
||||||
|
pub fn parse() -> CliAction {
|
||||||
|
Self::parse_args(env::args().collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_args(args: Vec<String>) -> CliAction {
|
||||||
|
let mut enable_notifications = false;
|
||||||
|
let mut enable_visualizer = false;
|
||||||
|
let mut positional = Vec::new();
|
||||||
|
|
||||||
|
for arg in args.into_iter().skip(1) {
|
||||||
|
match arg.as_str() {
|
||||||
|
"-h" | "--help" => return CliAction::PrintHelp,
|
||||||
|
"-v" | "--version" => return CliAction::PrintVersion,
|
||||||
|
"-n" | "--notify" => enable_notifications = true,
|
||||||
|
"-z" | "--visualizer" => enable_visualizer = true,
|
||||||
|
"--" => {}
|
||||||
|
s if s.starts_with('-') => {
|
||||||
|
return CliAction::Error(format!("Unknown option: '{}'", s));
|
||||||
|
}
|
||||||
|
s => positional.push(s.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let numbers: Vec<u32> = positional
|
||||||
|
.iter()
|
||||||
|
.filter_map(|s| s.parse::<u32>().ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let work_mins = numbers.first().copied().unwrap_or(25);
|
||||||
|
let break_mins = numbers.get(1).copied().unwrap_or(5);
|
||||||
|
|
||||||
|
if numbers.len() > 2 {
|
||||||
|
return CliAction::Error(format!("Unexpected argument: '{}'", positional[2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
CliAction::Run(CliConfig {
|
||||||
|
work_mins,
|
||||||
|
break_mins,
|
||||||
|
enable_notifications,
|
||||||
|
enable_visualizer,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_help() {
|
||||||
|
println!("Usage: termato [options] [work_minutes] [break_minutes]");
|
||||||
|
println!();
|
||||||
|
println!("Options:");
|
||||||
|
println!(" -n, --notify Enable desktop notifications");
|
||||||
|
println!(" -z, --visualizer Display visualizer of playing audio");
|
||||||
|
println!(" -h, --help Print this help message");
|
||||||
|
println!(" -v, --version Print the version number");
|
||||||
|
println!();
|
||||||
|
println!("Defaults: work_minutes = 25, break_minutes = 5");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_version() {
|
||||||
|
println!("termato version {}", env!("CARGO_PKG_VERSION"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_defaults() {
|
||||||
|
let args = vec!["termato".to_string()];
|
||||||
|
assert_eq!(
|
||||||
|
Cli::parse_args(args),
|
||||||
|
CliAction::Run(CliConfig {
|
||||||
|
work_mins: 25,
|
||||||
|
break_mins: 5,
|
||||||
|
enable_notifications: false,
|
||||||
|
enable_visualizer: false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cargo_run_syntax_with_dot_and_dash_dash() {
|
||||||
|
let args = vec![
|
||||||
|
"target/debug/termato.exe".to_string(),
|
||||||
|
".".to_string(),
|
||||||
|
"--".to_string(),
|
||||||
|
"-z".to_string(),
|
||||||
|
];
|
||||||
|
assert_eq!(
|
||||||
|
Cli::parse_args(args),
|
||||||
|
CliAction::Run(CliConfig {
|
||||||
|
work_mins: 25,
|
||||||
|
break_mins: 5,
|
||||||
|
enable_notifications: false,
|
||||||
|
enable_visualizer: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_custom_time_and_flags() {
|
||||||
|
let args = vec![
|
||||||
|
"termato".to_string(),
|
||||||
|
"-n".to_string(),
|
||||||
|
"-z".to_string(),
|
||||||
|
"50".to_string(),
|
||||||
|
"10".to_string(),
|
||||||
|
];
|
||||||
|
assert_eq!(
|
||||||
|
Cli::parse_args(args),
|
||||||
|
CliAction::Run(CliConfig {
|
||||||
|
work_mins: 50,
|
||||||
|
break_mins: 10,
|
||||||
|
enable_notifications: true,
|
||||||
|
enable_visualizer: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_help_flag() {
|
||||||
|
let args = vec!["termato".to_string(), "--help".to_string()];
|
||||||
|
assert_eq!(Cli::parse_args(args), CliAction::PrintHelp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unknown_flag() {
|
||||||
|
let args = vec!["termato".to_string(), "--foo".to_string()];
|
||||||
|
assert_eq!(
|
||||||
|
Cli::parse_args(args),
|
||||||
|
CliAction::Error("Unknown option: '--foo'".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_non_numeric_positional_defaults() {
|
||||||
|
let args = vec!["termato".to_string(), "abc".to_string()];
|
||||||
|
assert_eq!(
|
||||||
|
Cli::parse_args(args),
|
||||||
|
CliAction::Run(CliConfig {
|
||||||
|
work_mins: 25,
|
||||||
|
break_mins: 5,
|
||||||
|
enable_notifications: false,
|
||||||
|
enable_visualizer: false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
-49
@@ -1,4 +1,5 @@
|
|||||||
mod app;
|
mod app;
|
||||||
|
mod args;
|
||||||
mod audio;
|
mod audio;
|
||||||
mod events;
|
mod events;
|
||||||
mod msg;
|
mod msg;
|
||||||
@@ -24,26 +25,25 @@ use msg::Message;
|
|||||||
use terminal::TerminalGuard;
|
use terminal::TerminalGuard;
|
||||||
use ui::render_app;
|
use ui::render_app;
|
||||||
|
|
||||||
|
use args::{Cli, CliAction};
|
||||||
|
|
||||||
fn main() -> Result<(), io::Error> {
|
fn main() -> Result<(), io::Error> {
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let config = match Cli::parse() {
|
||||||
|
CliAction::PrintHelp => {
|
||||||
if args.iter().any(|arg| arg == "-h" || arg == "--help") {
|
Cli::print_help();
|
||||||
println!("Usage: termato [options] [work_minutes] [break_minutes]");
|
return Ok(());
|
||||||
println!();
|
}
|
||||||
println!("Options:");
|
CliAction::PrintVersion => {
|
||||||
println!(" -n, --notify Enable desktop notifications");
|
Cli::print_version();
|
||||||
println!(" -h, --help Print this help message");
|
return Ok(());
|
||||||
println!(" -v, --version Print the version number");
|
}
|
||||||
println!(" -z, --visualizer Display visualizer of playing audio");
|
CliAction::Error(err) => {
|
||||||
println!();
|
eprintln!("Error: {}", err);
|
||||||
println!("Defaults: work_minutes = 25, break_minutes = 5");
|
eprintln!("Run 'termato --help' for usage instructions.");
|
||||||
return Ok(());
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
CliAction::Run(cfg) => cfg,
|
||||||
if args.iter().any(|arg| arg == "-v" || arg == "--version") {
|
};
|
||||||
println!("termato version {}", env!("CARGO_PKG_VERSION"));
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let _guard = TerminalGuard::new()?;
|
let _guard = TerminalGuard::new()?;
|
||||||
|
|
||||||
@@ -51,41 +51,15 @@ fn main() -> Result<(), io::Error> {
|
|||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
let font = Toilet::smblock().unwrap();
|
let font = Toilet::smblock().unwrap();
|
||||||
|
|
||||||
let enable_visualizer = args.iter().any(|arg| arg == "-z" || arg == "--visualizer");
|
let visualizer = if config.enable_visualizer {
|
||||||
let enable_notifications = args.iter().any(|arg| arg == "-n" || arg == "--notify");
|
|
||||||
|
|
||||||
let visualizer = if enable_visualizer {
|
|
||||||
Some(AudioVisualizer::new(64))
|
Some(AudioVisualizer::new(64))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_flag = |arg: &str| {
|
let mut termato = Termato::new(config.work_mins, config.break_mins)
|
||||||
arg == "-n"
|
.with_notifications(config.enable_notifications)
|
||||||
|| arg == "--notify"
|
.with_visualizer(config.enable_visualizer);
|
||||||
|| arg == "-z"
|
|
||||||
|| arg == "--visualizer"
|
|
||||||
|| arg == "-h"
|
|
||||||
|| arg == "--help"
|
|
||||||
|| arg == "-v"
|
|
||||||
|| arg == "--version"
|
|
||||||
};
|
|
||||||
|
|
||||||
let positional_args: Vec<&String> = args.iter().skip(1).filter(|arg| !is_flag(arg)).collect();
|
|
||||||
|
|
||||||
let work_mins = positional_args
|
|
||||||
.first()
|
|
||||||
.and_then(|s| s.parse().ok())
|
|
||||||
.unwrap_or(25);
|
|
||||||
|
|
||||||
let break_mins = positional_args
|
|
||||||
.get(1)
|
|
||||||
.and_then(|s| s.parse().ok())
|
|
||||||
.unwrap_or(5);
|
|
||||||
|
|
||||||
let mut termato = Termato::new(work_mins, break_mins)
|
|
||||||
.with_notifications(enable_notifications)
|
|
||||||
.with_visualizer(enable_visualizer);
|
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user