mod cli;
mod config;
mod constants;
mod date_parse;
mod elements;
mod error;
mod parser;
mod ref_resolver;
mod store;
mod commands;
use anyhow::{Context, Result};
use chrono::NaiveDate;
use clap::Parser as ClapParser;
use cli::{Cli, Commands};
use config::{Config, default_config_path};
fn main() {
if let Err(e) = run() {
eprintln!("error: {:#}", e);
std::process::exit(1);
}
}
fn run() -> Result<()> {
let cli = Cli::parse();
let config_path = cli.config_path
.as_deref()
.map(std::path::PathBuf::from)
.unwrap_or_else(default_config_path);
if !config_path.exists() || cli.force {
Config::init(&config_path)
.with_context(|| format!("failed to init config at {}", config_path.display()))?;
}
let config = Config::load(&config_path)
.with_context(|| format!("failed to load config from {}", config_path.display()))?;
config.ensure_dirs()
.context("failed to create MPS directories")?;
let default_cmd = config.default_command.to_lowercase();
let command = cli.command.unwrap_or_else(|| {
match default_cmd.as_str() {
"list" => Commands::List {
datesign: None,
r#type: None,
tag: None,
status: None,
since: None,
refs: false,
all: false,
},
_ => Commands::Open { datesign: None },
}
});
match command {
Commands::Version => {
println!("mps (v{})", env!("CARGO_PKG_VERSION"));
}
Commands::Open { datesign } => {
let date = resolve_date(datesign.as_deref().unwrap_or("today"))?;
commands::open::run(&config, date)?;
}
Commands::List { datesign, r#type, tag, status, since, refs, all } => {
let date = resolve_date(datesign.as_deref().unwrap_or("today"))?;
let dates = resolve_date_range(since.as_deref(), date)?;
commands::list::run(&config, date, dates, r#type, tag, status, refs, all)?;
}
Commands::Append { kind, body, tags, status, at, start_time, end_time } => {
let body_str = body.join(" ");
let kind_resolved = resolve_alias(&kind, &config);
let tags_vec: Vec<String> = tags
.as_deref()
.unwrap_or("")
.split(',')
.map(|t| t.trim().to_string())
.filter(|t| !t.is_empty())
.collect();
let date = chrono::Local::now().date_naive();
commands::append::run(
&config, &kind_resolved, &body_str, tags_vec,
status, at, start_time, end_time, date,
)?;
}
Commands::Update { ref_path, status, start_time, end_time, at, date } => {
commands::update::run(&config, &ref_path, status, start_time, end_time, at, date)?;
}
Commands::Done { ref_path, date } => {
commands::update::run_done(&config, &ref_path, date)?;
}
Commands::Search { query, r#type, tag, since } => {
let since_date = since.as_deref().map(date_parse::parse_date).transpose()?;
commands::search::run(&config, &query, r#type, tag, since_date)?;
}
Commands::Stats { datesign, since, all } => {
let date = resolve_date(datesign.as_deref().unwrap_or("today"))?;
let dates = resolve_date_range(since.as_deref(), date)?;
commands::stats::run(&config, dates, all)?;
}
Commands::Tags { datesign, r#type, status, since, all } => {
let date = resolve_date(datesign.as_deref().unwrap_or("today"))?;
let dates = resolve_date_range(since.as_deref(), date)?;
commands::tags::run(&config, dates, all, r#type, status)?;
}
Commands::Export { datesign, format, r#type, since } => {
let date = resolve_date(datesign.as_deref().unwrap_or("today"))?;
let dates = resolve_date_range(since.as_deref(), date)?;
commands::export::run(&config, dates, &format, r#type)?;
}
Commands::Config { subcommand } => {
commands::config_cmd::run(&config, &config_path, subcommand.as_deref())?;
}
Commands::Git { args } => {
commands::git::run_git(&config, &args)?;
}
Commands::Autogit => {
commands::git::run_autogit(&config)?;
}
Commands::Cmd { args } => {
commands::git::run_cmd(&config, &args)?;
}
}
Ok(())
}
fn resolve_alias(kind: &str, config: &Config) -> String {
let normalized = kind.to_lowercase();
config.aliases
.get(&normalized)
.cloned()
.unwrap_or(normalized)
}
fn resolve_date(datesign: &str) -> Result<NaiveDate> {
date_parse::parse_date(datesign)
.with_context(|| format!("cannot parse date '{}'", datesign))
}
fn resolve_date_range(since: Option<&str>, to_date: NaiveDate) -> Result<Vec<NaiveDate>> {
match since {
None => Ok(vec![to_date]),
Some(s) => {
let since_date = resolve_date(s)?;
let mut dates = Vec::new();
let mut d = since_date;
while d <= to_date {
dates.push(d);
d += chrono::Duration::days(1);
}
Ok(dates)
}
}
}