mirror of
https://github.com/xqtc161/meowlog.git
synced 2024-11-24 02:33:54 +01:00
Initial commit
This commit is contained in:
commit
1739d39409
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
1264
Cargo.lock
generated
Normal file
1264
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
19
Cargo.toml
Normal file
19
Cargo.toml
Normal 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
BIN
ingestions.bin
Normal file
Binary file not shown.
44
src/config.rs
Normal file
44
src/config.rs
Normal 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
141
src/ingestions.rs
Normal 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
80
src/main.rs
Normal 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
97
src/substances.rs
Normal 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
BIN
substances.bin
Normal file
Binary file not shown.
24921
substances.json
Normal file
24921
substances.json
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue