Compare commits

..

2 commits

12 changed files with 614 additions and 1 deletions

57
Cargo.lock generated
View file

@ -26,6 +26,21 @@ dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "ashpd"
version = "0.8.1"
@ -364,6 +379,21 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "ciborium"
version = "0.2.2"
@ -2080,6 +2110,30 @@ dependencies = [
"tracing",
]
[[package]]
name = "iana-time-zone"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "icu_collections"
version = "1.5.0"
@ -2330,7 +2384,10 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
name = "journal"
version = "0.1.0"
dependencies = [
"chrono",
"dioxus",
"serde",
"serde_json",
]
[[package]]

View file

@ -7,10 +7,22 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = { version = "0.4.41", features = ["serde"] }
dioxus = { version = "0.6.0", features = [] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.140"
[features]
default = ["desktop"]
web = ["dioxus/web"]
desktop = ["dioxus/desktop"]
mobile = ["dioxus/mobile"]
[profile.wasm-dev]
inherits = "dev"
opt-level = 1
[profile.server-dev]
inherits = "dev"
[profile.android-dev]
inherits = "dev"

156
assets/ketamine.json Normal file
View file

@ -0,0 +1,156 @@
{
"canonical_name": "ketamine",
"display_name": "Ketamine",
"aliases": [
"k",
"ket",
"kitty",
"kittens",
"special k",
"vitamin k"
],
"categories": [
"Dissociative",
"HabitForming",
"Common"
],
"description": "A short acting dissociative anaesthetic and hallucinogen commonly used in emergency medicine. It is the prototypical dissociative, and is widely used at sub-anesthetic doses recreationally. Small doses are comparable with alcohol, while larger doses are immobilising and lead to psychedelic experiences: the \"K-Hole.\"",
"effects": [
"A feeling of drunkenness and well being at low doses",
"As dose increases the user may begin to feel a disconnection from their body",
"At 'khole' doses the user may become completely disconnected from both body and mind"
],
"combos": [
{
"name": "2c-t-x",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "2c-x",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "5-meo-xxt",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "alcohol",
"note": "Both substances cause ataxia and bring a very high risk of vomiting and unconsciousness. If the user falls unconscious while under the influence there is a severe risk of vomit aspiration if they are not placed in the recovery position.",
"status": "Dangerous"
},
{
"name": "amphetamines",
"note": "No unexpected interactions, though likely to increase blood pressure but not an issue with sensible doses. Moving around on high doses of this combination may be ill advised due to risk of physical injury.",
"status": "Caution"
},
{
"name": "amt",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "benzodiazepines",
"note": "Both substances potentiate the ataxia and sedation caused by the other and can lead to unexpected loss of consciousness at high doses. While unconscious, vomit aspiration is a risk if not placed in the recovery position.",
"status": "Cuation"
},
{
"name": "caffeine",
"note": "No unexpected interactions",
"status": "Low Risk & No Synergy"
},
{
"name": "cannabis",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "cocaine",
"note": "No unexpected interactions, though likely to increase blood pressure but not an issue with sensible doses. Moving around on high doses of this combination may be ill advised due to risk of physical injury.",
"status": "Caution"
},
{
"name": "dextromethorphan",
"note": "Both substances primarily exert their effects through NMDA antagonism. Currently, there is no evidence regarding mechanisms that might reduce these effects.",
"status": "Low Risk & Synergy"
},
{
"name": "dmt",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "dox",
"note": "Ketamine and psychedelics tend to potentiate each other - go slowly.",
"status": "Low Risk & Synergy"
},
{
"name": "ghb/gbl",
"note": "Both substances cause ataxia and bring a risk of vomiting and unconsciousness. If the user falls unconscious while under the influence there is a severe risk of vomit aspiration if they are not placed in the recovery position.",
"status": "Dangerous"
},
{
"name": "lsd",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "maois",
"note": "MAO-B inhibitors appear to increase the potency of Ketamine. MAO-A inhbitors have some negative reports associated with the combination but there isn't much information available",
"status": "Caution"
},
{
"name": "mdma",
"note": "No unexpected interactions, though likely to increase blood pressure but not an issue with sensible doses. Moving around on high doses of this combination may be ill advised due to risk of physical injury.",
"status": "Low Risk & Synergy"
},
{
"name": "mescaline",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "mushrooms",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "mxe",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "nbomes",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "nitrous",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "opioids",
"note": "Both substances bring a risk of vomiting and unconsciousness. If the user falls unconscious while under the influence there is a severe risk of vomit aspiration if they are not placed in the recovery position.",
"status": "Dangerous"
},
{
"name": "pcp",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "ssris",
"note": "",
"status": "Low Risk & Synergy"
},
{
"name": "tramadol",
"note": "",
"status": "Dangerous"
}
],
"dose_note": "NOTE: Ketamine is based on weight. These are figures for the average 150 pound male. There is no concrete dose for the \"K-Hole\" as each user is different."
}

View file

@ -0,0 +1,95 @@
{
"name": "Cannabis",
"nicknames": [
"Marijuana",
"Weed"
],
"summary": "A common and widely used psychoactive plant, which is beginning to enjoy legal status for medical and even recreational use in some parts of the world. Usually smoked or eaten, primary effects are relaxation and an affinity towards food - a state described as being 'stoned.'",
"effects": "The most important of a total of...",
"routes": [
"Oral",
"Smoked"
],
"dosages": {
"Smoked": {
"unit": "mg",
"threshold": "0.4",
"light_common": "2",
"common_strong": "4",
"strong": "10"
},
"Oral": {
"unit": "mg",
"threshold": "1",
"light_common": "5",
"common_strong": "10",
"strong": "25"
}
},
"durations": {
"Oral": {
"onset": {
"minimum_seconds": 1200,
"maximum_seconds": 3600,
"display": "20 - 60 minutes"
},
"comeup": {
"minimum_seconds": 1800,
"maximum_seconds": 3600,
"display": "30 - 60 minutes"
},
"peak": {
"minimum_seconds": 3600,
"maximum_seconds": 7200,
"display": "1 - 2 hours"
},
"offset": {
"minimum_seconds": 14400,
"maximum_seconds": 21600,
"display": "4 - 6 hours"
},
"total": {
"minimum_seconds": 14400,
"maximum_seconds": 36000,
"display": "4 - 10 hours"
},
"after_effects": {
"minimum_seconds": 21600,
"maximum_seconds": 43200,
"display": "6 - 12 hours"
}
},
"Smoked": {
"onset": {
"minimum_seconds": 6,
"maximum_seconds": 600,
"display": "0.1 - 10 minutes"
},
"comeup": {
"minimum_seconds": 300,
"maximum_seconds": 600,
"display": "5 - 10 minutes"
},
"peak": {
"minimum_seconds": 900,
"maximum_seconds": 2700,
"display": "15 - 45 minutes"
},
"offset": {
"minimum_seconds": 10800,
"maximum_seconds": 14400,
"display": "3 - 4 hours"
},
"total": {
"minimum_seconds": 9000,
"maximum_seconds": 18000,
"display": "2.5 - 5 hours"
},
"after_effects": {
"minimum_seconds": 2700,
"maximum_seconds": 10800,
"display": "45 - 180 minutes"
}
}
}
}

1
src/lib.rs Normal file
View file

@ -0,0 +1 @@
pub mod pwjournal;

60
src/pwjournal.rs Normal file
View file

@ -0,0 +1,60 @@
#![allow(non_snake_case)]
use chrono::{serde::ts_milliseconds, DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct CustomUnit {
unit: String,
isEstimate: bool,
#[serde(with = "ts_milliseconds")]
creationDate: DateTime<Utc>,
administrationRoute: String,
isArchived: bool,
originalUnit: String,
estimatedDoseStandardDeviation: Option<String>,
dose: f64,
id: u64,
unitPlural: Option<String>,
name: String,
substanceName: String,
note: String,
}
#[cfg(test)]
mod tests {
use chrono::NaiveDate;
use super::*;
#[test]
fn load_custom_unit() {
let unit_raw_json = r#"
{
"unit": "can",
"isEstimate": false,
"creationDate": 1728475485000,
"administrationRoute": "ORAL",
"isArchived": false,
"originalUnit": "mg",
"estimatedDoseStandardDeviation": null,
"dose": 150,
"id": 382940388,
"unitPlural": null,
"name": "Monster",
"substanceName": "Caffeine",
"note": ""
}"#;
let unit: CustomUnit =
serde_json::from_str(unit_raw_json).expect("LOADING TEST CUSTOM UNIT SHOULD WORK");
assert_eq!(unit.dose, 150 as f64);
assert_eq!(unit.unit, String::from("can"));
let time = NaiveDate::from_ymd_opt(2024, 10, 9)
.unwrap()
.and_hms_micro_opt(12, 4, 45, 0)
.unwrap()
.and_utc();
assert_eq!(unit.creationDate, time);
}
}

100
src/substance.rs Normal file
View file

@ -0,0 +1,100 @@
use std::{collections::HashMap, error::Error, fmt, fs::File, io::BufReader, path::Path};
use serde::{Serialize, Deserialize};
mod links;
pub use links::Link;
mod dose;
pub use dose::Dose;
mod duration;
pub use duration::{Duration, TimeRange};
mod effect;
pub use effect::Effect;
mod administration;
pub use administration::{AdministrationType, Administration};
mod interaction;
pub use interaction::Interaction;
#[derive(Serialize, Deserialize, Debug)]
pub struct Drug {
pub canonical_name: String,
pub display_name: String,
pub aliases: Vec<String>,
pub categories: Vec<Category>,
pub description: Option<String>,
pub effects: Vec<String>,
pub roas: HashMap<AdministrationType, Administration>,
pub combos: Vec<Interaction>,
pub dose_note: Option<String>
}
pub fn load_substance<P: AsRef<Path>>(path: P) -> Result<Drug, Box<dyn Error>> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let s = serde_json::from_reader(reader)?;
Ok(s)
}
#[derive(Serialize, Deserialize, Debug)]
pub enum Category {
Depressant,
HabitForming,
Tentative,
ResearchChemical,
Psychedelic,
Stimulant,
Dissociative,
Inactive,
Empathogen,
Common,
Benzodiazepine,
Opioid,
Supplement,
Nootropic,
Barbiturate,
Deliriant,
Ssri
}
impl fmt::Display for Category {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let print_str = match self {
Category::Depressant => "depressant",
Category::HabitForming => "habit forming",
Category::Tentative => "tentative",
Category::ResearchChemical => "research chemical",
Category::Psychedelic => "psychedlic",
Category::Stimulant => "stimulant",
Category::Dissociative => "dissociative",
Category::Inactive => "inactive",
Category::Empathogen => "empathogen",
Category::Common => "common",
Category::Benzodiazepine => "benzodiazepine",
Category::Opioid => "opioid",
Category::Supplement => "supplement",
Category::Nootropic => "nootropic",
Category::Barbiturate => "barbiturate",
Category::Deliriant => "deliriant",
Category::Ssri => "SSRI",
};
write!(f, "{}", print_str)
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Tolerance {
full: String,
half: Option<TimeRange>,
zero: Option<TimeRange>
}

View file

@ -0,0 +1,30 @@
use::serde::{Serialize, Deserialize};
use super::{Dose, Duration};
#[derive(PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
pub enum AdministrationType {
Oral,
Insufflated,
Smoked,
Inhaled,
Sublingual,
Buccal,
Rectal,
Transdermal,
Subcutaneous,
Intramuscular,
Intravenous
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Administration {
dose: Dose,
duration: Duration
}
impl Administration {
pub fn new(dose: Dose, duration: Duration) -> Self {
Self { dose, duration }
}
}

73
src/substance/duration.rs Normal file
View file

@ -0,0 +1,73 @@
use std::fmt;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct Duration {
pub onset: DurationRange,
pub comeup: DurationRange,
pub peak: DurationRange,
pub offset: DurationRange,
pub total: DurationRange,
pub after_effects: DurationRange
}
#[derive(Serialize, Deserialize, Debug)]
pub struct DurationRange {
pub minimum_seconds: u32,
pub maximum_seconds: u32,
pub display: String
}
#[derive(Serialize, Deserialize, Debug)]
pub enum DurationTimeUnits {
Seconds,
Minutes,
Hours,
Days
}
impl fmt::Display for DurationTimeUnits {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let print_str = match self {
DurationTimeUnits::Seconds => "seconds",
DurationTimeUnits::Minutes => "minutes",
DurationTimeUnits::Hours => "hours",
DurationTimeUnits::Days => "days",
};
write!(f, "{}", print_str)
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct TimeRange {
units: DurationTimeUnits,
minimum: u32,
maximum: u32
}
impl TimeRange {
pub fn seconds(&self) -> (u32, u32) {
match self.units {
DurationTimeUnits::Seconds => (self.minimum, self.maximum),
DurationTimeUnits::Minutes => (self.minimum * 60, self.maximum * 60),
DurationTimeUnits::Hours => (self.minimum * 60 * 60, self.maximum * 60 * 60),
DurationTimeUnits::Days => (self.minimum * 60 * 60 * 24, self.maximum * 60 * 60 * 24)
}
}
pub fn minimum_seconds(&self) -> u32 {
self.seconds().0
}
pub fn maximum_seconds(&self) -> u32 {
self.seconds().1
}
}
impl fmt::Display for TimeRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{} {}", self.minimum, self.maximum, self.units)
}
}

9
src/substance/effect.rs Normal file
View file

@ -0,0 +1,9 @@
use serde::{Deserialize, Serialize};
use super::Link;
#[derive(Serialize, Deserialize, Debug)]
pub struct Effect {
name: String,
links: Vec<Link>
}

View file

@ -0,0 +1,14 @@
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct Interaction {
name: String,
note: String,
status: String
}
impl Interaction {
pub fn new(name: &str, note: &str, status: &str) -> Self {
Self { name: name.to_owned(), note: note.to_owned(), status: status.to_owned() }
}
}

6
src/substance/links.rs Normal file
View file

@ -0,0 +1,6 @@
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug)]
pub enum Link{
PsychonautWiki(String),
}