implement t configure
This commit is contained in:
parent
4ebf0681c2
commit
23a3603628
|
@ -23,6 +23,7 @@ pub mod kill;
|
||||||
pub mod now;
|
pub mod now;
|
||||||
pub mod edit;
|
pub mod edit;
|
||||||
pub mod archive;
|
pub mod archive;
|
||||||
|
pub mod configure;
|
||||||
|
|
||||||
pub trait Command<'a> {
|
pub trait Command<'a> {
|
||||||
type Args: TryFrom<&'a ArgMatches<'a>>;
|
type Args: TryFrom<&'a ArgMatches<'a>>;
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use clap::ArgMatches;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
|
use crate::database::Database;
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
use crate::commands::Command;
|
||||||
|
use crate::config::{Config, WeekDay};
|
||||||
|
use crate::formatters::Formatter;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Args {
|
||||||
|
database_file: Option<PathBuf>,
|
||||||
|
round_in_seconds: Option<u32>,
|
||||||
|
append_notes_delimiter: Option<String>,
|
||||||
|
formatter_search_paths: Option<Vec<PathBuf>>,
|
||||||
|
default_formatter: Option<Formatter>,
|
||||||
|
auto_sheet: Option<String>,
|
||||||
|
auto_sheet_search_paths: Option<Vec<PathBuf>>,
|
||||||
|
default_command: Option<String>,
|
||||||
|
auto_checkout: Option<bool>,
|
||||||
|
require_note: Option<bool>,
|
||||||
|
note_editor: Option<String>,
|
||||||
|
week_start: Option<WeekDay>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Args {
|
||||||
|
/// returns true only if no argument was passed other that possibly --id.
|
||||||
|
/// This means that an edit was requested without specifying what to edit,
|
||||||
|
/// therefore let's edit the note because why not
|
||||||
|
fn none_given(&self) -> bool {
|
||||||
|
!(
|
||||||
|
self.database_file.is_some() ||
|
||||||
|
self.round_in_seconds.is_some() ||
|
||||||
|
self.append_notes_delimiter.is_some() ||
|
||||||
|
self.formatter_search_paths.is_some() ||
|
||||||
|
self.default_formatter.is_some() ||
|
||||||
|
self.auto_sheet.is_some() ||
|
||||||
|
self.auto_sheet_search_paths.is_some() ||
|
||||||
|
self.default_command.is_some() ||
|
||||||
|
self.auto_checkout.is_some() ||
|
||||||
|
self.require_note.is_some() ||
|
||||||
|
self.note_editor.is_some() ||
|
||||||
|
self.week_start.is_some()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn yes_no_none(matches: &ArgMatches, opt: &str) -> Option<bool> {
|
||||||
|
if matches.is_present(opt) {
|
||||||
|
Some(true)
|
||||||
|
} else if matches.is_present(&format!("no_{}", opt)) {
|
||||||
|
Some(false)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a ArgMatches<'a>> for Args {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(matches: &'a ArgMatches) -> Result<Self> {
|
||||||
|
Ok(Args {
|
||||||
|
database_file: matches.value_of("database_file").map(|e| e.into()),
|
||||||
|
round_in_seconds: matches.value_of("round_in_seconds").map(|v| v.parse().unwrap()),
|
||||||
|
append_notes_delimiter: matches.value_of("append_notes_delimiter").map(|v| v.to_owned()),
|
||||||
|
formatter_search_paths: matches.values_of("formatter_search_paths").map(|v| v.map(|a| a.into()).collect()),
|
||||||
|
default_formatter: matches.value_of("default_formatter").map(|v| v.parse().unwrap()),
|
||||||
|
auto_sheet: matches.value_of("auto_sheet").map(|v| v.to_owned()),
|
||||||
|
auto_sheet_search_paths: matches.values_of("auto_sheet_search_paths").map(|v| v.map(|a| a.into()).collect()),
|
||||||
|
default_command: matches.value_of("default_command").map(|v| v.to_owned()),
|
||||||
|
auto_checkout: yes_no_none(matches, "auto_checkout"),
|
||||||
|
require_note: yes_no_none(matches, "require_note"),
|
||||||
|
note_editor: matches.value_of("note_editor").map(|v| v.to_owned()),
|
||||||
|
week_start: matches.value_of("week_start").map(|w| w.parse()).transpose()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ConfigureCommand {}
|
||||||
|
|
||||||
|
impl<'a> Command<'a> for ConfigureCommand {
|
||||||
|
type Args = Args;
|
||||||
|
|
||||||
|
fn handle<D, O, E>(args: Args, _db: &mut D, out: &mut O, _err: &mut E, config: &Config, _now: DateTime<Utc>) -> Result<()>
|
||||||
|
where
|
||||||
|
D: Database,
|
||||||
|
O: Write,
|
||||||
|
E: Write,
|
||||||
|
{
|
||||||
|
if args.none_given() {
|
||||||
|
if let Some(path) = config.path.as_deref() {
|
||||||
|
writeln!(out, "{}", path.display())?;
|
||||||
|
} else {
|
||||||
|
writeln!(out, "Config file is in memory")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let mut new_config = config.clone();
|
||||||
|
|
||||||
|
if let Some(path) = args.database_file {
|
||||||
|
new_config.database_file = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = args.round_in_seconds {
|
||||||
|
new_config.round_in_seconds = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = args.append_notes_delimiter {
|
||||||
|
new_config.append_notes_delimiter = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = args.formatter_search_paths {
|
||||||
|
new_config.formatter_search_paths = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = args.default_formatter {
|
||||||
|
new_config.default_formatter = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = args.auto_sheet {
|
||||||
|
new_config.auto_sheet = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = args.auto_sheet_search_paths {
|
||||||
|
new_config.auto_sheet_search_paths = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = args.default_command {
|
||||||
|
new_config.default_command = Some(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = args.auto_checkout {
|
||||||
|
new_config.auto_checkout = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = args.require_note {
|
||||||
|
new_config.require_note = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = args.note_editor {
|
||||||
|
new_config.note_editor = Some(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = args.week_start {
|
||||||
|
new_config.week_start = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(path) = config.path.as_deref() {
|
||||||
|
let output = new_config.write(path)?;
|
||||||
|
|
||||||
|
writeln!(out, "Your new config:\n")?;
|
||||||
|
|
||||||
|
out.write_all(output.as_bytes())?;
|
||||||
|
} else {
|
||||||
|
writeln!(out, "Your config file is in memory and cannot be written to")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,12 +2,14 @@ use std::env;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::fs::{File, create_dir_all};
|
use std::fs::{File, create_dir_all};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use directories::{UserDirs, ProjectDirs};
|
use directories::{UserDirs, ProjectDirs};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use toml::to_string;
|
use toml::to_string;
|
||||||
|
|
||||||
use crate::{error::{Result, Error::*}, formatters::Formatter};
|
use crate::{error::{Result, Error::{self, *}}, formatters::Formatter};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
|
||||||
pub enum WeekDay {
|
pub enum WeekDay {
|
||||||
|
@ -20,8 +22,31 @@ pub enum WeekDay {
|
||||||
Sunday,
|
Sunday,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
impl FromStr for WeekDay {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<WeekDay> {
|
||||||
|
Ok(match s.to_lowercase().as_str() {
|
||||||
|
"monday" => WeekDay::Monday,
|
||||||
|
"tuesday" => WeekDay::Tuesday,
|
||||||
|
"wednesday" => WeekDay::Wednesday,
|
||||||
|
"thursday" => WeekDay::Thursday,
|
||||||
|
"friday" => WeekDay::Friday,
|
||||||
|
"saturday" => WeekDay::Saturday,
|
||||||
|
"sunday" => WeekDay::Sunday,
|
||||||
|
x => return Err(InvalidWeekDaySpec(x.to_owned())),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
#[serde(skip)]
|
||||||
|
pub path: Option<PathBuf>,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub extra: HashMap<String, serde_json::Value>,
|
||||||
|
|
||||||
/// Absolute path to the sqlite database
|
/// Absolute path to the sqlite database
|
||||||
pub database_file: PathBuf, // "/home/user/.timetrap.db"
|
pub database_file: PathBuf, // "/home/user/.timetrap.db"
|
||||||
|
|
||||||
|
@ -119,6 +144,38 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write<P: AsRef<Path>>(&self, path: P) -> Result<String> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let ext = path.extension().map(|e| e.to_str()).flatten();
|
||||||
|
|
||||||
|
let output = match ext {
|
||||||
|
Some("toml") => {
|
||||||
|
toml::to_string(self).unwrap()
|
||||||
|
},
|
||||||
|
Some("yaml" | "yml") => {
|
||||||
|
serde_yaml::to_string(self).unwrap()
|
||||||
|
},
|
||||||
|
Some(ext) => {
|
||||||
|
return Err(Error::GenericFailure(format!("\
|
||||||
|
Your config file has '{}' extension which I don't understand, so I'd rather not
|
||||||
|
mess with its contents. If its formatted as toml use '.toml' extension. If it is
|
||||||
|
yaml use '.yml' or '.yaml'", ext)));
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
return Err(Error::GenericFailure(format!("\
|
||||||
|
Your config file, located at {} has no extension so I'll not write to it. Please
|
||||||
|
set it an extension like '.toml' or '.yaml' (and ensure it matches the content's
|
||||||
|
format)", path.display())));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut f = File::create(path)?;
|
||||||
|
|
||||||
|
f.write_all(output.as_bytes())?;
|
||||||
|
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
|
||||||
fn read_from_yaml<P: AsRef<Path>>(path: P) -> Result<Config> {
|
fn read_from_yaml<P: AsRef<Path>>(path: P) -> Result<Config> {
|
||||||
let path: PathBuf = path.as_ref().into();
|
let path: PathBuf = path.as_ref().into();
|
||||||
|
|
||||||
|
@ -133,9 +190,13 @@ impl Config {
|
||||||
error: e,
|
error: e,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
serde_yaml::from_str(&contents).map_err(|error| YamlError {
|
let mut config: Config = serde_yaml::from_str(&contents).map_err(|error| YamlError {
|
||||||
path, error
|
path: path.clone(), error
|
||||||
})
|
})?;
|
||||||
|
|
||||||
|
config.path = Some(path);
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_from_toml<P: AsRef<Path>>(path: P) -> Result<Config> {
|
fn read_from_toml<P: AsRef<Path>>(path: P) -> Result<Config> {
|
||||||
|
@ -152,9 +213,13 @@ impl Config {
|
||||||
error: e,
|
error: e,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
toml::from_str(&contents).map_err(|error| TomlError {
|
let mut config: Config = toml::from_str(&contents).map_err(|error| TomlError {
|
||||||
path, error
|
path: path.clone(), error
|
||||||
})
|
})?;
|
||||||
|
|
||||||
|
config.path = Some(path);
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assume the configuration file does not exist, create a default one and
|
/// Assume the configuration file does not exist, create a default one and
|
||||||
|
@ -177,6 +242,7 @@ impl Config {
|
||||||
};
|
};
|
||||||
|
|
||||||
let config = Config {
|
let config = Config {
|
||||||
|
path: Some(config_filename.to_owned()),
|
||||||
database_file: database_filename,
|
database_file: database_filename,
|
||||||
formatter_search_paths: vec![formatter_search_paths],
|
formatter_search_paths: vec![formatter_search_paths],
|
||||||
auto_sheet_search_paths: vec![auto_sheet_search_paths],
|
auto_sheet_search_paths: vec![auto_sheet_search_paths],
|
||||||
|
@ -200,6 +266,9 @@ impl Config {
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Config {
|
fn default() -> Config {
|
||||||
Config {
|
Config {
|
||||||
|
path: None,
|
||||||
|
extra: HashMap::new(),
|
||||||
|
|
||||||
database_file: PathBuf::new(), // see above for definition
|
database_file: PathBuf::new(), // see above for definition
|
||||||
round_in_seconds: 900,
|
round_in_seconds: 900,
|
||||||
append_notes_delimiter: " ".into(),
|
append_notes_delimiter: " ".into(),
|
||||||
|
|
|
@ -10,6 +10,12 @@ pub enum Error {
|
||||||
#[error("The subcommand '{0}' is not implemented")]
|
#[error("The subcommand '{0}' is not implemented")]
|
||||||
UnimplementedCommand(String),
|
UnimplementedCommand(String),
|
||||||
|
|
||||||
|
/// Sometimes a specific variant for an error is not necessary if the error
|
||||||
|
/// can only happen in one place in the code. This is that the generic error
|
||||||
|
/// is for and nothing else.
|
||||||
|
#[error("Error: {0}")]
|
||||||
|
GenericFailure(String),
|
||||||
|
|
||||||
#[error("Sqlite error: {0}")]
|
#[error("Sqlite error: {0}")]
|
||||||
Sqlite(#[from] rusqlite::Error),
|
Sqlite(#[from] rusqlite::Error),
|
||||||
|
|
||||||
|
@ -97,6 +103,9 @@ query it using t backend or the sqlite3 command provided by your system.")]
|
||||||
#[error("Could not understand '{0}' as a month specifier. Try 'this', 'last', or any month name like 'january' or 'nov'")]
|
#[error("Could not understand '{0}' as a month specifier. Try 'this', 'last', or any month name like 'january' or 'nov'")]
|
||||||
InvalidMonthSpec(String),
|
InvalidMonthSpec(String),
|
||||||
|
|
||||||
|
#[error("Could not understand '{0}' as a week day. Try 'monday' or 'TuesDay'")]
|
||||||
|
InvalidWeekDaySpec(String),
|
||||||
|
|
||||||
#[error("An error ocurred while trying to read entries from the database.
|
#[error("An error ocurred while trying to read entries from the database.
|
||||||
|
|
||||||
In the row with id {id} the data at column '{col}' has a value that is not a
|
In the row with id {id} the data at column '{col}' has a value that is not a
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub mod json;
|
||||||
pub mod ids;
|
pub mod ids;
|
||||||
pub mod ical;
|
pub mod ical;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum Formatter {
|
pub enum Formatter {
|
||||||
Text,
|
Text,
|
||||||
|
@ -57,9 +57,7 @@ impl FromStr for Formatter {
|
||||||
type Err = error::Error;
|
type Err = error::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> error::Result<Formatter> {
|
fn from_str(s: &str) -> error::Result<Formatter> {
|
||||||
let lower = s.to_lowercase();
|
Ok(match s.to_lowercase().as_str() {
|
||||||
|
|
||||||
Ok(match &*lower {
|
|
||||||
"text" => Formatter::Text,
|
"text" => Formatter::Text,
|
||||||
"csv" => Formatter::Csv,
|
"csv" => Formatter::Csv,
|
||||||
"json" => Formatter::Json,
|
"json" => Formatter::Json,
|
||||||
|
|
37
src/main.rs
37
src/main.rs
|
@ -15,6 +15,7 @@ use tiempo::commands::{
|
||||||
month::MonthCommand, list::ListCommand, out::OutCommand,
|
month::MonthCommand, list::ListCommand, out::OutCommand,
|
||||||
resume::ResumeCommand, backend::BackendCommand, kill::KillCommand,
|
resume::ResumeCommand, backend::BackendCommand, kill::KillCommand,
|
||||||
now::NowCommand, edit::EditCommand, archive::ArchiveCommand,
|
now::NowCommand, edit::EditCommand, archive::ArchiveCommand,
|
||||||
|
configure::ConfigureCommand,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn error_trap(matches: ArgMatches) -> error::Result<()> {
|
fn error_trap(matches: ArgMatches) -> error::Result<()> {
|
||||||
|
@ -46,6 +47,7 @@ fn error_trap(matches: ArgMatches) -> error::Result<()> {
|
||||||
("now", Some(matches)) => NowCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
|
("now", Some(matches)) => NowCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
|
||||||
("edit", Some(matches)) => EditCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
|
("edit", Some(matches)) => EditCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
|
||||||
("archive", Some(matches)) => ArchiveCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
|
("archive", Some(matches)) => ArchiveCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
|
||||||
|
("configure", Some(matches)) => ConfigureCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config, now),
|
||||||
|
|
||||||
(cmd, _) => Err(error::Error::UnimplementedCommand(cmd.into())),
|
(cmd, _) => Err(error::Error::UnimplementedCommand(cmd.into())),
|
||||||
}
|
}
|
||||||
|
@ -94,15 +96,16 @@ fn main() {
|
||||||
.help("Use this time instead of now");
|
.help("Use this time instead of now");
|
||||||
|
|
||||||
let num_re = Regex::new(r"^\d+$").unwrap();
|
let num_re = Regex::new(r"^\d+$").unwrap();
|
||||||
|
let is_number = move |v: String| if num_re.is_match(&v) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(format!("the --id arg must be a number. '{}' is not a valid number", v))
|
||||||
|
};
|
||||||
|
|
||||||
let id_arg = Arg::with_name("id")
|
let id_arg = Arg::with_name("id")
|
||||||
.long("id")
|
.long("id")
|
||||||
.takes_value(true).value_name("ID")
|
.takes_value(true).value_name("ID")
|
||||||
.validator(move |v| if num_re.is_match(&v) {
|
.validator(is_number);
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(format!("the --id arg must be a number. '{}' is not a valid number", v))
|
|
||||||
});
|
|
||||||
|
|
||||||
// Now declar this app's cli
|
// Now declar this app's cli
|
||||||
let matches = App::new("Tiempo")
|
let matches = App::new("Tiempo")
|
||||||
|
@ -132,12 +135,12 @@ fn main() {
|
||||||
|
|
||||||
.subcommand(SubCommand::with_name("configure")
|
.subcommand(SubCommand::with_name("configure")
|
||||||
.visible_alias("c")
|
.visible_alias("c")
|
||||||
.about("Configure tiempo. Print path to config file.")
|
.about("Configure tiempo in-place. If no arguments are given it just prints the path to the config file in use.")
|
||||||
.arg(Arg::with_name("round_in_seconds")
|
.arg(Arg::with_name("round_in_seconds")
|
||||||
.long("round-in-seconds")
|
.long("round-in-seconds")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("SECONDS")
|
.value_name("SECONDS")
|
||||||
.help("The duration of time to use for rounding with the -r flag"))
|
.help("The duration of time to use for rounding with the -r flag. Default: 900 (15 m)"))
|
||||||
.arg(Arg::with_name("database_file")
|
.arg(Arg::with_name("database_file")
|
||||||
.long("database-file")
|
.long("database-file")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
@ -147,33 +150,41 @@ fn main() {
|
||||||
.long("append-notes-delimiter")
|
.long("append-notes-delimiter")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("DELIMITER")
|
.value_name("DELIMITER")
|
||||||
.help("delimiter used when appending notes via t edit --append"))
|
.help("delimiter used when appending notes via t edit --append. Default: ' ' (space)"))
|
||||||
.arg(Arg::with_name("formatter_search_paths")
|
.arg(Arg::with_name("formatter_search_paths")
|
||||||
.long("formatter-search-paths")
|
.long("formatter-search-paths")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.multiple(true)
|
||||||
.value_name("PATHS")
|
.value_name("PATHS")
|
||||||
.help("comma separated directories to search for user defined fomatter classes"))
|
.help("comma separated directories to search for user defined fomatter classes"))
|
||||||
.arg(Arg::with_name("default_formatter")
|
.arg(Arg::with_name("default_formatter")
|
||||||
.long("default-formatter")
|
.long("default-formatter")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("FORMATTER")
|
.value_name("FORMATTER")
|
||||||
.help("The format to use when display is invoked without a `--format` option"))
|
.help("The format to use when display is invoked without a `--format` option. Default 'text'"))
|
||||||
.arg(Arg::with_name("require_note")
|
.arg(Arg::with_name("require_note")
|
||||||
.long("require-note")
|
.long("require-note")
|
||||||
.help("Prompt for a note if one isn't provided when checking in"))
|
.help("Prompt for a note if one isn't provided when checking in (default)"))
|
||||||
.arg(Arg::with_name("no_require_note")
|
.arg(Arg::with_name("no_require_note")
|
||||||
.long("no-require-note")
|
.long("no-require-note")
|
||||||
.help("Prompt for a note if one isn't provided when checking in"))
|
.help("Entries can be created without notes"))
|
||||||
|
.arg(Arg::with_name("auto_checkout")
|
||||||
|
.long("auto-checkout")
|
||||||
|
.help("Checkout of current running entry when starting a new one"))
|
||||||
|
.arg(Arg::with_name("no_auto_checkout")
|
||||||
|
.long("no-auto-checkout")
|
||||||
|
.help("Starting a new entry fails if one is running (default)"))
|
||||||
.arg(Arg::with_name("note_editor")
|
.arg(Arg::with_name("note_editor")
|
||||||
.long("note-editor")
|
.long("note-editor")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("EDITOR")
|
.value_name("EDITOR")
|
||||||
.help("Command to launch notes editor or false if no editor use."))
|
.help("Command to launch notes editor. Default: $EDITOR"))
|
||||||
.arg(Arg::with_name("week_start")
|
.arg(Arg::with_name("week_start")
|
||||||
.long("week-start")
|
.long("week-start")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("DAY")
|
.value_name("DAY")
|
||||||
.help("The day of the week to use as the start of the week for t week."))
|
.possible_values(&["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"])
|
||||||
|
.help("The day of the week to use as the start of the week for t week. Default: monday"))
|
||||||
)
|
)
|
||||||
|
|
||||||
.subcommand(SubCommand::with_name("display")
|
.subcommand(SubCommand::with_name("display")
|
||||||
|
|
Loading…
Reference in New Issue