update
This commit is contained in:
parent
b892946c25
commit
841e561973
|
@ -2,3 +2,4 @@ hard_tabs = true
|
|||
use_field_init_shorthand = true
|
||||
unstable_features = true
|
||||
imports_granularity = "Crate"
|
||||
merge_derives = false
|
38
Cargo.lock
generated
38
Cargo.lock
generated
|
@ -142,6 +142,15 @@ dependencies = [
|
|||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.18"
|
||||
|
@ -297,6 +306,22 @@ dependencies = [
|
|||
"psychonaut_journal_types",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "journal_cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"journal",
|
||||
"prettytable-rs",
|
||||
"psychonaut_journal_types",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -389,19 +414,6 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psychonaut_journal_cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"journal",
|
||||
"prettytable-rs",
|
||||
"psychonaut_journal_types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psychonaut_journal_types"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -5,6 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.215", features = ["std", "derive", "serde_derive"] }
|
||||
serde_json = "1.0.132"
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
psychonaut_journal_types = { path = "../psychonaut_journal_types" }
|
||||
rand = "0.8.5"
|
||||
|
|
|
@ -1,132 +1,28 @@
|
|||
use rand::Rng;
|
||||
|
||||
use crate::types::{
|
||||
AdministrationRoute, Consumer, CustomUnit, Experience, ExportFormat, Ingestion, Unit,
|
||||
};
|
||||
use crate::types::{AdministrationRoute, Consumer, CustomUnit, Ingestion, Session, Unit};
|
||||
|
||||
pub type JournalType = Box<dyn Journal>;
|
||||
|
||||
pub trait Journal {
|
||||
fn get_custom_unit(&self, id: i64) -> Option<CustomUnit>;
|
||||
fn get_experience(&self, id: i64) -> Option<Experience>;
|
||||
fn get_experience_ingestions(&self, id: i64) -> Option<Vec<Ingestion>>;
|
||||
fn get_experience_ingestions_by(
|
||||
fn get_custom_unit(&self, id: i32) -> Option<CustomUnit>;
|
||||
fn get_session(&self, id: i32) -> Option<Session>;
|
||||
fn get_session_ingestions(&self, id: i32) -> Option<Vec<Ingestion>>;
|
||||
fn get_session_ingestions_by(
|
||||
&self,
|
||||
id: i64,
|
||||
id: i32,
|
||||
substance_filter: Option<&Vec<String>>,
|
||||
route_filter: Option<&Vec<AdministrationRoute>>,
|
||||
consumer_filter: Option<&Vec<Consumer>>,
|
||||
) -> Option<Vec<Ingestion>>;
|
||||
|
||||
fn first_experience_by_title(&self, title: &str) -> Option<Experience>;
|
||||
fn set_custom_unit(&mut self, id: i32, unit: CustomUnit);
|
||||
fn set_session(&mut self, id: i32, session: Session);
|
||||
fn set_session_ingestions(&mut self, id: i32, ingestions: Vec<Ingestion>);
|
||||
|
||||
fn first_session_by_title(&self, title: &str) -> Option<Session>;
|
||||
fn resolve_unit(&self, unit: &Unit) -> Unit;
|
||||
|
||||
fn import_psychonaut(&mut self, data: psychonaut_journal_types::ExportData);
|
||||
fn save(&self) -> Result<(), Box<dyn std::error::Error>>;
|
||||
}
|
||||
|
||||
pub type InMemJournal = ExportFormat;
|
||||
|
||||
impl Journal for InMemJournal {
|
||||
fn get_custom_unit(&self, id: i64) -> Option<CustomUnit> {
|
||||
self.custom_units.get(&id).cloned()
|
||||
}
|
||||
|
||||
fn get_experience(&self, id: i64) -> Option<Experience> {
|
||||
self.experiences.get(&id).cloned()
|
||||
}
|
||||
|
||||
fn get_experience_ingestions(&self, id: i64) -> Option<Vec<Ingestion>> {
|
||||
self.ingestions.get(&id).cloned()
|
||||
}
|
||||
|
||||
fn get_experience_ingestions_by(
|
||||
&self,
|
||||
id: i64,
|
||||
substance_filter: Option<&Vec<String>>,
|
||||
route_filter: Option<&Vec<AdministrationRoute>>,
|
||||
consumer_filter: Option<&Vec<Consumer>>,
|
||||
) -> Option<Vec<Ingestion>> {
|
||||
if let Some(ingestions) = self.get_experience_ingestions(id) {
|
||||
let ingestions = ingestions
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|ingestion| {
|
||||
if let Some(substance_filter) = substance_filter {
|
||||
if !substance_filter.contains(&ingestion.substance_name) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(route_filter) = route_filter {
|
||||
if !route_filter.contains(&ingestion.roa) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(consumer_filter) = consumer_filter {
|
||||
if !consumer_filter.contains(&ingestion.consumer) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
.collect::<Vec<Ingestion>>();
|
||||
|
||||
Some(ingestions)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_unit(&self, unit: &Unit) -> Unit {
|
||||
match unit {
|
||||
Unit::Simple(_) => unit.clone(),
|
||||
Unit::Custom { id, unit } => match unit {
|
||||
Some(unit) => Unit::Custom {
|
||||
id: *id,
|
||||
unit: Some(unit.clone()),
|
||||
},
|
||||
None => Unit::Custom {
|
||||
id: *id,
|
||||
unit: self.get_custom_unit(*id),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn first_experience_by_title(&self, title: &str) -> Option<Experience> {
|
||||
self.experiences
|
||||
.values()
|
||||
.find(|&experience| experience.title == title)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn import_psychonaut(&mut self, data: psychonaut_journal_types::ExportData) {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
for custom_unit in data.custom_units.into_iter() {
|
||||
self.custom_units
|
||||
.insert(custom_unit.id, CustomUnit::from(custom_unit));
|
||||
}
|
||||
|
||||
for experience in data.experiences.into_iter() {
|
||||
let experience_id = rng.gen::<i64>();
|
||||
|
||||
let mut ingestions: Vec<Ingestion> = Vec::new();
|
||||
for ingestion in experience.ingestions.iter() {
|
||||
ingestions.push(Ingestion::from(ingestion.clone()))
|
||||
}
|
||||
|
||||
self.ingestions.insert(experience_id, ingestions);
|
||||
|
||||
self.experiences.insert(
|
||||
experience_id,
|
||||
Experience {
|
||||
id: experience_id,
|
||||
..Experience::from(experience)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
mod json_journal;
|
||||
pub use json_journal::JSONJournal;
|
||||
|
|
151
journal/src/journal/json_journal.rs
Normal file
151
journal/src/journal/json_journal.rs
Normal file
|
@ -0,0 +1,151 @@
|
|||
use std::{collections::HashMap, fs::File};
|
||||
|
||||
use crate::types::{AdministrationRoute, Consumer, CustomUnit, Ingestion, Session, Unit};
|
||||
|
||||
use super::{Journal, JournalType};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct JSONJournalData {
|
||||
pub ingestions: HashMap<i32, Vec<Ingestion>>,
|
||||
pub sessions: HashMap<i32, Session>,
|
||||
pub custom_units: HashMap<i32, CustomUnit>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct JSONJournal {
|
||||
data: JSONJournalData,
|
||||
filename: String,
|
||||
}
|
||||
|
||||
impl JSONJournal {
|
||||
pub fn create(filename: &String) -> Result<JournalType, Box<dyn std::error::Error>> {
|
||||
let data: JSONJournalData = JSONJournalData {
|
||||
ingestions: HashMap::new(),
|
||||
sessions: HashMap::new(),
|
||||
custom_units: HashMap::new(),
|
||||
};
|
||||
|
||||
let journal = JSONJournal {
|
||||
data,
|
||||
filename: filename.clone(),
|
||||
};
|
||||
|
||||
journal.save()?;
|
||||
|
||||
Ok(Box::new(journal))
|
||||
}
|
||||
|
||||
pub fn load(filename: &String) -> Result<JournalType, Box<dyn std::error::Error>> {
|
||||
let file = File::open(filename)?;
|
||||
|
||||
let data: JSONJournalData = serde_json::from_reader(file)?;
|
||||
let journal = JSONJournal {
|
||||
data,
|
||||
filename: filename.clone(),
|
||||
};
|
||||
|
||||
Ok(Box::new(journal))
|
||||
}
|
||||
|
||||
pub fn save(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let file = File::create(self.filename.clone())?;
|
||||
serde_json::to_writer_pretty(&file, &self.data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Journal for JSONJournal {
|
||||
fn get_custom_unit(&self, id: i32) -> Option<CustomUnit> {
|
||||
self.data.custom_units.get(&id).cloned()
|
||||
}
|
||||
|
||||
fn get_session(&self, id: i32) -> Option<Session> {
|
||||
self.data.sessions.get(&id).cloned()
|
||||
}
|
||||
|
||||
fn get_session_ingestions(&self, id: i32) -> Option<Vec<Ingestion>> {
|
||||
self.data.ingestions.get(&id).cloned()
|
||||
}
|
||||
|
||||
fn get_session_ingestions_by(
|
||||
&self,
|
||||
id: i32,
|
||||
substance_filter: Option<&Vec<String>>,
|
||||
route_filter: Option<&Vec<AdministrationRoute>>,
|
||||
consumer_filter: Option<&Vec<Consumer>>,
|
||||
) -> Option<Vec<Ingestion>> {
|
||||
if let Some(ingestions) = self.get_session_ingestions(id) {
|
||||
let ingestions = ingestions
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|ingestion| {
|
||||
if let Some(substance_filter) = substance_filter {
|
||||
if !substance_filter.contains(&ingestion.substance_name) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(route_filter) = route_filter {
|
||||
if !route_filter.contains(&ingestion.roa) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(consumer_filter) = consumer_filter {
|
||||
if !consumer_filter.contains(&ingestion.consumer) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
.collect::<Vec<Ingestion>>();
|
||||
|
||||
Some(ingestions)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_unit(&self, unit: &Unit) -> Unit {
|
||||
match unit {
|
||||
Unit::Simple(_) => unit.clone(),
|
||||
Unit::Custom { id, unit } => match unit {
|
||||
Some(unit) => Unit::Custom {
|
||||
id: *id,
|
||||
unit: Some(unit.clone()),
|
||||
},
|
||||
None => Unit::Custom {
|
||||
id: *id,
|
||||
unit: self.get_custom_unit(*id),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn first_session_by_title(&self, title: &str) -> Option<Session> {
|
||||
self.data
|
||||
.sessions
|
||||
.values()
|
||||
.find(|&session| session.title == title)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn set_custom_unit(&mut self, unit_id: i32, unit: CustomUnit) {
|
||||
self.data.custom_units.insert(unit_id, unit);
|
||||
}
|
||||
|
||||
fn set_session(&mut self, session_id: i32, session: Session) {
|
||||
self.data.sessions.insert(session_id, session);
|
||||
}
|
||||
|
||||
fn set_session_ingestions(&mut self, session_id: i32, ingestions: Vec<Ingestion>) {
|
||||
self.data.ingestions.insert(session_id, ingestions);
|
||||
}
|
||||
|
||||
fn save(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
self.save()
|
||||
}
|
||||
}
|
|
@ -1,2 +1,4 @@
|
|||
pub mod journal;
|
||||
pub mod types;
|
||||
|
||||
pub use journal::JSONJournal;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub enum Consumer {
|
||||
Default,
|
||||
Named(String),
|
||||
|
|
|
@ -2,8 +2,9 @@ use super::{from_unix_millis, AdministrationRoute, Dose};
|
|||
use chrono::{DateTime, Utc};
|
||||
|
||||
#[derive(Default, PartialEq, Debug, Clone)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct CustomUnit {
|
||||
pub id: i64,
|
||||
pub id: i32,
|
||||
|
||||
pub name: String,
|
||||
pub substance_name: String,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#[derive(Default, PartialEq, Debug, Clone)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Dose {
|
||||
pub value: Option<f64>,
|
||||
pub is_estimate: bool,
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use super::{CustomUnit, Experience, Ingestion};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ExportFormat {
|
||||
pub ingestions: HashMap<i64, Vec<Ingestion>>,
|
||||
pub experiences: HashMap<i64, Experience>,
|
||||
pub custom_units: HashMap<i64, CustomUnit>,
|
||||
}
|
|
@ -3,6 +3,7 @@ use chrono::{DateTime, Utc};
|
|||
use super::{dose::Dose, from_unix_millis, AdministrationRoute, Consumer, Unit};
|
||||
|
||||
#[derive(PartialEq, Default, Debug, Clone)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Ingestion {
|
||||
pub substance_name: String,
|
||||
pub ingestion_time: DateTime<Utc>,
|
||||
|
|
|
@ -15,18 +15,15 @@ pub use consumer::Consumer;
|
|||
mod ingestion;
|
||||
pub use ingestion::Ingestion;
|
||||
|
||||
mod custom_substance;
|
||||
pub use custom_substance::CustomSubstance;
|
||||
mod substance;
|
||||
pub use substance::Substance;
|
||||
|
||||
mod experience;
|
||||
pub use experience::Experience;
|
||||
mod session;
|
||||
pub use session::Session;
|
||||
|
||||
mod custom_unit;
|
||||
pub use custom_unit::CustomUnit;
|
||||
|
||||
mod export;
|
||||
pub use export::ExportFormat;
|
||||
|
||||
pub type AdministrationRoute = psychonaut_journal_types::AdministrationRoute;
|
||||
|
||||
pub(crate) fn from_unix_millis(time: u64) -> DateTime<Utc> {
|
||||
|
|
|
@ -3,22 +3,21 @@ use chrono::{DateTime, Utc};
|
|||
use super::from_unix_millis;
|
||||
|
||||
#[derive(PartialEq, Default, Debug, Clone)]
|
||||
pub struct Experience {
|
||||
pub id: i64,
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Session {
|
||||
pub id: i32,
|
||||
pub title: String,
|
||||
pub text: String,
|
||||
pub creation_time: DateTime<Utc>,
|
||||
pub modified_time: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl From<psychonaut_journal_types::Experience> for Experience {
|
||||
impl From<psychonaut_journal_types::Experience> for Session {
|
||||
fn from(experience: psychonaut_journal_types::Experience) -> Self {
|
||||
Experience {
|
||||
Session {
|
||||
id: 0,
|
||||
title: experience.title,
|
||||
text: experience.text,
|
||||
creation_time: from_unix_millis(experience.creation_time),
|
||||
modified_time: from_unix_millis(experience.modified_time),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,25 +26,23 @@ impl From<psychonaut_journal_types::Experience> for Experience {
|
|||
mod tests {
|
||||
use crate::types::from_unix_millis;
|
||||
|
||||
use super::Experience;
|
||||
use super::Session;
|
||||
use psychonaut_journal_types::Experience as PsychonautExperience;
|
||||
|
||||
#[test]
|
||||
fn psychonaut_journal_conversion() {
|
||||
assert_eq!(
|
||||
Experience::from(PsychonautExperience {
|
||||
Session::from(PsychonautExperience {
|
||||
title: "Experience".to_string(),
|
||||
text: "Some Text".to_string(),
|
||||
creation_time: 0,
|
||||
modified_time: 0,
|
||||
ingestions: vec![]
|
||||
}),
|
||||
Experience {
|
||||
Session {
|
||||
id: 0,
|
||||
title: "Experience".to_string(),
|
||||
text: "Some Text".to_string(),
|
||||
creation_time: from_unix_millis(0),
|
||||
modified_time: from_unix_millis(0)
|
||||
}
|
||||
);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct CustomSubstance {
|
||||
pub struct Substance {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub units: String,
|
|
@ -1,9 +1,10 @@
|
|||
use super::CustomUnit;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub enum Unit {
|
||||
Simple(String),
|
||||
Custom { id: i64, unit: Option<CustomUnit> },
|
||||
Custom { id: i32, unit: Option<CustomUnit> },
|
||||
}
|
||||
|
||||
impl Default for Unit {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
[package]
|
||||
name = "psychonaut_journal_cli"
|
||||
name = "journal_cli"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "journal-cli"
|
||||
name = "journal_cli"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
|
@ -17,6 +17,8 @@ clap = { version = "4.5.21", features = ["derive", "env"] }
|
|||
prettytable-rs = "0.10.0"
|
||||
serde = { version = "1.0.215", features = ["std", "derive", "serde_derive"] }
|
||||
serde_json = "1.0.132"
|
||||
clap_complete = "4.5.38"
|
||||
rand = "0.8.5"
|
||||
#serde_with = "3.11.0"
|
||||
#string-error = "0.1.0"
|
||||
#termcolor = "1.4.1"
|
||||
|
|
|
@ -1,32 +1,49 @@
|
|||
use clap::{Parser, Subcommand};
|
||||
use crate::commands;
|
||||
use clap_complete::Shell;
|
||||
|
||||
#[derive(Debug, Clone, Subcommand)]
|
||||
#[command(rename_all = "camelCase")]
|
||||
#[command(flatten_help = false)]
|
||||
pub enum Commands {
|
||||
PrintExperience(crate::commands::print_experience::PrintExperienceArgs),
|
||||
}
|
||||
|
||||
// TODO: add env vars back to this,
|
||||
// clashes with multiple=false when one set as env and other as arg
|
||||
#[derive(Debug, Clone, clap::Args)]
|
||||
#[group(id = "journal_location", multiple = false)]
|
||||
pub struct JournalLocationArgs {
|
||||
// journal file is writable
|
||||
#[arg(group = "journal_location", global = true, long)]
|
||||
pub journal_file: Option<String>,
|
||||
|
||||
// export file is read-only
|
||||
#[arg(group = "journal_location", global = true, long)]
|
||||
pub psychonaut_export_file: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(flatten_help = false)]
|
||||
#[derive(Debug, clap::Parser)]
|
||||
pub struct Args {
|
||||
#[command(subcommand)]
|
||||
pub command: Commands,
|
||||
|
||||
#[command(flatten)]
|
||||
pub journal_location: JournalLocationArgs,
|
||||
pub journal_location: MaybeJournalLocation,
|
||||
|
||||
#[arg(long = "generate", value_enum)]
|
||||
pub autocomplete_shell_kind: Option<Shell>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, clap::Subcommand)]
|
||||
#[command(rename_all = "camelCase")]
|
||||
#[command(flatten_help = false)]
|
||||
pub enum Commands {
|
||||
Journal(commands::journal::JournalSubcommand),
|
||||
PrintSession(commands::PrintSessionArgs),
|
||||
GenerateShell,
|
||||
}
|
||||
|
||||
// We need two copies of this struct, one for the root command
|
||||
// another for subcommands which require the required parts
|
||||
#[derive(Debug, Clone, clap::Args)]
|
||||
#[group(id = "journal_location")]
|
||||
pub struct JournalLocation {
|
||||
// journal file is writable
|
||||
#[arg(long, group = "journal_location", value_hint = clap::ValueHint::FilePath)]
|
||||
pub journal_file: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, clap::Args)]
|
||||
#[group(id = "journal_location")]
|
||||
pub struct MaybeJournalLocation {
|
||||
// journal file is writable
|
||||
#[arg(long, group = "journal_location", global = true, value_hint = clap::ValueHint::FilePath)]
|
||||
pub journal_file: Option<String>,
|
||||
}
|
||||
|
||||
impl From<MaybeJournalLocation> for JournalLocation {
|
||||
fn from(value: MaybeJournalLocation) -> Self {
|
||||
JournalLocation {
|
||||
journal_file: value.journal_file.unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
59
journal_cli/src/commands/journal/import_psychonaut.rs
Normal file
59
journal_cli/src/commands/journal/import_psychonaut.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use std::fs::File;
|
||||
|
||||
use journal::{
|
||||
journal::JSONJournal,
|
||||
types::{CustomUnit, Ingestion, Session},
|
||||
};
|
||||
use rand::Rng;
|
||||
|
||||
use crate::args::JournalLocation;
|
||||
|
||||
#[derive(Debug, Clone, clap::Args)]
|
||||
#[group(requires = "journal_location")]
|
||||
pub struct JournalImportPsychonautArgs {
|
||||
#[command(flatten)]
|
||||
pub journal_location: JournalLocation,
|
||||
|
||||
#[arg(long, value_hint = clap::ValueHint::FilePath)]
|
||||
pub export_file: String,
|
||||
}
|
||||
|
||||
pub fn journal_import_psychonaut(
|
||||
args: &JournalImportPsychonautArgs,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let journal_file_location = &args.journal_location.journal_file.clone();
|
||||
|
||||
let mut journal = JSONJournal::load(journal_file_location)?;
|
||||
|
||||
let export_file = File::open(args.export_file.clone())?;
|
||||
let export_data: psychonaut_journal_types::ExportData = serde_json::from_reader(export_file)?;
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
for custom_unit in export_data.custom_units.into_iter() {
|
||||
journal.set_custom_unit(custom_unit.id, CustomUnit::from(custom_unit));
|
||||
}
|
||||
|
||||
for session in export_data.experiences.into_iter() {
|
||||
let session_id = rng.gen::<i32>();
|
||||
|
||||
let mut ingestions: Vec<Ingestion> = Vec::new();
|
||||
for ingestion in session.ingestions.iter() {
|
||||
ingestions.push(Ingestion::from(ingestion.clone()))
|
||||
}
|
||||
|
||||
journal.set_session_ingestions(session_id, ingestions);
|
||||
|
||||
journal.set_session(
|
||||
session_id,
|
||||
Session {
|
||||
id: session_id,
|
||||
..Session::from(session)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
journal.save()?;
|
||||
|
||||
Ok(())
|
||||
}
|
16
journal_cli/src/commands/journal/mod.rs
Normal file
16
journal_cli/src/commands/journal/mod.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
#[derive(Debug, Clone, clap::Args)]
|
||||
#[command(rename_all = "camelCase")]
|
||||
pub struct JournalSubcommand {
|
||||
#[command(subcommand)]
|
||||
pub subcommand: JournalSubcommands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, clap::Subcommand)]
|
||||
#[command(rename_all = "camelCase")]
|
||||
pub enum JournalSubcommands {
|
||||
New(new::JournalNewArgs),
|
||||
ImportPsychonaut(import_psychonaut::JournalImportPsychonautArgs),
|
||||
}
|
||||
|
||||
pub mod import_psychonaut;
|
||||
pub mod new;
|
27
journal_cli/src/commands/journal/new.rs
Normal file
27
journal_cli/src/commands/journal/new.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use std::path::Path;
|
||||
|
||||
use journal::journal::JSONJournal;
|
||||
|
||||
use crate::args::JournalLocation;
|
||||
|
||||
#[derive(Debug, Clone, clap::Args)]
|
||||
#[group(requires = "journal_location")]
|
||||
pub struct JournalNewArgs {
|
||||
#[command(flatten)]
|
||||
pub journal_location: JournalLocation,
|
||||
|
||||
#[arg(long)]
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
pub fn journal_new(args: &JournalNewArgs) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let file_location = &args.journal_location.journal_file.clone();
|
||||
|
||||
if Path::new(&file_location).exists() && !args.force {
|
||||
panic!("file exists")
|
||||
}
|
||||
|
||||
JSONJournal::create(file_location)?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1 +1,4 @@
|
|||
pub mod print_experience;
|
||||
pub mod journal;
|
||||
|
||||
mod print_session;
|
||||
pub use print_session::{print_session, PrintSessionArgs};
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
use journal::types::Consumer;
|
||||
use journal::{journal::JSONJournal, types::Consumer};
|
||||
|
||||
use crate::{
|
||||
args::Args, display::print_ingestion_log, formatting::format_experience_title,
|
||||
utils::load_journal,
|
||||
};
|
||||
use crate::{args::Args, display::print_ingestion_log, formatting::format_session_title};
|
||||
|
||||
#[derive(Debug, Clone, clap::Args)]
|
||||
#[group(requires = "journal_location")]
|
||||
pub struct PrintExperienceArgs {
|
||||
pub experience_title: String,
|
||||
pub struct PrintSessionArgs {
|
||||
pub session_title: String,
|
||||
|
||||
#[arg(long)]
|
||||
pub substance_filter: Option<Vec<String>>,
|
||||
|
@ -34,33 +31,25 @@ pub fn parse_consumer_filter(consumer_filter: Option<Vec<String>>) -> Option<Vec
|
|||
}
|
||||
}
|
||||
|
||||
pub fn print_experience(
|
||||
pub fn print_session(
|
||||
global_args: &Args,
|
||||
args: &PrintExperienceArgs,
|
||||
args: &PrintSessionArgs,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let journal = load_journal(
|
||||
&global_args
|
||||
.journal_location
|
||||
.psychonaut_export_file
|
||||
.clone()
|
||||
.unwrap(),
|
||||
)
|
||||
.expect("could not load export data");
|
||||
let journal = JSONJournal::load(global_args.journal_location.journal_file.as_ref().unwrap())?;
|
||||
let session = journal
|
||||
.first_session_by_title(&args.session_title)
|
||||
.expect("could not find session");
|
||||
|
||||
let experience = journal
|
||||
.first_experience_by_title(&args.experience_title)
|
||||
.expect("could not find experience");
|
||||
//println!("{:#?}", &session);
|
||||
|
||||
//println!("{:#?}", &experience);
|
||||
|
||||
println!("{}", format_experience_title(&experience));
|
||||
println!("{}", format_session_title(&session));
|
||||
|
||||
let substance_filter = args.substance_filter.clone();
|
||||
let consumer_filter = parse_consumer_filter(args.consumer_filter.clone());
|
||||
|
||||
print_ingestion_log(
|
||||
&journal,
|
||||
&experience,
|
||||
&session,
|
||||
substance_filter.as_ref(),
|
||||
consumer_filter.as_ref(),
|
||||
);
|
|
@ -1,19 +1,19 @@
|
|||
use journal::{
|
||||
journal::JournalType,
|
||||
types::{format_dose, Consumer, Experience},
|
||||
types::{format_dose, Consumer, Session},
|
||||
};
|
||||
|
||||
use crate::formatting::{format_ingestion_roa, format_ingestion_time};
|
||||
|
||||
pub fn print_ingestion_log(
|
||||
journal: &JournalType,
|
||||
experience: &Experience,
|
||||
session: &Session,
|
||||
substance_filter: Option<&Vec<String>>,
|
||||
consumer_filter: Option<&Vec<Consumer>>,
|
||||
) {
|
||||
for ingestion in journal
|
||||
.get_experience_ingestions(experience.id)
|
||||
.expect("could not find ingestions for experience")
|
||||
.get_session_ingestions(session.id)
|
||||
.expect("could not find ingestions for session")
|
||||
.iter()
|
||||
{
|
||||
if let Some(substance_filter) = substance_filter {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use journal::types::{Experience, Ingestion, Unit};
|
||||
use journal::types::{Ingestion, Session, Unit};
|
||||
|
||||
pub fn format_experience_title(experience: &Experience) -> String {
|
||||
format!("{}: {}", experience.title, experience.creation_time)
|
||||
pub fn format_session_title(session: &Session) -> String {
|
||||
format!("{}: {}", session.title, session.creation_time)
|
||||
}
|
||||
|
||||
pub fn format_ingestion_time(ingestion: &Ingestion) -> String {
|
||||
|
@ -24,11 +24,11 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn format_experience_title() {
|
||||
let result = super::format_experience_title(&Experience {
|
||||
fn format_session_title() {
|
||||
let result = super::format_session_title(&Session {
|
||||
title: "Title".to_string(),
|
||||
creation_time: Utc.timestamp_millis_opt(0).unwrap(),
|
||||
..Experience::default()
|
||||
..Session::default()
|
||||
});
|
||||
assert_eq!(result, "Title: 1970-01-01 00:00:00 UTC");
|
||||
}
|
||||
|
|
|
@ -1,25 +1,36 @@
|
|||
use clap::Parser;
|
||||
use clap::{CommandFactory, Parser};
|
||||
use clap_complete::{generate, Generator};
|
||||
|
||||
pub mod args;
|
||||
pub mod commands;
|
||||
pub mod display;
|
||||
pub mod formatting;
|
||||
pub mod utils;
|
||||
|
||||
use commands::print_experience::print_experience;
|
||||
use args::{Args, Commands};
|
||||
|
||||
fn print_completions<G: Generator>(gen: G, cmd: &mut clap::Command) {
|
||||
generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout());
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args = args::Args::parse();
|
||||
let args = Args::parse();
|
||||
|
||||
let command = args.command.to_owned();
|
||||
|
||||
println!("{:#?}", args);
|
||||
//println!("{:#?}", args);
|
||||
|
||||
match command {
|
||||
args::Commands::PrintExperience(print_experience_args) => {
|
||||
println!("{:#?}", print_experience_args);
|
||||
|
||||
print_experience(&args, &print_experience_args)?
|
||||
Commands::PrintSession(subommand_args) => commands::print_session(&args, &subommand_args)?,
|
||||
Commands::Journal(command) => match command.subcommand {
|
||||
commands::journal::JournalSubcommands::New(subommand_args) => {
|
||||
commands::journal::new::journal_new(&subommand_args)?
|
||||
}
|
||||
commands::journal::JournalSubcommands::ImportPsychonaut(subommand_args) => {
|
||||
commands::journal::import_psychonaut::journal_import_psychonaut(&subommand_args)?
|
||||
}
|
||||
},
|
||||
Commands::GenerateShell => {
|
||||
print_completions(args.autocomplete_shell_kind.unwrap(), &mut Args::command())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
use journal::journal::{InMemJournal, Journal, JournalType};
|
||||
use psychonaut_journal_types::ExportData;
|
||||
use std::fs::File;
|
||||
|
||||
pub fn load_journal(filename: &String) -> Result<JournalType, Box<dyn std::error::Error>> {
|
||||
let file = File::open(filename)?;
|
||||
|
||||
let export_data: ExportData = serde_json::from_reader(file)?;
|
||||
|
||||
let mut journal = InMemJournal::default();
|
||||
journal.import_psychonaut(export_data);
|
||||
|
||||
Ok(Box::new(journal))
|
||||
}
|
5
loadShellCompletions.sh
Normal file
5
loadShellCompletions.sh
Normal file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
FILE="$(mktemp)"
|
||||
cargo run --bin journal_cli -- --generate bash generateShell >"${FILE}"
|
||||
# shellcheck disable=SC1090
|
||||
source "${FILE}"
|
|
@ -56,7 +56,7 @@ pub struct Ingestion {
|
|||
pub is_estimate: bool,
|
||||
#[serde(rename = "estimatedDoseStandardDeviation")]
|
||||
pub estimate_standard_deviation: Option<f64>,
|
||||
pub custom_unit_id: Option<i64>,
|
||||
pub custom_unit_id: Option<i32>,
|
||||
#[serde(rename = "administrationRoute")]
|
||||
pub roa: AdministrationRoute,
|
||||
pub consumer_name: Option<String>,
|
||||
|
@ -79,8 +79,6 @@ pub struct Experience {
|
|||
pub text: String,
|
||||
#[serde(rename = "creationDate")]
|
||||
pub creation_time: u64,
|
||||
#[serde(rename = "sortDate")]
|
||||
pub modified_time: u64,
|
||||
pub ingestions: Vec<Ingestion>,
|
||||
}
|
||||
|
||||
|
@ -94,7 +92,7 @@ pub struct SubstanceCompanion {
|
|||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CustomUnit {
|
||||
pub id: i64,
|
||||
pub id: i32,
|
||||
pub substance_name: String,
|
||||
pub name: String,
|
||||
#[serde(rename = "creationDate")]
|
||||
|
|
Loading…
Reference in a new issue