path handling and unwraps

This commit is contained in:
Andrei Stoica 2024-02-28 17:20:55 -05:00
parent 1c51c53eef
commit e0c81885c8
3 changed files with 33 additions and 79 deletions

View File

@ -13,3 +13,4 @@ figment = { version = "0.10.10", features = ["env", "serde_json", "json"] }
regex = "1.8.4" regex = "1.8.4"
serde = { version = "1.0.164", features = ["serde_derive"] } serde = { version = "1.0.164", features = ["serde_derive"] }
serde_json = "1.0.97" serde_json = "1.0.97"
resolve-path = "0.1.0"

View File

@ -11,17 +11,17 @@ use std::path::PathBuf;
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
pub struct Config { pub struct Config {
pub editor: Option<String>, pub editor: String,
pub sections: Option<Vec<String>>, pub sections: Vec<String>,
pub notes_dir: Option<String>, pub notes_dir: String,
} }
impl Default for Config { impl Default for Config {
fn default() -> Self { fn default() -> Self {
Config { Config {
editor: Some("nano".into()), editor: "nano".into(),
sections: Some(vec!["Daily".into(), "Weekly".into(), "Monthly".into()]), sections: vec!["Daily".into(), "Weekly".into(), "Monthly".into()],
notes_dir: Some("Notes".into()), notes_dir: "./Notes".into(),
} }
} }
} }
@ -44,17 +44,17 @@ impl Config {
pub fn write_default(cfg_file: &str) -> Result<(), ConfigError> { pub fn write_default(cfg_file: &str) -> Result<(), ConfigError> {
let buf = serde_json::to_string_pretty(&Self::default()).or_else(|_| { let buf = serde_json::to_string_pretty(&Self::default()).or_else(|_| {
return Err(ConfigError::ParseError( Err(ConfigError::ParseError(
"could not serialize default config", "could not serialize default config",
)); ))
})?; })?;
let mut f = File::create(cfg_file) let mut f = File::create(cfg_file)
.or_else(|_| Err(ConfigError::IOError("Could not open config file")))?; .or_else(|_| Err(ConfigError::IOError("Could not open config file")))?;
f.write_all(&buf.as_bytes()).or_else(|_| { f.write_all(&buf.as_bytes()).or_else(|_| {
return Err(ConfigError::IOError( Err(ConfigError::IOError(
"could not write default config to file", "could not write default config to file",
)); ))
})?; })?;
Ok(()) Ok(())

View File

@ -5,7 +5,7 @@ mod todo;
use crate::cli::Args; use crate::cli::Args;
use clap::Parser; use clap::Parser;
use crate::config::{Config, ConfigError}; use crate::config::Config;
use crate::todo::File as TodoFile; use crate::todo::File as TodoFile;
use crate::todo::{Status as TaskStatus, TaskGroup}; use crate::todo::{Status as TaskStatus, TaskGroup};
use chrono::naive::NaiveDate; use chrono::naive::NaiveDate;
@ -13,32 +13,22 @@ use chrono::{Datelike, Local};
use comrak::nodes::{AstNode, NodeValue}; use comrak::nodes::{AstNode, NodeValue};
use comrak::{parse_document, Arena}; use comrak::{parse_document, Arena};
use comrak::{ComrakExtensionOptions, ComrakOptions, ComrakParseOptions}; use comrak::{ComrakExtensionOptions, ComrakOptions, ComrakParseOptions};
use resolve_path::PathResolveExt;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::{create_dir_all, metadata, read, read_dir, File}; use std::fs::{create_dir_all, metadata, read, read_dir, File};
use std::io::{self, Write}; use std::io::Write;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use std::{env, str}; use std::str;
//TODO handle unwraps and errors more uniformly
//TODO refactor creating new file //TODO refactor creating new file
//TODO clean up verbose printing
//TODO create custom errors for better error handling
//TODO Default path for note_dir should start with curent path not home
// Nested errors look bad, code smell? fn main() {
#[derive(Debug)]
enum ExitError {
ConfigError(ConfigError),
IOError(String, io::Error),
}
fn main() -> Result<(), ExitError> {
let args = Args::parse(); let args = Args::parse();
let expected_cfg_files = match Config::expected_locations() { let expected_cfg_files = match Config::expected_locations() {
Err(e) => return Err(ExitError::ConfigError(e)),
Ok(cfg_files) => cfg_files, Ok(cfg_files) => cfg_files,
Err(e) => panic!("{:?}", e),
}; };
let cfg_files: Vec<&Path> = expected_cfg_files let cfg_files: Vec<&Path> = expected_cfg_files
@ -48,9 +38,11 @@ fn main() -> Result<(), ExitError> {
.collect(); .collect();
if cfg_files.len() <= 0 { if cfg_files.len() <= 0 {
match Config::write_default(expected_cfg_files[0].to_str()?) { if let Err(e) = Config::write_default(match expected_cfg_files[0].to_str() {
Err(e) => return Err(ExitError::ConfigError(e)), Some(s) => s,
_ => (), None => panic!("Could not resolve expected cfg file paths"),
}) {
panic!("Could not write config: {:?}", e);
} }
} }
@ -64,45 +56,29 @@ fn main() -> Result<(), ExitError> {
if args.current_config { if args.current_config {
println!("{}", &cfg_file); println!("{}", &cfg_file);
return Ok(()); return;
} }
let cfg = Config::load(&cfg_file).unwrap(); let cfg = match Config::load(&cfg_file) {
Ok(cfg) => cfg,
println!("{:#?}", cfg); Err(_e) => panic!("could not load config: {}", cfg_file),
let data_dir = match &cfg.notes_dir {
Some(dir) => get_data_dir(dir),
_ => {
return Err(ExitError::ConfigError(ConfigError::IOError(
"Could not get notes dir from config",
)))
}
}; };
let data_dir = cfg.notes_dir.resolve().to_path_buf();
if !metadata(&data_dir).is_ok() { if !metadata(&data_dir).is_ok() {
match create_dir_all(&data_dir) { match create_dir_all(&data_dir) {
Err(e) => { Err(_e) => panic!("Could not create defult directory: {:?}", &data_dir),
return Err(ExitError::IOError(
format!(
"Could not create defult directory: {}",
&data_dir.to_str().unwrap(),
),
e,
))
}
_ => (), _ => (),
}; };
} }
println!("dir = {}", data_dir.to_str().unwrap());
let latest_file = get_latest_file(&data_dir); let latest_file = get_latest_file(&data_dir);
println!("Latest file: {:?}", latest_file);
let now = Local::now(); let now = Local::now();
let today = NaiveDate::from_ymd_opt(now.year(), now.month(), now.day()).unwrap(); let today = NaiveDate::from_ymd_opt(now.year(), now.month(), now.day()).unwrap();
let current_file = match latest_file { let current_file = match latest_file {
Ok(todo_file) if todo_file.date < today => { Ok(todo_file) if todo_file.date < today => {
println!("Today's file does not exist, creating");
let arena = Arena::new(); let arena = Arena::new();
let root = { let root = {
let contents = load_file(&todo_file); let contents = load_file(&todo_file);
@ -110,12 +86,9 @@ fn main() -> Result<(), ExitError> {
root root
}; };
println!("{:#?}", root); let sections = &cfg.sections;
println!("=======================================================");
let sections = &cfg.sections.unwrap();
let groups = extract_secitons(root, sections); let groups = extract_secitons(root, sections);
println!("{:#?}", groups);
let level = groups.values().map(|group| group.level).min().unwrap_or(2); let level = groups.values().map(|group| group.level).min().unwrap_or(2);
@ -133,8 +106,7 @@ fn main() -> Result<(), ExitError> {
file_path file_path
} }
Err(_) => { Err(_) => {
println!("No files in dir: {:}", cfg.notes_dir.unwrap()); let sections = &cfg.sections;
let sections = &cfg.sections.unwrap();
let data = sections let data = sections
.iter() .iter()
.map(|sec| TaskGroup::empty(sec.clone(), 2)) .map(|sec| TaskGroup::empty(sec.clone(), 2))
@ -144,18 +116,13 @@ fn main() -> Result<(), ExitError> {
write_file(&file_path, &content); write_file(&file_path, &content);
file_path file_path
} }
Ok(todo_file) => { Ok(todo_file) => todo_file.file.path(),
println!("Today's file was created");
todo_file.file.path()
}
}; };
Command::new(cfg.editor.expect("Could not resolve editor from config")) Command::new(cfg.editor)
.args([current_file]) .args([current_file])
.status() .status()
.expect(format!("failed to launch editor {}", "vim").as_str()); .expect(format!("failed to launch editor {}", "vim").as_str());
Ok(())
} }
fn get_filepath(data_dir: &PathBuf, date: &NaiveDate) -> PathBuf { fn get_filepath(data_dir: &PathBuf, date: &NaiveDate) -> PathBuf {
@ -239,7 +206,6 @@ fn extract_secitons<'a>(
continue; continue;
}; };
println!("Attempting to parse {}", title);
if sections.iter().any(|section| section.eq(title)) { if sections.iter().any(|section| section.eq(title)) {
if let Ok(mut group) = TaskGroup::try_from(node) { if let Ok(mut group) = TaskGroup::try_from(node) {
group.tasks = group group.tasks = group
@ -255,19 +221,6 @@ fn extract_secitons<'a>(
groups groups
} }
fn get_data_dir(dir_name: &str) -> PathBuf {
let mut dir = match env::var("HOME") {
Ok(home) => {
let mut x = PathBuf::new();
x.push(home);
x
}
_ => env::current_dir().expect("PWD environment variable not set"),
};
dir = dir.join(dir_name);
dir
}
fn get_latest_file(dir: &Path) -> Result<TodoFile, String> { fn get_latest_file(dir: &Path) -> Result<TodoFile, String> {
let dir = read_dir(dir).expect(format!("Could not find notes folder: {:?}", dir).as_str()); let dir = read_dir(dir).expect(format!("Could not find notes folder: {:?}", dir).as_str());
dir.filter_map(|f| f.ok()) dir.filter_map(|f| f.ok())