diff --git a/journal/src/journal.rs b/journal/src/journal.rs index f93575c..d91349e 100644 --- a/journal/src/journal.rs +++ b/journal/src/journal.rs @@ -5,24 +5,29 @@ use crate::types::{ pub type JournalType = Box; pub trait Journal { - fn get_custom_unit(&self, id: i32) -> Option; + fn save(&self) -> Result<(), Box>; + fn get_session(&self, id: i32) -> Option; fn get_session_ingestions(&self, id: i32) -> Option>; - fn get_substance(&self, name: &str) -> Option; - fn get_sustenance(&self, sustenance_id: &str) -> Option; - fn get_sustenance_ingestions(&self, sustenance_id: &str) -> Option>; + fn get_sustenance(&self, id: &str) -> Option; + fn get_sustenance_ingestions(&self, id: &str) -> Option>; + fn get_custom_unit(&self, id: i32) -> Option; + + fn create_session(&mut self, title: &str) -> Session; + fn create_substance(&mut self, substance: Substance) -> Substance; + fn create_sustenance(&mut self, sustenance: Sustenance) -> Sustenance; + fn create_custom_unit(&mut self, unit: CustomUnit) -> CustomUnit; + + fn update_session(&mut self, id: i32, session: Session, create: bool); + fn update_substance(&mut self, name: &str, substance: Substance, create: bool); + fn update_sustenance(&mut self, id: &str, sustenance: Sustenance, create: bool); + fn update_custom_unit(&mut self, id: i32, unit: CustomUnit, create: bool); - fn set_custom_unit(&mut self, unit: CustomUnit); - fn set_session(&mut self, session: Session); fn set_session_ingestions(&mut self, session_id: i32, ingestions: Vec); - fn set_substance(&mut self, substance: Substance); - fn set_sustenance(&mut self, sustenance: Sustenance); fn first_session_by_title(&self, title: &str) -> Option; fn resolve_substance_unit(&self, ingestion: &SubstanceIngestion) -> Option; - - fn save(&self) -> Result<(), Box>; } mod json_journal; diff --git a/journal/src/journal/json_journal.rs b/journal/src/journal/json_journal.rs index 0479565..d0e796e 100644 --- a/journal/src/journal/json_journal.rs +++ b/journal/src/journal/json_journal.rs @@ -1,5 +1,8 @@ use std::{collections::HashMap, fs::File}; +use chrono::Utc; +use rand::Rng; + use crate::types::{ CustomUnit, Ingestion, IngestionKind, Session, Substance, SubstanceIngestion, Sustenance, Unit, }; @@ -59,8 +62,8 @@ impl JSONJournal { } impl Journal for JSONJournal { - fn get_custom_unit(&self, id: i32) -> Option { - self.data.custom_units.get(&id).cloned() + fn save(&self) -> Result<(), Box> { + self.save() } fn get_session(&self, id: i32) -> Option { @@ -75,58 +78,12 @@ impl Journal for JSONJournal { self.data.substances.get(name).cloned() } - fn resolve_substance_unit(&self, ingestion: &SubstanceIngestion) -> Option { - match ingestion.custom_unit_id { - None => match self.get_substance(&ingestion.substance_name) { - Some(substance) => Some(Unit::Simple(substance.unit)), - None => None, - }, - Some(custom_unit_id) => self.get_custom_unit(custom_unit_id).map(Unit::Custom), - } - } - - fn first_session_by_title(&self, title: &str) -> Option { - self.data - .sessions - .values() - .find(|&session| session.title == title) - .cloned() - } - - fn set_custom_unit(&mut self, unit: CustomUnit) { - self.data.custom_units.insert(unit.id, unit); - } - - fn set_session(&mut self, session: Session) { - self.data.sessions.insert(session.id, session); - } - - fn set_session_ingestions(&mut self, session_id: i32, ingestions: Vec) { - self.data.ingestions.insert(session_id, ingestions); - } - - fn set_substance(&mut self, substance: Substance) { - self.data - .substances - .insert(substance.name.clone(), substance); - } - - fn save(&self) -> Result<(), Box> { - self.save() - } - fn get_sustenance(&self, sustenance_id: &str) -> Option { self.data.sustenances.get(sustenance_id).cloned() } - fn set_sustenance(&mut self, sustenance: Sustenance) { - self.data - .sustenances - .insert(sustenance.id.clone(), sustenance); - } - fn get_sustenance_ingestions(&self, sustenance_id: &str) -> Option> { - let sustenance = self.get_sustenance(sustenance_id).unwrap(); + let sustenance = self.get_sustenance(sustenance_id)?; let mut ingestions: Vec = Vec::new(); for include in sustenance.includes.into_iter() { @@ -152,4 +109,107 @@ impl Journal for JSONJournal { Some(ingestions) } + + fn get_custom_unit(&self, id: i32) -> Option { + self.data.custom_units.get(&id).cloned() + } + + fn create_session(&mut self, title: &str) -> Session { + let id = rand::thread_rng().gen::(); + + let session = Session { + id, + title: title.to_string(), + creation_time: Utc::now(), + ..Session::default() + }; + + self.data.sessions.insert(id, session.clone()); + + session + } + + fn create_substance(&mut self, substance: Substance) -> Substance { + self.data + .substances + .insert(substance.name.clone(), substance.clone()); + + substance + } + + fn create_sustenance(&mut self, sustenance: Sustenance) -> Sustenance { + self.data + .sustenances + .insert(sustenance.id.clone(), sustenance.clone()); + + sustenance + } + + fn create_custom_unit(&mut self, unit: CustomUnit) -> CustomUnit { + let id = rand::thread_rng().gen::(); + + let unit = CustomUnit { id, ..unit }; + + self.data.custom_units.insert(id, unit.clone()); + + unit + } + + fn update_session(&mut self, id: i32, session: Session, create: bool) { + assert!(self.data.sessions.contains_key(&id) || create); + + self.data.sessions.insert(id, Session { id, ..session }); + } + + fn update_substance(&mut self, name: &str, substance: Substance, create: bool) { + assert!(self.data.substances.contains_key(name) || create); + + self.data.substances.insert( + name.to_string(), + Substance { + name: name.to_string(), + ..substance + }, + ); + } + + fn update_sustenance(&mut self, id: &str, sustenance: Sustenance, create: bool) { + assert!(self.data.sustenances.contains_key(id) || create); + + self.data.sustenances.insert( + id.to_string(), + Sustenance { + id: id.to_string(), + ..sustenance + }, + ); + } + + fn update_custom_unit(&mut self, id: i32, unit: CustomUnit, create: bool) { + assert!(self.data.custom_units.contains_key(&id) || create); + + self.data.custom_units.insert(id, CustomUnit { id, ..unit }); + } + + fn set_session_ingestions(&mut self, session_id: i32, ingestions: Vec) { + self.data.ingestions.insert(session_id, ingestions); + } + + fn first_session_by_title(&self, title: &str) -> Option { + self.data + .sessions + .values() + .find(|&session| session.title == title) + .cloned() + } + + fn resolve_substance_unit(&self, ingestion: &SubstanceIngestion) -> Option { + match ingestion.custom_unit_id { + None => match self.get_substance(&ingestion.substance_name) { + Some(substance) => Some(Unit::Simple(substance.unit)), + None => None, + }, + Some(custom_unit_id) => self.get_custom_unit(custom_unit_id).map(Unit::Custom), + } + } } diff --git a/journal_cli/src/commands/journal/import_psychonaut.rs b/journal_cli/src/commands/journal/import_psychonaut.rs index 44e8687..ca7fa5e 100644 --- a/journal_cli/src/commands/journal/import_psychonaut.rs +++ b/journal_cli/src/commands/journal/import_psychonaut.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, fs::File, path::Path}; use journal::{ - journal::{JSONJournal, Journal}, + journal::{JSONJournal, Journal, JournalType}, types::{CustomUnit, Ingestion, Session, Substance}, }; use rand::Rng; @@ -22,8 +22,13 @@ pub struct JournalImportPsychonautArgs { #[arg(long)] pub force_create: bool, - #[arg(long, value_hint = clap::ValueHint::FilePath)] - pub mappings_file: Option, + #[arg(long)] + pub custom_unit_id: Option, + #[arg(long)] + pub custom_unit_name: Option, + + #[arg(long)] + pub custom_substance: Option, #[arg(long)] pub infer_missing_substances: bool, @@ -52,49 +57,126 @@ pub fn journal_import_psychonaut( let export_file = File::open(args.export_file.clone())?; let export_data: psychonaut_journal_types::ExportData = serde_json::from_reader(export_file)?; - let mappings: Mappings = match args.mappings_file.clone() { - Some(filename) => { - let mappings_file = File::open(filename)?; - let mappings_data: Mappings = serde_json::from_reader(mappings_file)?; - mappings_data - } - None => Mappings::default(), - }; - let mut rng = rand::thread_rng(); - for custom_unit in export_data.custom_units.into_iter() { - journal.set_custom_unit(CustomUnit::from(custom_unit)); + fn create_or_update_custom_unit(journal: &mut JournalType, unit: CustomUnit) { + match journal.get_custom_unit(unit.id) { + Some(custom_unit) => { + println!( + "Updating Custom Unit ID: {} Name: {}", + unit.id, custom_unit.name + ); + journal.update_custom_unit(unit.id, unit, false); + } + None => { + println!("Creating Custom Unit ID: {} Name: {}", unit.id, unit.name); + journal.update_custom_unit(unit.id, unit, true); + } + } } - for custom_substance in export_data.custom_substances.into_iter() { + if let Some(id) = &args.custom_unit_id { + let unit = export_data + .custom_units + .into_iter() + .find(|unit| unit.id == *id); + + match unit { + Some(unit) => { + create_or_update_custom_unit(&mut journal, CustomUnit::from(unit)); + } + None => { + panic!("Could not find custom unit with ID: {id}") + } + } + + return Ok(()); + } else if let Some(name) = &args.custom_unit_name { + let unit = export_data + .custom_units + .into_iter() + .find(|unit| unit.name == *name); + + match unit { + Some(unit) => { + create_or_update_custom_unit(&mut journal, CustomUnit::from(unit)); + } + None => { + panic!("Could not find custom unit with name: {name}") + } + } + + return Ok(()); + } else if args.custom_substance.is_none() { + for custom_unit in export_data.custom_units.into_iter() { + create_or_update_custom_unit(&mut journal, CustomUnit::from(custom_unit)); + } + } + + fn create_or_check_custom_substance( + journal: &mut JournalType, + args: &JournalImportPsychonautArgs, + custom_substance: Substance, + ) { match journal.get_substance(&custom_substance.name) { Some(substance) => { - if substance.unit != custom_substance.units { + println!("Checking substance units {}", custom_substance.name); + + if substance.unit != custom_substance.unit { panic!( - "mismatch on units for {}: {} != {} ", - custom_substance.name, substance.unit, custom_substance.units + "mismatch on unit for {}: {} != {} ", + custom_substance.name, substance.unit, custom_substance.unit ) } } None => { if args.infer_missing_substances { - journal.set_substance(Substance { - name: custom_substance.name.clone(), - description: custom_substance.description.clone(), - unit: custom_substance.units, - }); + println!("Creating substance {}", custom_substance.name); + journal.create_substance(custom_substance); } } } } + if let Some(substance_name) = &args.custom_substance { + let custom_substance: Option = export_data + .custom_substances + .into_iter() + .find(|substance| substance.name == *substance_name); + + match custom_substance { + Some(custom_substance) => { + create_or_check_custom_substance( + &mut journal, + args, + Substance::from(Substance { + name: custom_substance.name.clone(), + description: custom_substance.description.clone(), + unit: custom_substance.units, + }), + ); + } + None => { + panic!("Could not find substance with name: {substance_name}") + } + } + } else { + for custom_substance in export_data.custom_substances.into_iter() { + create_or_check_custom_substance( + &mut journal, + args, + Substance { + name: custom_substance.name.clone(), + description: custom_substance.description.clone(), + unit: custom_substance.units, + }, + ); + } + } + for experience in export_data.experiences.iter() { for ingestion in experience.ingestions.iter() { - let substance_name = mappings - .substance_names - .get(&ingestion.substance_name) - .unwrap_or(&ingestion.substance_name); + let substance_name = &ingestion.substance_name; let ingestion_unit = match ingestion.custom_unit_id { Some(id) => journal.get_custom_unit(id).unwrap().original_unit, @@ -111,7 +193,7 @@ pub fn journal_import_psychonaut( } } None => { - journal.set_substance(Substance { + journal.create_substance(Substance { name: substance_name.clone(), description: "".to_string(), unit: ingestion_unit, @@ -125,14 +207,20 @@ pub fn journal_import_psychonaut( if journal.first_session_by_title(&experience.title).is_some() { println!("Skipping {} as is already in journal", experience.title); continue; + } else { + println!("Creating experience {}", experience.title); } let session_id = rng.gen::(); - journal.set_session(Session { - id: session_id, - ..Session::from(experience.clone()) - }); + journal.update_session( + session_id, + Session { + id: session_id, + ..Session::from(experience.clone()) + }, + true, + ); let mut ingestions: Vec = Vec::new(); for ingestion in experience.ingestions.iter() {