This commit is contained in:
chaos 2024-11-23 21:43:28 +00:00
parent b892946c25
commit 841e561973
29 changed files with 418 additions and 252 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
dotenv_if_exists ".dev.env"

View file

@ -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
View file

@ -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"

View file

@ -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"

View file

@ -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;

View 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()
}
}

View file

@ -1,2 +1,4 @@
pub mod journal;
pub mod types;
pub use journal::JSONJournal;

View file

@ -1,6 +1,7 @@
use std::fmt::Display;
#[derive(PartialEq, Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum Consumer {
Default,
Named(String),

View file

@ -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,

View file

@ -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,

View file

@ -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>,
}

View file

@ -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>,

View file

@ -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> {

View file

@ -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)
}
);
}

View file

@ -1,5 +1,5 @@
#[derive(Debug, Clone)]
pub struct CustomSubstance {
pub struct Substance {
pub name: String,
pub description: String,
pub units: String,

View file

@ -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 {

View file

@ -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"

View file

@ -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(),
}
}
}

View 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(())
}

View 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;

View 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(())
}

View file

@ -1 +1,4 @@
pub mod print_experience;
pub mod journal;
mod print_session;
pub use print_session::{print_session, PrintSessionArgs};

View file

@ -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(),
);

View file

@ -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 {

View file

@ -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");
}

View file

@ -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())
}
}

View file

@ -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
View 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}"

View 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")]