use crate::action::*;
use crate::item::{Item, Urgency};
use crate::list::{Line, List};
use clap::{Arg, ArgMatches, Command};
pub fn get_action() -> Action {
let name = String::from("add");
let mut command = Command::new("add")
.about("Add a task to the todo list")
.after_help("After success, displays the added task.")
.arg(Arg::new("task").help("Task text (may use todo.txt features)"));
command = FileType::TodoTxt.add_args(command);
command = AddActionConfig::add_args(command);
Action { name, command }
}
pub struct AddActionConfig {
pub no_date: bool,
pub no_fixup: bool,
pub urgency: Option<Urgency>,
pub quiet: bool,
pub outputter: Outputter,
}
impl Default for AddActionConfig {
fn default() -> Self {
Self::new()
}
}
impl AddActionConfig {
pub fn new() -> Self {
Self {
no_date: false,
no_fixup: false,
urgency: None,
quiet: false,
outputter: Outputter::default(),
}
}
pub fn add_args(cmd: Command) -> Command {
let cmd = cmd
.arg(
Arg::new("no-date")
.num_args(0)
.long("no-date")
.aliases(["nodate"])
.help(
"Don't automatically add a creation date to the task",
),
)
.arg(
Arg::new("no-fixup")
.num_args(0)
.long("no-fixup")
.aliases(["nofixup"])
.help("Don't try to fix task syntax"),
)
.arg(
Arg::new("quiet")
.num_args(0)
.long("quiet")
.help("Quieter output"),
)
.arg(
Arg::new("today")
.num_args(0)
.short('T')
.long("today")
.help("Include a due date of today"),
)
.arg(
Arg::new("soon")
.num_args(0)
.short('S')
.long("soon")
.aliases(["overmorrow"])
.help("Include a due date of overmorrow"),
)
.arg(
Arg::new("next-week")
.num_args(0)
.short('W')
.long("next-week")
.aliases(["nextweek"])
.help("Include a due date the end of next week"),
)
.arg(
Arg::new("next-month")
.num_args(0)
.short('M')
.long("next-month")
.aliases(["nextmonth"])
.help("Include a due date the end of next month"),
);
Outputter::add_args(cmd)
}
pub fn from_argmatches(args: &ArgMatches) -> Self {
let no_date = *args.get_one::<bool>("no-date").unwrap();
let no_fixup = *args.get_one::<bool>("no-fixup").unwrap();
let urgency = if *args.get_one::<bool>("today").unwrap() {
Some(Urgency::Today)
} else if *args.get_one::<bool>("soon").unwrap() {
Some(Urgency::Soon)
} else if *args.get_one::<bool>("next-week").unwrap() {
Some(Urgency::NextWeek)
} else if *args.get_one::<bool>("next-month").unwrap() {
Some(Urgency::NextMonth)
} else {
None
};
let quiet = *args.get_one::<bool>("quiet").unwrap();
let outputter = Outputter::from_argmatches(args);
Self {
no_date,
no_fixup,
urgency,
quiet,
outputter,
}
}
}
#[cfg(not(tarpaulin_include))]
pub fn execute(args: &ArgMatches) {
let mut cfg = AddActionConfig::from_argmatches(args);
let input = args.get_one::<String>("task").unwrap();
let new_line = process_line(input, &cfg);
if !cfg.quiet {
cfg.outputter
.write_item(new_line.item.as_ref().unwrap());
}
let filename = FileType::TodoTxt.filename(args);
List::append_lines_to_url(filename, Vec::from([&new_line]));
}
pub fn process_line(input: &str, cfg: &AddActionConfig) -> Line {
let mut item = Item::parse(input);
if item.creation_date().is_none() && !cfg.no_date {
item.set_creation_date(chrono::Utc::now().date_naive());
}
if let Some(u) = cfg.urgency {
item.set_urgency(u);
}
if !cfg.no_fixup {
item = item.fixup(!cfg.quiet);
}
Line::from_item(item)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::list::LineKind;
#[test]
fn test_get_action() {
assert_eq!(String::from("add"), get_action().name);
}
#[test]
fn test_process_line() {
let cfg = AddActionConfig {
no_date: true,
no_fixup: true,
urgency: None,
quiet: true,
outputter: Outputter::default(),
};
let line = process_line(&String::from("ABC start:today"), &cfg);
assert_eq!(LineKind::Item, line.kind);
let item = line.item.unwrap();
assert_eq!("ABC start:today", item.description());
assert_eq!(None, item.creation_date());
assert_eq!("today", item.kv().get("start").unwrap());
let cfg = AddActionConfig {
no_date: false,
no_fixup: false,
urgency: Some(Urgency::Today),
quiet: true,
outputter: Outputter::default(),
};
let line = process_line(&String::from("ABC start:today"), &cfg);
assert_eq!(LineKind::Item, line.kind);
let item = line.item.unwrap();
assert!(item.creation_date().is_some());
assert_eq!(item.creation_date(), item.start_date());
assert_eq!(item.creation_date(), item.due_date());
assert_ne!("today", item.kv().get("start").unwrap());
}
}
#[cfg(test)]
mod tests_add_action_config {
use super::*;
#[test]
fn test_new() {
let cfg = AddActionConfig::new();
assert_eq!(false, cfg.no_date);
assert_eq!(false, cfg.no_fixup);
assert_eq!(None, cfg.urgency);
assert_eq!(false, cfg.quiet);
}
#[test]
fn test_default() {
let cfg = AddActionConfig::default();
assert_eq!(false, cfg.no_date);
assert_eq!(false, cfg.no_fixup);
assert_eq!(None, cfg.urgency);
assert_eq!(false, cfg.quiet);
}
#[test]
fn test_from_argmatches() {
let matches = get_action()
.command
.get_matches_from(vec!["add"]);
let cfg = AddActionConfig::from_argmatches(&matches);
assert_eq!(false, cfg.no_date);
assert_eq!(false, cfg.no_fixup);
assert_eq!(None, cfg.urgency);
assert_eq!(false, cfg.quiet);
let matches = get_action().command.get_matches_from(vec![
"add",
"-T",
"--no-date",
]);
let cfg = AddActionConfig::from_argmatches(&matches);
assert_eq!(true, cfg.no_date);
assert_eq!(false, cfg.no_fixup);
assert_eq!(Some(Urgency::Today), cfg.urgency);
assert_eq!(false, cfg.quiet);
let matches = get_action().command.get_matches_from(vec![
"add",
"-S",
"--no-fixup",
]);
let cfg = AddActionConfig::from_argmatches(&matches);
assert_eq!(false, cfg.no_date);
assert_eq!(true, cfg.no_fixup);
assert_eq!(Some(Urgency::Soon), cfg.urgency);
assert_eq!(false, cfg.quiet);
let matches = get_action()
.command
.get_matches_from(vec!["add", "-W"]);
let cfg = AddActionConfig::from_argmatches(&matches);
assert_eq!(false, cfg.no_date);
assert_eq!(false, cfg.no_fixup);
assert_eq!(Some(Urgency::NextWeek), cfg.urgency);
assert_eq!(false, cfg.quiet);
let matches = get_action().command.get_matches_from(vec![
"add",
"--no-fixup",
"-M",
"--no-date",
"--quiet",
]);
let cfg = AddActionConfig::from_argmatches(&matches);
assert_eq!(true, cfg.no_date);
assert_eq!(true, cfg.no_fixup);
assert_eq!(Some(Urgency::NextMonth), cfg.urgency);
assert_eq!(true, cfg.quiet);
}
}