From ab8ac7943458b0da086dfd13354505abcc6d4fa0 Mon Sep 17 00:00:00 2001 From: xqtc Date: Sun, 13 Oct 2024 23:22:12 +0200 Subject: [PATCH] this codebase redefines spaghetti holy fuck --- .gitignore | 1 + Cargo.lock | 35 + Cargo.toml | 4 + ingestions.bin | Bin 29870 -> 0 bytes src/config.rs | 1 + src/ingestions.rs | 171 +- src/ingestions_util.rs | 82 +- src/main.rs | 4 +- src/substances.rs | 15 +- substances.bin | Bin 198 -> 0 bytes substances.json | 24921 --------------------------------------- 11 files changed, 270 insertions(+), 24964 deletions(-) delete mode 100644 ingestions.bin delete mode 100644 substances.bin delete mode 100644 substances.json diff --git a/.gitignore b/.gitignore index ea8c4bf..3a8cabc 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +.idea diff --git a/Cargo.lock b/Cargo.lock index f56b3c2..4d28f98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,6 +187,15 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_complete" +version = "4.5.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9646e2e245bf62f45d39a0f3f36f1171ad1ea0d6967fd114bca72cb02a8fcdfb" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" version = "4.5.18" @@ -400,6 +409,12 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "js-sys" version = "0.3.70" @@ -450,10 +465,12 @@ dependencies = [ "bincode", "chrono", "clap", + "clap_complete", "color-eyre", "inquire", "lazy_static", "serde", + "serde_json", "strum", "strum_macros", "toml", @@ -588,6 +605,12 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "scopeguard" version = "1.2.0" @@ -614,6 +637,18 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.8" diff --git a/Cargo.toml b/Cargo.toml index 1c76adb..8f73be0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,11 @@ color-eyre = "0.6.3" inquire = "0.7.5" lazy_static = "1.5.0" serde = { version = "1.0.210", features = ["derive"] } +serde_json = "1.0.128" strum = { version = "0.26.3", features = ["derive"] } strum_macros = "0.26.4" toml = "0.8.19" uuid = { version = "1.10.0", features = ["serde", "v4"] } + +[build-dependencies] +clap_complete = "4.5.33" diff --git a/ingestions.bin b/ingestions.bin deleted file mode 100644 index d9de65391897299e4c8e60e8d215e4bf568f263a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29870 zcmaLbO^PN+6$Q`+H=wS-C1l3?i5H7-5rPdZh&bqpD|Q=peKE6%lY=VA;C@A3UKWS@ z-#`5H`TFDi>!-i|{Od3O`RBiX`T1Y}`1{{}{mb`{Z$I7^-U4rlx58WFZR2h5c7DA5 z>Bk?xef|H>{`KRVug6a_ug6a{ug6a}ug6b0ug6b2ug6b4ug6b6ug6b8uSYes)X@4y zeM=23HMG>wQbS7(Ej6^%&{9L8hC&U68VWTOYADoDsG(3pp@u>Yr5Z{#lxir|P^zI+ zL#c*R4W$}NHB@S-)KICRQbVPNN)447Dm7GUsMJuap;kk!hFT4^8frDvYN*vvtD#my zTMcbBwAIj7Lt71PHMG^xRzq72Z8bD%Xw=ZCp;1GlhDHsI8X7e;YG~BZQA0-!9W`{+ z&{0E24IMRf)X-5w=hM)if6(T)uiuIBQ_=JF)6w(wQ_}PG)6(w-qpqTD|_|* zd|mn-y#9V>c~@89&n)li8vL2%U0sF0vX|k{SKify_%qA9x)gtAc~=+X&n)lia{QU) zU0smBvRCBK*QL9N{F&ulU75eKm*&q`-qpqVGt0ZWJbz|+R~P8d zEbr(5u-)dl-A%e%T{ ze`a}C7wyk1@9MJsnblpbFIjYbsjKz1a98U~U9B(O)%sFb>q~dFzSPzF(p{}Dc15=@ zx_!~@i*8?Z`=Z+y-M;AdMYk`yebMcUZeMpIf86t;+t(e*pIP43oynhB-qjt-pIP1& z-M;Qt{`zFPeciSEndR@`-OHa@{tn*7{F&wN;N8q$S-O4E?d#6w&rjz6S9dsnW_ee4 zI)7$)S9d&rW_ee1`?~x2>yzpBbrGpM(_GgxNb+`6smUng6 z_GgxN#l1b~_MqE?ZV$RW==Px7gKiJHJ?QqJ+kSH{ITJd(-Vrw>RD1bbHh7O}97Q-gJA@?M=5g-QIM2 z)9p>SH{ITJd(-Vrw>RD1bbHh7O}97Q-gJA@?M=5g-QIM2)9p>SH{ITJd(-Vrw>RD1 zbbHh7O}97Q-gJA@?M=5g-QIM2)9p>SH{ITJd(-Vrw>RD1bbHh7O}97Q-gJA@?M=5g z-QIM2)9p>SH{ITJd(-Vrw>RD1bbHh7O}97Q-gJA@?M=5g-QIM2)9p>SH{ITJd(-Vr zw>RD1bbHh7O}97Q-gJA@?M=5g-QIM2)9p>SH{ITJ`=;AB-M;DeO}B5lebeomZr^nK zrrS5&zUlT&w{NGn;xZ@PWc?VE1jbo-{;H{HJJ_D#2Mx_#5_ zn{MB9`=;AB-M;DeO}B5lebeomZr^nKrrS5&zUlT&w{NGn;x zZ@PWc?VE1jbo-{;H{HJJ_D#2Mx_#5_n{MB9`=;AB-M;DeO}B5lebeomZr^nKrrS5& zzUlT&w{NGn;xZ@PWc?VE1jbo-{;H{HJJ_D#2Mx_#5_n{MB9 z`=;AB-M;DeO}7u-K6Lxg?L)T@-9B{t(CtIF58Xa=`_Sz}w-4Pubo) -> std::fmt::Result { + write!(f, "{} {} {} {}{}", self.date, self.time.format("%H:%M"), self.substance.name, self.dose.value, self.dose.unit) + } +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, PartialOrd)] pub struct Dose { pub unit: String, pub value: f64, } -#[derive(Serialize, Deserialize, Debug, strum::Display, strum::EnumIter)] +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, strum::Display, strum::EnumIter)] #[strum(serialize_all = "snake_case")] pub enum DoseUnit { Ug, @@ -33,7 +44,7 @@ pub enum DoseUnit { Ml, } -#[derive(Serialize, Deserialize, Debug, Clone, strum::Display, strum::EnumIter)] +#[derive(Serialize, Deserialize, Debug, Clone, Eq, strum::Display, strum::EnumIter, PartialEq)] pub enum IngestionMethod { Oral, Sublingual, @@ -55,7 +66,9 @@ pub fn add_ingestion() { let ingestion_method = get_ingestion_method(); - let time: NaiveDateTime = get_user_datetime(); + let current_datetime = Utc::now().naive_utc(); + let date: NaiveDate = get_user_date(current_datetime); + let time: NaiveTime = get_user_time(current_datetime); let dose_num: f64 = inquire::prompt_f64("Enter the amount consumed:").unwrap(); let dose_unit: DoseUnit = get_dose_unit(); @@ -68,6 +81,7 @@ pub fn add_ingestion() { substance, dose, ingestion_method, + date, time, }; @@ -83,11 +97,11 @@ pub fn add_ingestion() { pub fn list_ingestions() -> Result<(), std::io::Error> { let ing_read = std::fs::read(INGESTIONS_FILE.to_string()).unwrap(); - let ing_dec: HashMap = bincode::deserialize(&ing_read).unwrap(); - for (id, ingestion) in ing_dec.clone().into_iter() { + let ing_des: HashMap = bincode::deserialize(&ing_read).unwrap(); + for (id, ingestion) in ing_des.clone().into_iter() { println!( - "Substance: {} ({})\nDose: {}{}\nTime: {}\nUUID: {:?}\n", - ingestion.substance, + "Substance: {} ({})\nDose: {} {}\nTime: {}\nUUID: {:?}\n", + ingestion.substance.name, ingestion.ingestion_method, ingestion.dose.value, ingestion.dose.unit, @@ -99,6 +113,137 @@ pub fn list_ingestions() -> Result<(), std::io::Error> { Ok(()) } + +pub fn edit_ingestion() -> Result<(), std::io::Error> { + let ing_des = ensure_ingestion_files(); + if ing_des.is_empty() { + eprintln!("No ingestions to edit!"); + exit(1); + } + + let mut ingest_sel_vec_id: Vec = Vec::new(); + let mut ingest_sel_vec_ing: Vec = Vec::new(); + + + for ingestion in ing_des.clone().into_iter() { + ingest_sel_vec_id.push(ingestion.0); + ingest_sel_vec_ing.push(ingestion.1); + } + + let ingest_select = inquire::Select::new("Which ingestion do you want to edit?", ingest_sel_vec_ing).prompt().unwrap(); + let ing_id = ing_des.iter() + .map(|(key, &ref val)| if val.substance.name == ingest_select.substance.name && val.substance.substance_class == ingest_select.substance.substance_class && val.date == ingest_select.date && val.time == ingest_select.time { key.clone() } else { unreachable!() }).collect::>(); + + let edit_select = inquire::MultiSelect::new("What do you want to edit?", vec!["Substance", "Dose", "Ingestion Method", "Time", "Date"]).prompt().unwrap(); + + for edit in edit_select { + match edit { + "Substance" => { + let substance = get_substance(); + let ingestion = Ingestion { + substance, + dose: ingest_select.dose.clone(), + ingestion_method: ingest_select.ingestion_method.clone(), + time: ingest_select.time, + date: ingest_select.date, + }; + let confirm = get_ingestion_confirmation(ingestion.clone()); + if confirm { + let mut ing_des = ensure_ingestion_files(); + ing_des.insert(ing_id[0], ingestion.clone()); + let ingestion_ser = bincode::serialize(&ing_des).unwrap(); + std::fs::write(INGESTIONS_FILE.to_string(), ingestion_ser).unwrap(); + } else { + edit_ingestion(); + } + } + "Dose" => { + let dose_num: f64 = inquire::prompt_f64("Enter the amount consumed:").unwrap(); + let dose_unit: DoseUnit = get_dose_unit(); + let dose = Dose { + unit: dose_unit.to_string(), + value: dose_num, + }; + let ingestion = Ingestion { + substance: ingest_select.substance.clone(), + dose, + ingestion_method: ingest_select.ingestion_method.clone(), + time: ingest_select.time, + date: ingest_select.date, + }; + let confirm = get_ingestion_confirmation(ingestion.clone()); + if confirm { + let mut ing_des = ensure_ingestion_files(); + ing_des.insert(ing_id[0], ingestion.clone()); + let ingestion_ser = bincode::serialize(&ing_des).unwrap(); + std::fs::write(INGESTIONS_FILE.to_string(), ingestion_ser).unwrap(); + } else { + edit_ingestion(); + } + } + "Ingestion Method" => { + let ingestion_method = get_ingestion_method(); + let ingestion = Ingestion { + substance: ingest_select.substance.clone(), + dose: ingest_select.dose.clone(), + ingestion_method, + time: ingest_select.time, + date: ingest_select.date, + }; + let confirm = get_ingestion_confirmation(ingestion.clone()); + if confirm { + let mut ing_des = ensure_ingestion_files(); + ing_des.insert(ing_id[0], ingestion.clone()); + let ingestion_ser = bincode::serialize(&ing_des).unwrap(); + std::fs::write(INGESTIONS_FILE.to_string(), ingestion_ser).unwrap(); + } else { + edit_ingestion(); + } + } + "Time" => { + let time: NaiveTime = get_user_time(Utc::now().naive_utc()); + let ingestion = Ingestion { + substance: ingest_select.substance.clone(), + dose: ingest_select.dose.clone(), + ingestion_method: ingest_select.ingestion_method.clone(), + time, + date: ingest_select.date, + }; + let confirm = get_ingestion_confirmation(ingestion.clone()); + if confirm { + let mut ing_des = ensure_ingestion_files(); + ing_des.insert(ing_id[0], ingestion.clone()); + let ingestion_ser = bincode::serialize(&ing_des).unwrap(); + std::fs::write(INGESTIONS_FILE.to_string(), ingestion_ser).unwrap(); + } else { + edit_ingestion(); + } + } + "Date" => { + let date: NaiveDate = get_user_date(Utc::now().naive_utc()); + let ingestion = Ingestion { + substance: ingest_select.substance.clone(), + dose: ingest_select.dose.clone(), + ingestion_method: ingest_select.ingestion_method.clone(), + time: ingest_select.time, + date, + }; + let confirm = get_ingestion_confirmation(ingestion.clone()); + if confirm { + let mut ing_des = ensure_ingestion_files(); + ing_des.insert(ing_id[0], ingestion.clone()); + let ingestion_ser = bincode::serialize(&ing_des).unwrap(); + std::fs::write(INGESTIONS_FILE.to_string(), ingestion_ser).unwrap(); + } else { + edit_ingestion(); + } + } + _ => {} + } + } + Ok(()) +} + pub fn create_ingestions_file() -> Result<(), std::io::Error> { let hash: HashMap = HashMap::new(); let hash_ser = bincode::serialize(&hash).unwrap(); diff --git a/src/ingestions_util.rs b/src/ingestions_util.rs index 4e33aff..4e5aa4e 100644 --- a/src/ingestions_util.rs +++ b/src/ingestions_util.rs @@ -1,7 +1,8 @@ use crate::config::INGESTIONS_FILE; use crate::ingestions::{DoseUnit, Ingestion, IngestionMethod}; +use crate::substances::Substance; use crate::util::path_exists; -use chrono::Utc; +use chrono::NaiveDateTime; use inquire; use std::{collections::HashMap, process::exit}; use strum::IntoEnumIterator; @@ -10,8 +11,8 @@ use uuid::Uuid; pub fn ensure_ingestion_files() -> HashMap { 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(); + let substances_bytes_loaded = std::fs::read(INGESTIONS_FILE.to_string()).expect("Could not read ingestions file"); + ingesstions_bytes_loaded_des = bincode::deserialize(&substances_bytes_loaded).expect("Could not deserialize ingestions file. If you are tech-savvy try fixing it with a hex editor."); } else { std::fs::File::create(INGESTIONS_FILE.to_string()).unwrap(); ingesstions_bytes_loaded_des = HashMap::new(); @@ -22,21 +23,39 @@ pub fn ensure_ingestion_files() -> HashMap { ingesstions_bytes_loaded_des } -pub fn get_user_datetime() -> chrono::NaiveDateTime { - let current_time = Utc::now().naive_utc(); - let date_time: chrono::NaiveDateTime = inquire::CustomType::::new( - "Enter the date and time (YYYY-MM-DD HH:MM):", +pub fn get_user_date(current: NaiveDateTime) -> chrono::NaiveDate { + let current_time = current.time(); + let current_date = current.date(); + let date: chrono::NaiveDate = inquire::CustomType::::new( + "Enter the date (YYYY-MM-DD):", ) - .with_placeholder("YYYY-MM-DD HH:MM") - .with_default(current_time) - .with_parser(&|input| { - chrono::NaiveDateTime::parse_from_str(input, "%Y-%m-%d %H:%M").map_err(|_| ()) - }) - .with_error_message("Please enter a valid date and time in the format YYYY-MM-DD HH:MM.") - .with_help_message("Use the format YYYY-MM-DD HH:MM") - .prompt() - .unwrap(); - date_time + .with_placeholder("YYYY-MM-DD") + .with_default(current_date) + .with_parser(&|input| { + chrono::NaiveDate::parse_from_str(input, "%Y-%m-%d").map_err(|_| ()) + }) + .with_error_message("Please enter a valid date and time in the format YYYY-MM-DD") + .with_help_message("Use the format YYYY-MM-DD") + .prompt() + .unwrap(); + date +} + +pub fn get_user_time(current: NaiveDateTime) -> chrono::NaiveTime { + let current_time = current.time(); + let time: chrono::NaiveTime = inquire::CustomType::::new( + "Enter the time (HH:MM):", + ) + .with_placeholder("HH:MM") + .with_default(current_time) + .with_parser(&|input| { + chrono::NaiveTime::parse_from_str(input, "%H:%M").map_err(|_| ()) + }) + .with_error_message("Please enter a valid time in the format HH:MM.") + .with_help_message("Use the format HH:MM") + .prompt() + .unwrap(); + time } pub fn get_dose_unit() -> DoseUnit { @@ -44,20 +63,35 @@ pub fn get_dose_unit() -> DoseUnit { "What unit should be used?", DoseUnit::iter().collect::>(), ) - .prompt() - .unwrap(); + .prompt() + .unwrap(); dose_unit } -pub fn get_substance() -> String { +pub fn get_substance() -> Substance { let substances = crate::substance_util::substances_to_vec(); if substances.is_empty() { eprintln!("Add a substance before you log an ingestions"); exit(1) } - let substance = inquire::Select::new("What did yout ingest?", substances) + let substance_select = inquire::Select::new("What did yout ingest?", substances) .prompt() .unwrap(); + + let substance_file: HashMap = crate::substance_util::ensure_substance_file(); + let substances: Vec = substance_file + .into_iter() + .filter_map(|(_, s)| if s.name == substance_select { Some(s) } else { None }) + .collect(); + + if substances.len() != 1 { + eprintln!("Substance not found or multiple substances with the same name."); + exit(1); + } + + let substance = substances.into_iter().next().unwrap(); + dbg!(&substance); + substance } @@ -66,15 +100,15 @@ pub fn get_ingestion_method() -> IngestionMethod { "How did you ingest?", IngestionMethod::iter().collect::>(), ) - .prompt() - .unwrap(); + .prompt() + .unwrap(); ingestion_method } pub fn get_ingestion_confirmation(ingestion: Ingestion) -> bool { println!( "Substance: {} ({})\nDose: {}{}\nTime: {}\n", - ingestion.substance, + ingestion.substance.name, ingestion.ingestion_method, ingestion.dose.value, ingestion.dose.unit, diff --git a/src/main.rs b/src/main.rs index a410668..388a12a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,8 @@ mod ingestions_util; mod substance_util; mod substances; +// mod drug_parser; + #[derive(Parser)] #[command(author, version, about, long_about = None)] #[command(propagate_version = true)] @@ -53,7 +55,7 @@ fn main() { match &cli.command { Some(Commands::AddIngestion) => ingestions::add_ingestion(), - Some(Commands::EditIngestion) => {} + Some(Commands::EditIngestion) => {ingestions::edit_ingestion().unwrap()} Some(Commands::ListIngestions) => ingestions::list_ingestions().unwrap(), Some(Commands::RemoveIngestion) => {} Some(Commands::AddSubstance) => substances::add_substance().unwrap(), diff --git a/src/substances.rs b/src/substances.rs index 8627937..04bbd94 100644 --- a/src/substances.rs +++ b/src/substances.rs @@ -6,7 +6,7 @@ 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)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Substance { pub name: String, pub substance_class: SubstanceClass, @@ -25,6 +25,11 @@ pub enum SubstanceClass { Neurotransmitter, } +impl PartialEq for SubstanceClass { + fn eq(&self, other: &Self) -> bool { + std::mem::discriminant(self) == std::mem::discriminant(other) + } +} pub fn add_substance() -> Result<(), std::io::Error> { let mut substances_bytes_loaded_des: HashMap = ensure_substance_file(); let name = inquire::prompt_text("What is the substances name?").unwrap(); @@ -76,7 +81,7 @@ pub fn remove_substance() -> Result<(), std::io::Error> { "Are you sure you want to remove '{}'? [y/N]", name )) - .unwrap(); + .unwrap(); if confirm { // Clone to avoid immutable borrow let sub_dec_clone = sub_dec.clone(); @@ -129,8 +134,8 @@ pub fn edit_substance() -> Result<(), std::io::Error> { format!("[{}] What do you want to edit?", substance_name).as_str(), SubstanceEditOptions::iter().collect::>(), ) - .prompt() - .unwrap(); + .prompt() + .unwrap(); match edit_select { SubstanceEditOptions::Name => { let name_updated = inquire::prompt_text("What should the new name be?").unwrap(); @@ -156,7 +161,7 @@ pub fn edit_substance() -> Result<(), std::io::Error> { "[{}] What should the new substance class be?", substance_name ) - .as_str(), + .as_str(), class_variants, ); let substance = Substance { diff --git a/substances.bin b/substances.bin deleted file mode 100644 index 1300415770acfe477ba03c1c01159d52a1678c97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 198 zcmZQ!fB*p~J^#zCIu<*ZrY=`M`3=H6d-