rusty-tasks/src/main.rs

204 lines
6.9 KiB
Rust
Raw Normal View History

2024-02-22 01:34:11 -05:00
mod cli;
mod config;
2024-03-04 14:58:30 -05:00
mod file;
2024-04-02 10:58:49 -04:00
mod logging;
2023-06-23 09:41:27 -04:00
mod todo;
2023-06-15 08:57:19 -04:00
2023-06-12 23:46:40 -04:00
use chrono::naive::NaiveDate;
2024-10-10 20:05:23 -04:00
use chrono::{Datelike, Local, TimeDelta};
2024-03-04 14:58:30 -05:00
use clap::Parser;
2024-04-02 10:58:49 -04:00
use cli::Args;
2024-10-10 20:05:23 -04:00
use comrak::{format_commonmark, Arena, ComrakOptions, ExtensionOptions, ParseOptions};
2024-04-02 10:58:49 -04:00
use config::Config;
use log;
use logging::get_logging_level;
2024-02-28 17:20:55 -05:00
use resolve_path::PathResolveExt;
2024-04-02 10:58:49 -04:00
use simple_logger::init_with_level;
2024-03-04 14:58:30 -05:00
use std::fs;
2024-10-10 20:05:23 -04:00
use std::io::BufWriter;
2024-03-04 14:58:30 -05:00
use std::path::Path;
2023-06-13 08:10:18 -04:00
use std::process::Command;
2024-04-02 10:58:49 -04:00
use todo::{File as TodoFile, TaskGroup};
2023-06-12 23:46:40 -04:00
2024-10-10 20:05:23 -04:00
use crate::file::{create_new_doc, extract_sections, process_doc_tree};
2024-02-28 17:20:55 -05:00
fn main() {
2024-04-10 12:38:40 -04:00
// setup
2024-02-22 02:16:58 -05:00
let args = Args::parse();
2024-04-02 10:58:49 -04:00
let _logger = init_with_level(get_logging_level(args.verbose)).unwrap();
log::debug!("{:?}", args);
2024-02-22 02:16:58 -05:00
2024-04-10 12:38:40 -04:00
// getting config location
2024-02-28 15:12:19 -05:00
let expected_cfg_files = match Config::expected_locations() {
Ok(cfg_files) => cfg_files,
2024-02-28 17:20:55 -05:00
Err(e) => panic!("{:?}", e),
2024-02-28 15:12:19 -05:00
};
2024-04-10 12:38:40 -04:00
// getting exising config files
let cfg_files: Vec<&Path> = expected_cfg_files
.iter()
.map(|file| Path::new(file))
.filter(|file| file.exists())
.collect();
2024-04-10 12:38:40 -04:00
// writing default config if non exist
if cfg_files.len() <= 0 && args.config.is_none() {
2024-02-28 17:20:55 -05:00
if let Err(e) = Config::write_default(match expected_cfg_files[0].to_str() {
Some(s) => s,
None => panic!("Could not resolve expected cfg file paths"),
}) {
panic!("Could not write config: {:?}", e);
}
}
2023-10-06 11:46:21 -04:00
2024-04-10 12:38:40 -04:00
// set witch config file to load
2024-02-22 02:16:58 -05:00
let cfg_file = match args.config {
Some(file) => file,
None => match cfg_files.last() {
None => expected_cfg_files[0].to_string_lossy().to_string(),
Some(file) => file.to_string_lossy().to_string(),
},
2023-10-06 11:46:21 -04:00
};
2024-04-10 12:38:40 -04:00
// show current config file or just log it based on args
2024-02-22 02:16:58 -05:00
if args.current_config {
2024-04-10 12:38:40 -04:00
print!("{}", &cfg_file);
2024-02-28 17:20:55 -05:00
return;
2024-04-10 12:38:40 -04:00
} else {
log::debug!("config file: {}", &cfg_file);
2024-02-22 02:16:58 -05:00
}
2024-04-10 12:38:40 -04:00
// load config file
2024-02-28 17:20:55 -05:00
let cfg = match Config::load(&cfg_file) {
Ok(cfg) => cfg,
Err(_e) => panic!("could not load config: {}", cfg_file),
};
2024-04-02 10:58:49 -04:00
log::debug!("{:#?}", cfg);
2024-04-10 12:38:40 -04:00
// resolve data directory and create it if it does not exisit
2024-02-28 17:20:55 -05:00
let data_dir = cfg.notes_dir.resolve().to_path_buf();
2024-03-04 14:58:30 -05:00
if !fs::metadata(&data_dir).is_ok() {
match fs::create_dir_all(&data_dir) {
2024-03-18 20:20:18 -04:00
Err(_e) => panic!("Could not create default directory: {:?}", &data_dir),
2024-04-02 10:58:49 -04:00
_ => log::info!("created dir {}", &data_dir.to_string_lossy()),
};
2023-10-06 11:46:21 -04:00
}
2023-06-13 08:41:37 -04:00
2024-04-10 12:38:40 -04:00
// get file paths of notes
2024-03-18 20:17:38 -04:00
let files = fs::read_dir(&data_dir)
.expect(format!("Could not find notes folder: {:?}", &data_dir).as_str())
.filter_map(|f| f.ok())
.map(|file| file.path());
2024-04-10 12:38:40 -04:00
// list all notes
2024-03-18 22:02:24 -04:00
if args.list_all {
files
.into_iter()
.for_each(|f| println!("{}", f.canonicalize().unwrap().to_string_lossy()));
return ();
}
2024-04-10 12:38:40 -04:00
// get clossest files to specified date
2024-03-18 20:17:38 -04:00
let today = Local::now().date_naive();
2024-04-19 20:19:31 -04:00
let target = if let Some(date_str) = args.date {
cli::smart_parse_date(&date_str, &today).expect("Could not parse date")
} else {
today - TimeDelta::try_days(args.previous.into()).unwrap()
};
2024-04-01 22:00:40 -04:00
let closest_files = TodoFile::get_closest_files(files.collect(), target, args.number);
2024-04-10 12:38:40 -04:00
// list files
2024-03-18 22:02:24 -04:00
if args.list {
2024-04-10 13:21:45 -04:00
println!("Today - n\tFile");
closest_files.into_iter().for_each(|f| {
println!(
"{}\t\t{}",
(today - f.date).num_days(),
f.file.canonicalize().unwrap().to_string_lossy(),
)
});
2024-03-18 22:02:24 -04:00
return ();
}
2024-04-10 13:21:45 -04:00
// TODO: If the user did not pick a date that exist they should have the
// option to updated their choice
2023-06-13 08:41:37 -04:00
2024-03-18 22:02:24 -04:00
let latest_file = closest_files.first();
let current_file = match latest_file {
2024-04-10 12:38:40 -04:00
// copy old file if the user specifies today's notes but it does not exist
2024-03-18 22:02:24 -04:00
Some(todo_file) if todo_file.date < today && args.previous == 0 => {
2024-10-10 20:05:23 -04:00
let mut extension_options = ExtensionOptions::default();
extension_options.tasklist = true;
let mut parse_options = ParseOptions::default();
parse_options.relaxed_tasklist_matching = true;
let options = &ComrakOptions {
extension: extension_options,
parse: parse_options,
..ComrakOptions::default()
};
2024-03-18 22:02:24 -04:00
let sections = &cfg.sections;
2024-04-02 10:58:49 -04:00
log::info!("looking for sections: {:?}", sections);
2023-06-15 12:14:36 -04:00
let arena = Arena::new();
2024-03-18 22:02:24 -04:00
2024-04-10 12:38:40 -04:00
// attempt to load file
2023-06-26 09:40:56 -04:00
let root = {
2024-04-02 10:58:49 -04:00
log::info!(
"loading and parsing file: {}",
todo_file.file.to_string_lossy()
);
2024-10-10 20:05:23 -04:00
2024-03-04 14:58:30 -05:00
let contents = file::load_file(&todo_file);
2024-10-10 20:05:23 -04:00
let root = comrak::parse_document(&arena, &contents, options);
2023-06-26 09:40:56 -04:00
root
};
2024-04-02 10:58:49 -04:00
log::trace!("file loaded");
2024-10-10 20:05:23 -04:00
let sect = extract_sections(root, &sections);
let date = format!("{}-{:02}-{:02}", today.year(), today.month(), today.day());
2023-06-23 09:41:27 -04:00
2024-04-10 12:38:40 -04:00
// generate string for new file and write to filesystem
2024-10-10 20:05:23 -04:00
let new_doc = file::create_new_doc(&arena, &date, sect);
process_doc_tree(root, &date, &sections);
let mut new_content = BufWriter::new(Vec::new());
format_commonmark(new_doc, options, &mut new_content);
let text = String::from_utf8(new_content.into_inner().expect(""));
2024-03-04 14:58:30 -05:00
let file_path = file::get_filepath(&data_dir, &today);
2024-04-02 10:58:49 -04:00
log::info!("writing to file: {}", file_path.to_string_lossy());
2024-10-10 20:05:23 -04:00
file::write_file(&file_path, &text.expect(""));
2024-04-10 12:38:40 -04:00
// return file name
2023-06-26 09:40:56 -04:00
file_path
2023-06-22 16:54:00 -04:00
}
2024-04-10 12:38:40 -04:00
// returning the selected file
Some(todo_file) => todo_file.file.to_owned(),
// no note files exist creating based on template from config
2024-03-18 22:02:24 -04:00
None => {
2024-04-10 12:38:40 -04:00
// generate empty file
2024-02-28 17:20:55 -05:00
let sections = &cfg.sections;
2024-04-02 10:58:49 -04:00
log::info!("creating new empty file with sections: {:?}", sections);
2023-06-24 08:44:07 -04:00
let data = sections
.iter()
2023-06-24 08:44:07 -04:00
.map(|sec| TaskGroup::empty(sec.clone(), 2))
2024-02-28 15:12:19 -05:00
.collect();
2024-03-04 14:58:30 -05:00
let content = file::generate_file_content(&data, &today);
let file_path = file::get_filepath(&data_dir, &today);
file::write_file(&file_path, &content);
2024-04-02 10:58:49 -04:00
log::info!("writing to file: {}", file_path.to_string_lossy());
2024-04-10 12:38:40 -04:00
// return file name
2023-06-26 09:40:56 -04:00
file_path
2023-06-24 08:44:07 -04:00
}
};
2024-04-10 12:38:40 -04:00
// opening file
2024-04-02 10:58:49 -04:00
log::info!(
"Opening {} in {}",
current_file.to_string_lossy(),
cfg.editor
);
2024-03-06 14:31:53 -05:00
Command::new(&cfg.editor)
2023-06-26 09:40:56 -04:00
.args([current_file])
.status()
2024-03-06 14:31:53 -05:00
.expect(format!("failed to launch editor {}", &cfg.editor).as_str());
2023-06-13 07:59:09 -04:00
}