2026-07-28 09:55:17 -05:00
|
|
|
use std::env;
|
|
|
|
|
|
2026-07-28 10:20:37 -05:00
|
|
|
use crate::constants::{DEFAULT_BREAK_MINS, DEFAULT_WORK_MINS};
|
|
|
|
|
|
|
|
|
|
pub const FLAG_HELP: &str = "-h";
|
|
|
|
|
pub const FLAG_HELP_LONG: &str = "--help";
|
|
|
|
|
pub const FLAG_VERSION: &str = "-v";
|
|
|
|
|
pub const FLAG_VERSION_LONG: &str = "--version";
|
|
|
|
|
pub const FLAG_NOTIFY: &str = "-n";
|
|
|
|
|
pub const FLAG_NOTIFY_LONG: &str = "--notify";
|
|
|
|
|
pub const FLAG_VISUALIZER: &str = "-z";
|
|
|
|
|
pub const FLAG_VISUALIZER_LONG: &str = "--visualizer";
|
|
|
|
|
|
2026-07-28 09:55:17 -05:00
|
|
|
#[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() {
|
2026-07-28 10:20:37 -05:00
|
|
|
FLAG_HELP | FLAG_HELP_LONG => return CliAction::PrintHelp,
|
|
|
|
|
FLAG_VERSION | FLAG_VERSION_LONG => return CliAction::PrintVersion,
|
|
|
|
|
FLAG_NOTIFY | FLAG_NOTIFY_LONG => enable_notifications = true,
|
|
|
|
|
FLAG_VISUALIZER | FLAG_VISUALIZER_LONG => enable_visualizer = true,
|
2026-07-28 09:55:17 -05:00
|
|
|
"--" => {}
|
|
|
|
|
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();
|
|
|
|
|
|
2026-07-28 10:20:37 -05:00
|
|
|
let work_mins = numbers.first().copied().unwrap_or(DEFAULT_WORK_MINS);
|
|
|
|
|
let break_mins = numbers.get(1).copied().unwrap_or(DEFAULT_BREAK_MINS);
|
2026-07-28 09:55:17 -05:00
|
|
|
|
|
|
|
|
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]");
|
2026-07-28 10:20:37 -05:00
|
|
|
|
2026-07-28 09:55:17 -05:00
|
|
|
println!();
|
2026-07-28 10:20:37 -05:00
|
|
|
|
2026-07-28 09:55:17 -05:00
|
|
|
println!("Options:");
|
2026-07-28 10:20:37 -05:00
|
|
|
|
|
|
|
|
println!(
|
|
|
|
|
" {:<19} Enable desktop notifications",
|
|
|
|
|
format_args!("{}, {}", FLAG_NOTIFY, FLAG_NOTIFY_LONG)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
println!(
|
|
|
|
|
" {:<19} Display visualizer of playing audio",
|
|
|
|
|
format_args!("{}, {}", FLAG_VISUALIZER, FLAG_VISUALIZER_LONG)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
println!(
|
|
|
|
|
" {:<19} Print this help message",
|
|
|
|
|
format_args!("{}, {}", FLAG_HELP, FLAG_HELP_LONG)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
println!(
|
|
|
|
|
" {:<19} Print the version number",
|
|
|
|
|
format_args!("{}, {}", FLAG_VERSION, FLAG_VERSION_LONG)
|
|
|
|
|
);
|
|
|
|
|
|
2026-07-28 09:55:17 -05:00
|
|
|
println!();
|
2026-07-28 10:20:37 -05:00
|
|
|
|
|
|
|
|
println!(
|
|
|
|
|
"Defaults: work_minutes = {}, break_minutes = {}",
|
|
|
|
|
DEFAULT_WORK_MINS, DEFAULT_BREAK_MINS
|
|
|
|
|
);
|
2026-07-28 09:55:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|