diff --git a/src/config.rs b/src/config.rs index ccfc367..1c21616 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,8 +3,6 @@ use serde::Deserialize; use std::fs; use std::process::exit; -use crate::substances; - #[derive(Deserialize)] pub struct Config { pub save_dir: String, diff --git a/src/ingestions.rs b/src/ingestions.rs index 811d6e2..d841de9 100644 --- a/src/ingestions.rs +++ b/src/ingestions.rs @@ -1,16 +1,14 @@ use crate::ingestions_util::{ - self, ensure_ingestion_files, get_dose_unit, get_ingestion_confirmation, get_ingestion_method, + ensure_ingestion_files, get_dose_unit, get_ingestion_confirmation, get_ingestion_method, get_substance, get_user_datetime, }; -use chrono::{NaiveDateTime, Utc}; -use color_eyre::Section; +use chrono::NaiveDateTime; use inquire; use serde::{self, Deserialize, Serialize}; -use std::{collections::HashMap, process::exit}; -use strum::{EnumIter, IntoEnumIterator}; +use std::collections::HashMap; use uuid::Uuid; -use crate::{config::INGESTIONS_FILE, substances::Substance}; +use crate::config::INGESTIONS_FILE; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Ingestion { @@ -77,7 +75,7 @@ pub fn add_ingestion() { if confirm { ingesstions_bytes_loaded_des.insert(Uuid::new_v4(), ingestion.clone()); let ingestion_ser = bincode::serialize(&ingesstions_bytes_loaded_des).unwrap(); - std::fs::write(INGESTIONS_FILE.to_string(), ingestion_ser); + std::fs::write(INGESTIONS_FILE.to_string(), ingestion_ser).unwrap(); } else { add_ingestion(); } diff --git a/src/ingestions_util.rs b/src/ingestions_util.rs index 40730f4..4e33aff 100644 --- a/src/ingestions_util.rs +++ b/src/ingestions_util.rs @@ -1,16 +1,15 @@ +use crate::config::INGESTIONS_FILE; use crate::ingestions::{DoseUnit, Ingestion, IngestionMethod}; -use crate::{config::INGESTIONS_FILE, substances::Substance}; -use chrono::{NaiveDateTime, Utc}; -use color_eyre::Section; +use crate::util::path_exists; +use chrono::Utc; use inquire; -use serde::{self, Deserialize, Serialize}; use std::{collections::HashMap, process::exit}; -use strum::{EnumIter, IntoEnumIterator}; +use strum::IntoEnumIterator; use uuid::Uuid; pub fn ensure_ingestion_files() -> HashMap { - let mut ingesstions_bytes_loaded_des: HashMap; - if crate::substances::path_exists(INGESTIONS_FILE.to_string()) { + let ingesstions_bytes_loaded_des: HashMap; + if path_exists(INGESTIONS_FILE.to_string()) { let substances_bytes_loaded = std::fs::read(INGESTIONS_FILE.to_string()).unwrap(); ingesstions_bytes_loaded_des = bincode::deserialize(&substances_bytes_loaded).unwrap(); } else { @@ -51,7 +50,7 @@ pub fn get_dose_unit() -> DoseUnit { } pub fn get_substance() -> String { - let substances = crate::substances::substances_to_vec(); + let substances = crate::substance_util::substances_to_vec(); if substances.is_empty() { eprintln!("Add a substance before you log an ingestions"); exit(1) diff --git a/src/main.rs b/src/main.rs index ce3066b..a410668 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,9 +4,11 @@ use clap::{Parser, Subcommand}; use config::{INGESTIONS_FILE, LOCAL_PATH, SUBSTANCES_FILE}; mod config; +mod util; mod ingestions; mod ingestions_util; +mod substance_util; mod substances; #[derive(Parser)] @@ -55,15 +57,15 @@ fn main() { Some(Commands::ListIngestions) => ingestions::list_ingestions().unwrap(), Some(Commands::RemoveIngestion) => {} Some(Commands::AddSubstance) => substances::add_substance().unwrap(), - Some(Commands::EditSubstance) => {} + Some(Commands::EditSubstance) => substances::edit_substance().unwrap(), Some(Commands::ListSubstances) => substances::list_substances().unwrap(), - Some(Commands::RemoveSubstance) => {} + Some(Commands::RemoveSubstance) => substances::remove_substance().unwrap(), None => {} } } fn ensure_files() { - if !substances::path_exists(LOCAL_PATH.to_string()) { + if !util::path_exists(LOCAL_PATH.to_string()) { match std::fs::create_dir(LOCAL_PATH.to_string()) { Ok(_) => {} Err(e) => { @@ -72,8 +74,8 @@ fn ensure_files() { } } } - if !substances::path_exists(SUBSTANCES_FILE.to_string()) { - match substances::create_substances_file() { + if !util::path_exists(SUBSTANCES_FILE.to_string()) { + match substance_util::create_substances_file() { Ok(_) => { println!( "Created substances file at {:?}", @@ -86,7 +88,7 @@ fn ensure_files() { } }; } - if !substances::path_exists(INGESTIONS_FILE.to_string()) { + if !util::path_exists(INGESTIONS_FILE.to_string()) { match ingestions::create_ingestions_file() { Ok(_) => { println!( diff --git a/src/substance_util.rs b/src/substance_util.rs new file mode 100644 index 0000000..f8b3a10 --- /dev/null +++ b/src/substance_util.rs @@ -0,0 +1,50 @@ +use std::collections::HashMap; +use uuid::Uuid; + +use crate::substances::SubstanceClass; +use crate::util::path_exists; +use crate::{config::SUBSTANCES_FILE, substances::Substance}; + +pub fn ensure_substance_file() -> HashMap { + let substances_bytes_loaded_des: HashMap; + if path_exists(SUBSTANCES_FILE.to_string()) { + let substances_bytes_loaded = std::fs::read(SUBSTANCES_FILE.to_string()).unwrap(); + substances_bytes_loaded_des = bincode::deserialize(&substances_bytes_loaded).unwrap(); + } else { + std::fs::File::create(SUBSTANCES_FILE.to_string()).unwrap(); + substances_bytes_loaded_des = HashMap::new(); + } + substances_bytes_loaded_des +} + +pub fn get_substance_class(msg: &str, variants: Vec) -> SubstanceClass { + let class = inquire::Select::new(msg, variants).prompt().unwrap(); + class +} + +pub fn substances_to_vec() -> Vec { + let sub_read_res = std::fs::read(SUBSTANCES_FILE.to_string()); + let sub_read = match sub_read_res { + Ok(sub_contents) => sub_contents, + Err(_) => { + println!("Error! Substance file does not exist. Creating file..."); + let hash: HashMap = HashMap::new(); + let hash_ser = bincode::serialize(&hash).unwrap(); + std::fs::write(SUBSTANCES_FILE.to_string(), hash_ser).unwrap(); + let ret: Vec = vec![]; + ret + } + }; + let sub_dec: HashMap = bincode::deserialize(&sub_read).unwrap(); + let mut sub_vec: Vec = vec![]; + for (_id, substance) in sub_dec.clone().into_iter() { + sub_vec.push(substance.name); + } + sub_vec +} + +pub fn create_substances_file() -> Result<(), std::io::Error> { + let hash: HashMap = HashMap::new(); + let hash_ser = bincode::serialize(&hash).unwrap(); + std::fs::write(SUBSTANCES_FILE.to_string(), hash_ser) +} diff --git a/src/substances.rs b/src/substances.rs index 9d39606..8627937 100644 --- a/src/substances.rs +++ b/src/substances.rs @@ -1,18 +1,19 @@ use serde::{self, Deserialize, Serialize}; use std::collections::HashMap; -use strum::{EnumIter, IntoEnumIterator}; +use strum::IntoEnumIterator; use uuid::Uuid; use crate::config::SUBSTANCES_FILE; +use crate::substance_util::{ensure_substance_file, get_substance_class, substances_to_vec}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Substance { - name: String, - class: SubstanceClass, + pub name: String, + pub substance_class: SubstanceClass, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, strum::Display, strum::EnumIter)] -enum SubstanceClass { +pub enum SubstanceClass { Stimulant, Depressant, Psychedelic, @@ -24,44 +25,18 @@ enum SubstanceClass { Neurotransmitter, } -impl SubstanceClass { - fn to_string(&self) -> String { - match self { - Self::Stimulant => "Stimulant".to_string(), - Self::Depressant => "Depressant".to_string(), - Self::Psychedelic => "Psychedelic".to_string(), - Self::Dissociative => "Dissociative".to_string(), - Self::Cannabinoid => "Cannabinoid".to_string(), - Self::Entheogen => "Entheogen".to_string(), - Self::Deliriant => "Deliriant".to_string(), - Self::Empathogen => "Empathogen".to_string(), - Self::Neurotransmitter => "Neurotransmitter".to_string(), - } - } -} -pub fn path_exists(path: String) -> bool { - std::fs::metadata(path).is_ok() -} pub fn add_substance() -> Result<(), std::io::Error> { - let mut substances_bytes_loaded_des: HashMap; - if path_exists(SUBSTANCES_FILE.to_string()) { - let substances_bytes_loaded = std::fs::read(SUBSTANCES_FILE.to_string()).unwrap(); - substances_bytes_loaded_des = bincode::deserialize(&substances_bytes_loaded).unwrap(); - } else { - std::fs::File::create(SUBSTANCES_FILE.to_string()).unwrap(); - substances_bytes_loaded_des = HashMap::new(); - } + let mut substances_bytes_loaded_des: HashMap = ensure_substance_file(); let name = inquire::prompt_text("What is the substances name?").unwrap(); if !substances_bytes_loaded_des.values().any(|x| x.name == name) { let class_variants = SubstanceClass::iter().collect::>(); - let class_select = inquire::Select::new("What type of substance is this?", class_variants) - .prompt() - .unwrap(); + let substance_class = + get_substance_class("What type of substance is this?", class_variants); let substance = Substance { name, - class: class_select, + substance_class, }; - let subs_hash = substances_bytes_loaded_des.insert(Uuid::new_v4(), substance); + substances_bytes_loaded_des.insert(Uuid::new_v4(), substance); let sub_enc = bincode::serialize(&substances_bytes_loaded_des).unwrap(); match std::fs::write(SUBSTANCES_FILE.to_string(), sub_enc) { Ok(_) => Ok(()), @@ -79,36 +54,120 @@ pub fn list_substances() -> Result<(), std::io::Error> { for (id, substance) in sub_dec.clone().into_iter() { println!( "Name: {}\nClass: {:?}\nUUID: {:?}\n", - substance.name, substance.class, id + substance.name, substance.substance_class, id ); } Ok(()) } -pub fn substances_to_vec() -> Vec { - let sub_read_res = std::fs::read(SUBSTANCES_FILE.to_string()); - let sub_read = match sub_read_res { - Ok(sub_contents) => sub_contents, - Err(_) => { - println!("Error! Substance file does not exist. Creating file..."); - let hash: HashMap = HashMap::new(); - let hash_ser = bincode::serialize(&hash).unwrap(); - std::fs::write(SUBSTANCES_FILE.to_string(), hash_ser).unwrap(); - let ret: Vec = vec![]; - ret +pub fn remove_substance() -> Result<(), std::io::Error> { + let sub_read = std::fs::read(SUBSTANCES_FILE.to_string()).unwrap(); + let mut sub_dec: HashMap = bincode::deserialize(&sub_read).unwrap(); + + let substances = substances_to_vec(); + let substances_select = + inquire::MultiSelect::new("Which substance do you want to remove?", substances) + .prompt() + .unwrap(); + dbg!(&substances_select); + for name in substances_select { + let confirm = inquire::prompt_confirmation(format!( + "Are you sure you want to remove '{}'? [y/N]", + name + )) + .unwrap(); + if confirm { + // Clone to avoid immutable borrow + let sub_dec_clone = sub_dec.clone(); + let uuid = + sub_dec_clone + .iter() + .find_map(|(id, val)| if val.name == name { Some(id) } else { None }); + if uuid.is_some() { + let _ = sub_dec + .remove(uuid.expect("Fatal error. Couldn't find substance UUID in HashMap.")); + } } - }; - let sub_dec: HashMap = bincode::deserialize(&sub_read).unwrap(); - let mut sub_vec: Vec = vec![]; - for (id, substance) in sub_dec.clone().into_iter() { - sub_vec.push(substance.name); } - sub_vec + + let sub_enc = bincode::serialize(&sub_dec).unwrap(); + match std::fs::write(SUBSTANCES_FILE.to_string(), sub_enc) { + Ok(_) => Ok(()), + Err(e) => Err(e), + } } -pub fn create_substances_file() -> Result<(), std::io::Error> { - let hash: HashMap = HashMap::new(); - let hash_ser = bincode::serialize(&hash).unwrap(); - std::fs::write(SUBSTANCES_FILE.to_string(), hash_ser) +#[derive(Serialize, Deserialize, Debug, Clone, Copy, strum::Display, strum::EnumIter)] +pub enum SubstanceEditOptions { + Name, + Class, +} + +pub fn edit_substance() -> Result<(), std::io::Error> { + let sub_read = std::fs::read(SUBSTANCES_FILE.to_string()).unwrap(); + let mut sub_dec: HashMap = bincode::deserialize(&sub_read).unwrap(); + + let substances = substances_to_vec(); + let substance_name = inquire::Select::new("Which substance do you want to edit?", substances) + .prompt() + .unwrap(); + dbg!(&substance_name); + let sub_dec_clone = sub_dec.clone(); + let uuid_opt = sub_dec_clone.iter().find_map(|(id, val)| { + if val.name == substance_name { + Some(id) + } else { + None + } + }); + if uuid_opt.is_some() { + let uuid = uuid_opt.clone().unwrap().to_owned(); + let _ = sub_dec + .remove(uuid_opt.expect("Fatal error. Couldn't find substance UUID in HashMap.")); + let edit_select = inquire::Select::new( + format!("[{}] What do you want to edit?", substance_name).as_str(), + SubstanceEditOptions::iter().collect::>(), + ) + .prompt() + .unwrap(); + match edit_select { + SubstanceEditOptions::Name => { + let name_updated = inquire::prompt_text("What should the new name be?").unwrap(); + let class = match sub_dec_clone + .get(uuid_opt.expect("Fatal error. Couldn't find substance UUID in HashMap.")) + { + Some(class) => class.substance_class, + None => { + panic!("Fatal error. Couldn't find substance UUID in HashMap.") + } + }; + dbg!(&class); + let substance = Substance { + name: name_updated, + substance_class: class, + }; + sub_dec.insert(uuid, substance); + } + SubstanceEditOptions::Class => { + let class_variants = SubstanceClass::iter().collect::>(); + let substance_class = get_substance_class( + format!( + "[{}] What should the new substance class be?", + substance_name + ) + .as_str(), + class_variants, + ); + let substance = Substance { + name: substance_name, + substance_class, + }; + sub_dec.insert(uuid, substance); + } + } + let sub_enc = bincode::serialize(&sub_dec).unwrap(); + std::fs::write(SUBSTANCES_FILE.to_string(), sub_enc).unwrap(); + } + Ok(()) } diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..6a462a2 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,3 @@ +pub fn path_exists(path: String) -> bool { + std::fs::metadata(path).is_ok() +}