ability to overwrite and add to profile arguments from command line

This commit is contained in:
aaron-jack-manning 2022-09-05 11:14:18 +10:00
parent 4056cd2a60
commit 130fab27d7
3 changed files with 68 additions and 24 deletions

View File

@ -112,12 +112,12 @@ pub struct ListOptions {
/// Which columns to include. /// Which columns to include.
#[clap(short, value_enum)] #[clap(short, value_enum)]
pub column : Vec<Column>, pub column : Vec<Column>,
/// Field to order by. /// Field to order by [default: id].
#[clap(long, value_enum, default_value_t=OrderBy::Id)] #[clap(long, value_enum)]
pub order_by : OrderBy, pub order_by : Option<OrderBy>,
/// Sort ascending on descending. /// Sort ascending on descending [default: asc].
#[clap(long, value_enum, default_value_t=Order::Asc)] #[clap(long, value_enum)]
pub order : Order, pub order : Option<Order>,
/// Tags to include. /// Tags to include.
#[clap(short, long)] #[clap(short, long)]
pub tag : Vec<String>, pub tag : Vec<String>,
@ -150,14 +150,14 @@ pub struct ListOptions {
pub no_dependents : bool, pub no_dependents : bool,
} }
#[derive(Default, Clone, Debug, PartialEq, Eq, clap::ValueEnum, serde::Serialize, serde::Deserialize)] #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, clap::ValueEnum, serde::Serialize, serde::Deserialize)]
pub enum Order { pub enum Order {
#[default] #[default]
Asc, Asc,
Desc, Desc,
} }
#[derive(Hash, Clone, Debug, PartialEq, Eq, clap::ValueEnum, serde::Serialize, serde::Deserialize)] #[derive(Hash, Clone, Copy, Debug, PartialEq, Eq, clap::ValueEnum, serde::Serialize, serde::Deserialize)]
pub enum Column { pub enum Column {
Due, Due,
Priority, Priority,
@ -167,7 +167,7 @@ pub enum Column {
Status, Status,
} }
#[derive(Default, Clone, Debug, PartialEq, Eq, clap::ValueEnum, serde::Serialize, serde::Deserialize)] #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, clap::ValueEnum, serde::Serialize, serde::Deserialize)]
pub enum OrderBy { pub enum OrderBy {
#[default] #[default]
Id, Id,

View File

@ -171,13 +171,14 @@ fn program() -> Result<(), error::Error> {
task.save()?; task.save()?;
println!("Marked task {} as complete", format::id(id)); println!("Marked task {} as complete", format::id(id));
}, },
Command::List { profile, options } => { Command::List { profile : profile_name, options : additional } => {
let options = match profile { let options = match profile_name {
Some(profile) => { Some(profile_name) => {
config.get_profile(&profile)? let profile = config.get_profile(&profile_name)?;
ListOptions::combine(profile, &additional)
}, },
None => { None => {
&options additional
} }
}; };
tasks::list(options, vault_folder, &state)?; tasks::list(options, vault_folder, &state)?;

View File

@ -1,3 +1,4 @@
use crate::args;
use crate::error; use crate::error;
use crate::state; use crate::state;
use crate::format; use crate::format;
@ -355,8 +356,50 @@ fn compare_due_dates<T : Ord>(first : &Option<T>, second : &Option<T>) -> cmp::O
} }
} }
impl args::ListOptions {
/// Combines list options coming from a profile and from the additional arguments given. Order
/// of the arguments provided matters, hence the argument names (because optional arguments
/// from the profile are overwritten by the additional arguments).
pub fn combine(profile : &Self, additional : &Self) -> Self {
/// Joins two vectors together one after the other, creating a new allocation.
fn concat<T : Clone>(a : &Vec<T>, b : &Vec<T>) -> Vec<T> {
let mut a = a.clone();
a.extend(b.iter().cloned());
a
}
/// Takes two options, and prioritises the second if it is provided in the output, using
/// the first as a fallback, and returning None if both are None.
fn join_options<T : Clone>(a : &Option<T>, b : &Option<T>) -> Option<T> {
match (a, b) {
(Some(_), Some(b)) => Some(b.clone()),
(Some(a), None) => Some(a.clone()),
(None, Some(b)) => Some(b.clone()),
(None, None) => None,
}
}
Self {
column : concat(&profile.column, &additional.column),
order_by : join_options(&profile.order_by, &additional.order_by),
order : join_options(&profile.order, &profile.order),
tag : concat(&profile.tag, &additional.tag),
exclude_tag : concat(&profile.exclude_tag, &additional.exclude_tag),
priority : concat(&profile.priority, &additional.priority),
due_before : join_options(&profile.due_before, &additional.due_before),
due_after : join_options(&profile.due_after, &additional.due_after),
created_before : join_options(&profile.created_before, &additional.created_before),
created_after : join_options(&profile.created_after, &additional.created_after),
include_completed : profile.include_completed || additional.include_completed,
no_dependencies : profile.no_dependencies || additional.no_dependencies,
no_dependents : profile.no_dependents || additional.no_dependents,
}
}
}
/// Lists all tasks in the specified vault. /// Lists all tasks in the specified vault.
pub fn list(options : &super::ListOptions, vault_folder : &path::Path, state : &state::State) -> Result<(), error::Error> { pub fn list(mut options : args::ListOptions, vault_folder : &path::Path, state : &state::State) -> Result<(), error::Error> {
let mut table = comfy_table::Table::new(); let mut table = comfy_table::Table::new();
table table
@ -446,9 +489,9 @@ pub fn list(options : &super::ListOptions, vault_folder : &path::Path, state : &
// Sort the tasks. // Sort the tasks.
use super::{OrderBy, Order}; use super::{OrderBy, Order};
match options.order_by { match options.order_by.unwrap_or_default() {
OrderBy::Id => { OrderBy::Id => {
match options.order { match options.order.unwrap_or_default() {
Order::Asc => { Order::Asc => {
tasks.sort_by(|t1, t2| t1.data.id.cmp(&t2.data.id)); tasks.sort_by(|t1, t2| t1.data.id.cmp(&t2.data.id));
}, },
@ -458,7 +501,7 @@ pub fn list(options : &super::ListOptions, vault_folder : &path::Path, state : &
} }
}, },
OrderBy::Name => { OrderBy::Name => {
match options.order { match options.order.unwrap_or_default() {
Order::Asc => { Order::Asc => {
tasks.sort_by(|t1, t2| t1.data.name.cmp(&t2.data.name)); tasks.sort_by(|t1, t2| t1.data.name.cmp(&t2.data.name));
}, },
@ -468,7 +511,7 @@ pub fn list(options : &super::ListOptions, vault_folder : &path::Path, state : &
} }
}, },
OrderBy::Due => { OrderBy::Due => {
match options.order { match options.order.unwrap_or_default() {
Order::Asc => { Order::Asc => {
tasks.sort_by(|t1, t2| compare_due_dates(&t1.data.due, &t2.data.due)); tasks.sort_by(|t1, t2| compare_due_dates(&t1.data.due, &t2.data.due));
}, },
@ -478,7 +521,7 @@ pub fn list(options : &super::ListOptions, vault_folder : &path::Path, state : &
} }
}, },
OrderBy::Priority => { OrderBy::Priority => {
match options.order { match options.order.unwrap_or_default() {
Order::Asc => { Order::Asc => {
tasks.sort_by(|t1, t2| t1.data.priority.cmp(&t2.data.priority)); tasks.sort_by(|t1, t2| t1.data.priority.cmp(&t2.data.priority));
}, },
@ -488,7 +531,7 @@ pub fn list(options : &super::ListOptions, vault_folder : &path::Path, state : &
} }
}, },
OrderBy::Created => { OrderBy::Created => {
match options.order { match options.order.unwrap_or_default() {
Order::Asc => { Order::Asc => {
tasks.sort_by(|t1, t2| t1.data.created.cmp(&t2.data.created)); tasks.sort_by(|t1, t2| t1.data.created.cmp(&t2.data.created));
}, },
@ -498,7 +541,7 @@ pub fn list(options : &super::ListOptions, vault_folder : &path::Path, state : &
} }
}, },
OrderBy::Tracked => { OrderBy::Tracked => {
match options.order { match options.order.unwrap_or_default() {
Order::Asc => { Order::Asc => {
tasks.sort_by(|t1, t2| TimeEntry::total(&t1.data.time_entries).cmp(&TimeEntry::total(&t2.data.time_entries))); tasks.sort_by(|t1, t2| TimeEntry::total(&t1.data.time_entries).cmp(&TimeEntry::total(&t2.data.time_entries)));
}, },
@ -513,7 +556,7 @@ pub fn list(options : &super::ListOptions, vault_folder : &path::Path, state : &
let mut headers = vec!["Id", "Name"]; let mut headers = vec!["Id", "Name"];
// Remove duplicate columns. // Remove duplicate columns.
let unique_columns : Vec<_> = { options.column = {
let mut columns = HashSet::new(); let mut columns = HashSet::new();
options.column.clone() options.column.clone()
@ -531,7 +574,7 @@ pub fn list(options : &super::ListOptions, vault_folder : &path::Path, state : &
}; };
use super::Column; use super::Column;
for column in &unique_columns { for column in &options.column {
match column { match column {
Column::Tracked => { Column::Tracked => {
headers.push("Tracked"); headers.push("Tracked");