diff --git a/Cargo.toml b/Cargo.toml index 8f73be0..dc28830 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,3 @@ -[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" -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" +[workspace] +resolver = "2" +members = ["client", "server"] diff --git a/README.md b/README.md index 2141ff2..8feb2b9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,35 @@ +![img.png](assets/img.png) + # meowlog --- -## WIP NOT WORKING AND SUBJECT TO CHANGE + +## WIP SUBJECT TO CHANGE + +### ⚠️ IMPORTANT + +The client's core functionality (managing substances and ingestions) works. The codebase is a mess and will be heavily +refactired so use at your own risk. There will be no backwards compatibility until the first stable release. + +Planned features: + +- [x] Managing ingestions and substances +- [ ] Having a sensible set of default substances (taken from tripsit or psychonautwiki idk yet) +- [ ] Circular Concurrency Checking for binary files +- [ ] Server with syncing capabilities and maybe also a frontend with a similar featureset like the Psychonaut Wiki + Journal app +- [ ] Referring to harm reduction resources in the CLI + +Current problems: + +- [ ] Codebase is a mess +- [ ] No tests +- [ ] Poor error handling +- [ ] Unoptimized memory usage + --- + +### Client usage: + ``` Commands: add-ingestion Adds ingestion diff --git a/assets/img.png b/assets/img.png new file mode 100644 index 0000000..5a379d5 Binary files /dev/null and b/assets/img.png differ diff --git a/Cargo.lock b/client/Cargo.lock similarity index 93% rename from Cargo.lock rename to client/Cargo.lock index 4d28f98..1a6b205 100644 --- a/Cargo.lock +++ b/client/Cargo.lock @@ -17,6 +17,37 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher", + "opaque-debug", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher", + "opaque-debug", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -165,6 +196,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array", +] + [[package]] name = "clap" version = "4.5.20" @@ -253,6 +293,21 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crossterm" version = "0.25.0" @@ -318,6 +373,16 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -462,11 +527,13 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" name = "meowlog" version = "0.1.0" dependencies = [ + "aes", "bincode", "chrono", "clap", "clap_complete", "color-eyre", + "crc", "inquire", "lazy_static", "serde", @@ -531,6 +598,12 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "owo-colors" version = "3.5.0" @@ -833,6 +906,12 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.13" @@ -873,6 +952,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/client/Cargo.toml b/client/Cargo.toml new file mode 100644 index 0000000..fd8dbdf --- /dev/null +++ b/client/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "meowlog" +version = "0.1.0" +edition = "2021" + +[dependencies] +bincode = "1.3.3" +aes = "0.6.0" +crc = "3.2.1" +chrono = { version = "0.4.38", features = ["serde"] } +clap = { version = "4.5.20", features = ["derive"] } +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/src/config.rs b/client/src/config.rs similarity index 100% rename from src/config.rs rename to client/src/config.rs diff --git a/src/ingestions.rs b/client/src/ingestions.rs similarity index 89% rename from src/ingestions.rs rename to client/src/ingestions.rs index d5371a0..b381e3c 100644 --- a/src/ingestions.rs +++ b/client/src/ingestions.rs @@ -25,7 +25,15 @@ pub struct Ingestion { impl std::fmt::Display for Ingestion { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{} {} {} {}{}", self.date, self.time.format("%H:%M"), self.substance.name, self.dose.value, self.dose.unit) + write!( + f, + "{} {} {} {}{}", + self.date, + self.time.format("%H:%M"), + self.substance.name, + self.dose.value, + self.dose.unit + ) } } @@ -113,7 +121,6 @@ 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() { @@ -124,17 +131,36 @@ pub fn edit_ingestion() -> Result<(), std::io::Error> { 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 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(); + 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 { @@ -249,3 +275,4 @@ pub fn create_ingestions_file() -> Result<(), std::io::Error> { let hash_ser = bincode::serialize(&hash).unwrap(); std::fs::write(INGESTIONS_FILE.to_string(), hash_ser) } + diff --git a/src/ingestions_util.rs b/client/src/ingestions_util.rs similarity index 68% rename from src/ingestions_util.rs rename to client/src/ingestions_util.rs index 4e5aa4e..1d89e6d 100644 --- a/src/ingestions_util.rs +++ b/client/src/ingestions_util.rs @@ -11,7 +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()).expect("Could not read ingestions file"); + 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(); @@ -24,37 +25,32 @@ pub fn ensure_ingestion_files() -> HashMap { } 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") - .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(); + let date: chrono::NaiveDate = + inquire::CustomType::::new("Enter the date (YYYY-MM-DD):") + .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(); + 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 } @@ -63,8 +59,8 @@ pub fn get_dose_unit() -> DoseUnit { "What unit should be used?", DoseUnit::iter().collect::>(), ) - .prompt() - .unwrap(); + .prompt() + .unwrap(); dose_unit } @@ -81,7 +77,13 @@ pub fn get_substance() -> Substance { 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 }) + .filter_map(|(_, s)| { + if s.name == substance_select { + Some(s) + } else { + None + } + }) .collect(); if substances.len() != 1 { @@ -100,8 +102,8 @@ pub fn get_ingestion_method() -> IngestionMethod { "How did you ingest?", IngestionMethod::iter().collect::>(), ) - .prompt() - .unwrap(); + .prompt() + .unwrap(); ingestion_method } diff --git a/src/main.rs b/client/src/main.rs similarity index 97% rename from src/main.rs rename to client/src/main.rs index 388a12a..534ce72 100644 --- a/src/main.rs +++ b/client/src/main.rs @@ -55,7 +55,7 @@ fn main() { match &cli.command { Some(Commands::AddIngestion) => ingestions::add_ingestion(), - Some(Commands::EditIngestion) => {ingestions::edit_ingestion().unwrap()} + 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/substance_util.rs b/client/src/substance_util.rs similarity index 100% rename from src/substance_util.rs rename to client/src/substance_util.rs diff --git a/src/substances.rs b/client/src/substances.rs similarity index 100% rename from src/substances.rs rename to client/src/substances.rs diff --git a/src/util.rs b/client/src/util.rs similarity index 100% rename from src/util.rs rename to client/src/util.rs diff --git a/meowlog.proto b/meowlog.proto new file mode 100644 index 0000000..385c5b6 --- /dev/null +++ b/meowlog.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; +package meowlog; + +service MeowlogSync { + rpc GetLogs(GetLogsRequest) returns (GetLogsResponse) {} + rpc AddLog(AddLogRequest) returns (AddLogResponse) {} +} + +message GetLogsRequest { + string query = 1; +} + +message GetLogsResponse { + repeated Log logs = 1; +} + +message AddLogRequest { + Log log = 1; +} + +message AddLogResponse { + bool success = 1; +} + +message Log { + string id = 1; + string message = 2; + string level = 3; + string timestamp = 4; +} \ No newline at end of file