Initial commit

This commit is contained in:
xqtc 2024-10-10 21:36:31 +02:00
commit 1739d39409
10 changed files with 26567 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

1264
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

19
Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "meowlog"
version = "0.1.0"
edition = "2021"
[dependencies]
bincode = "1.3.3"
chrono = { version = "0.4.38", features = ["serde"] }
clap = { version = "4.5.20", features = ["derive"] }
color-eyre = "0.6.3"
git2 = "0.19.0"
inquire = "0.7.5"
lazy_static = "1.5.0"
ron = "0.8.1"
serde = { version = "1.0.210", features = ["derive"] }
strum = { version = "0.26.3", features = ["derive"] }
strum_macros = "0.26.4"
toml = "0.8.19"
uuid = { version = "1.10.0", features = ["serde", "v4"] }

BIN
ingestions.bin Normal file

Binary file not shown.

44
src/config.rs Normal file
View file

@ -0,0 +1,44 @@
use lazy_static::lazy_static;
use serde::Deserialize;
use std::fs;
use std::process::exit;
use crate::substances;
#[derive(Deserialize)]
pub struct Config {
pub save_dir: String,
}
fn load_config() -> Config {
let home = std::env::var("HOME").unwrap();
let path = format!("{}/.config/meowlog/config.toml", &home);
let contents = match fs::read_to_string(path.clone()) {
Ok(c) => c,
Err(e) => {
eprintln!("Could not read config file `{}`, with error: {:?}", path, e);
exit(1);
}
};
let config: Config = match toml::from_str(&contents) {
Ok(conf) => conf,
Err(err) => {
eprintln!("Unable to load data from file `{}`", path);
eprintln!("=> {}", err.message());
exit(1);
}
};
config
}
lazy_static! {
pub static ref CONFIG: Config = load_config();
pub static ref HOME: String = std::env::var("HOME").unwrap();
pub static ref LOCAL_PATH: String = format!("{}/.local/share/meowlog", HOME.to_string());
pub static ref SUBSTANCES_FILE: String =
format!("{}/substances.bin", LOCAL_PATH.to_string()).to_string();
pub static ref INGESTIONS_FILE: String =
format!("{}/ingestions.bin", LOCAL_PATH.to_string()).to_string();
}

141
src/ingestions.rs Normal file
View file

@ -0,0 +1,141 @@
use chrono::{NaiveDateTime, Utc};
use color_eyre::Section;
use inquire;
use serde::{self, Deserialize, Serialize};
use std::collections::HashMap;
use strum::{EnumIter, IntoEnumIterator};
use uuid::Uuid;
use crate::{config::INGESTIONS_FILE, substances::Substance};
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Ingestion {
substance: String,
dose: Dose,
ingest_method: IngestionMethod,
time: NaiveDateTime,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Dose {
unit: String,
value: f64,
}
#[derive(Serialize, Deserialize, Debug, strum::Display, strum::EnumIter)]
#[strum(serialize_all = "snake_case")]
pub enum DoseUnit {
Ug,
Mg,
G,
Ml,
}
#[derive(Serialize, Deserialize, Debug, Clone, strum::Display, strum::EnumIter)]
pub enum IngestionMethod {
Oral,
Sublingual,
Buccal,
Insuffulated,
Rectal,
Transdermal,
Subcutaneous,
Intramuscular,
Intravenous,
Smoked,
Inhaled,
}
pub fn add_ingestion() {
let mut ingesstions_bytes_loaded_des: HashMap<Uuid, Ingestion>;
if crate::substances::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 {
std::fs::File::create(INGESTIONS_FILE.to_string()).unwrap();
ingesstions_bytes_loaded_des = HashMap::new();
}
let substances = crate::substances::substances_to_vec();
let substance = inquire::Select::new("What did yout ingest?", substances)
.prompt()
.unwrap();
let ingestion_method_select = inquire::Select::new(
"How did you ingest?",
IngestionMethod::iter().collect::<Vec<_>>(),
)
.prompt()
.unwrap();
dbg!(&substance);
dbg!(&ingestion_method_select);
let current_time = Utc::now().naive_utc();
let date_time: chrono::NaiveDateTime = inquire::CustomType::<chrono::NaiveDateTime>::new(
"Enter the date and time (YYYY-MM-DD HH:MM):",
)
.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();
let dose_num: f64 = inquire::prompt_f64("Enter the amount consumed").unwrap();
let dose_unit = inquire::Select::new(
"What unit should be used?",
DoseUnit::iter().collect::<Vec<_>>(),
)
.prompt()
.unwrap();
let dose = Dose {
unit: dose_unit.to_string(),
value: dose_num,
};
let ingestion = Ingestion {
substance,
dose,
ingest_method: ingestion_method_select,
time: date_time,
};
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);
println!(
"Substance: {} ({})\nDose: {}{}\nTime: {}\n",
ingestion.substance,
ingestion.ingest_method,
ingestion.dose.value,
ingestion.dose.unit,
ingestion.time,
);
let confirm =
inquire::prompt_confirmation("Does the ingestion above look alright? [y/N]").unwrap();
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);
} else {
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<Uuid, Ingestion> = bincode::deserialize(&ing_read).unwrap();
for (id, ingestion) in ing_dec.clone().into_iter() {
println!(
"Substance: {} ({})\nDose: {}{}\nTime: {}\nUUID: {:?}\n",
ingestion.substance,
ingestion.ingest_method,
ingestion.dose.value,
ingestion.dose.unit,
ingestion.time,
id
);
}
Ok(())
}

80
src/main.rs Normal file
View file

@ -0,0 +1,80 @@
use core::panic;
use std::{collections::HashMap, path::Path};
use bincode::serialize;
use chrono::Utc;
use clap::{Parser, Subcommand};
use config::LOCAL_PATH;
use git2;
use inquire;
use serde::{self, Deserialize, Serialize};
use strum::{EnumIter, IntoEnumIterator};
use uuid::Uuid;
mod config;
mod ingestions;
mod substances;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
#[command(arg_required_else_help = true)]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
/// Adds ingestion
AddIngestion,
/// Edits an ingestion
EditIngestion,
/// List ingestions
ListIngestions,
/// Remove ingestion
RemoveIngestion,
/// Adds substance
AddSubstance,
/// Edits an substance
EditSubstance,
/// List substances
ListSubstances,
/// Remove substance
RemoveSubstance,
}
fn main() {
// let home = std::env::var("HOME").unwrap();
// let local_path = format!("{}/.local/share/meowlog", &home);
if !substances::path_exists(LOCAL_PATH.to_string()) {
match std::fs::create_dir(LOCAL_PATH.to_string()) {
Ok(_) => {}
Err(e) => {
eprintln!("Could not create data directory with error: {:?}", e);
std::process::exit(1);
}
}
}
let cli = Cli::parse();
match &cli.command {
Some(Commands::AddIngestion) => ingestions::add_ingestion(),
Some(Commands::EditIngestion) => {}
Some(Commands::ListIngestions) => ingestions::list_ingestions().unwrap(),
Some(Commands::RemoveIngestion) => {}
Some(Commands::AddSubstance) => substances::add_substance().unwrap(),
Some(Commands::EditSubstance) => {}
Some(Commands::ListSubstances) => substances::list_substances().unwrap(),
Some(Commands::RemoveSubstance) => {}
None => {}
}
}

97
src/substances.rs Normal file
View file

@ -0,0 +1,97 @@
use serde::{self, Deserialize, Serialize};
use std::collections::HashMap;
use strum::{EnumIter, IntoEnumIterator};
use uuid::Uuid;
use crate::config::SUBSTANCES_FILE;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Substance {
name: String,
class: SubstanceClass,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, strum::Display, strum::EnumIter)]
enum SubstanceClass {
Stimulant,
Depressant,
Psychedelic,
Dissociative,
Cannabinoid,
Entheogen,
Deliriant,
Empathogen,
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<Uuid, Substance>;
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 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::<Vec<_>>();
let class_select = inquire::Select::new("What type of substance is this?", class_variants)
.prompt()
.unwrap();
let substance = Substance {
name,
class: class_select,
};
let subs_hash = 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(()),
Err(e) => Err(e),
}
} else {
println!("Substance already exists!");
Ok(())
}
}
pub fn list_substances() -> Result<(), std::io::Error> {
let sub_read = std::fs::read(SUBSTANCES_FILE.to_string()).unwrap();
let sub_dec: HashMap<Uuid, Substance> = bincode::deserialize(&sub_read).unwrap();
for (id, substance) in sub_dec.clone().into_iter() {
println!(
"Name: {}\nClass: {:?}\nUUID: {:?}\n",
substance.name, substance.class, id
);
}
Ok(())
}
pub fn substances_to_vec() -> Vec<String> {
let sub_read = std::fs::read(SUBSTANCES_FILE.to_string()).unwrap();
let sub_dec: HashMap<Uuid, Substance> = bincode::deserialize(&sub_read).unwrap();
let mut sub_vec: Vec<String> = vec![];
for (id, substance) in sub_dec.clone().into_iter() {
sub_vec.push(substance.name);
}
sub_vec
}

BIN
substances.bin Normal file

Binary file not shown.

24921
substances.json Normal file

File diff suppressed because it is too large Load diff