First commit

This commit is contained in:
2025-05-12 10:58:23 +02:00
commit 244eb6e099
6 changed files with 210 additions and 0 deletions

16
train-cli/Cargo.toml Normal file
View File

@@ -0,0 +1,16 @@
[package]
name = "train-cli"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
postgres = { version = "0.19.10", features = ["with-chrono-0_4"] }
sqlx = { version = "0.8.5", features = ["rust_decimal"] }
clap = { version = "4.5.37", features = ["derive"] }
whoami = "1.6.0"
rust_decimal = { version = "1.37.1", features = ["db-postgres"] }
chrono = "0.4.41"

82
train-cli/src/main.rs Normal file
View File

@@ -0,0 +1,82 @@
use clap::Parser;
use postgres::{Client, NoTls};
use whoami;
use rust_decimal::Decimal;
use rust_decimal::prelude::FromPrimitive;
use chrono::{DateTime, Local, FixedOffset, TimeZone, NaiveTime, LocalResult, NaiveDateTime};
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct TrainingArgs {
// name of exercise
exercise: String,
// number of runs
runs: i16,
// number of reps per run
reps: i16,
// kilos
kilos: f32,
// time
time: Option<String>,
}
fn main() {
let args = TrainingArgs::parse();
insert_training(args);
}
fn parse_time (time: String) -> Option<DateTime<FixedOffset>> {
// check if time is an rfc3339 formatted timestamp, i.e. "2025-05-20T14:30:10+0200" (can also
// have milliseconds
let datetimetz = DateTime::parse_from_rfc3339(&time);
if let Ok(dt) = datetimetz {
return Some(dt);
}
// check if time is a simple "date time" without timezone, then apply user session
// time zone
let naive_datetime = NaiveDateTime::parse_from_str(&time, "%Y-%m-%d %H:%M:%S");
if let Ok(ndt) = naive_datetime {
let dt: DateTime<Local> = Local.from_local_datetime(&ndt).unwrap();
return Some(dt.into());
}
// check if time is merely a simple hour:minute:second -- then apply
// current date from user session
let naive_time = NaiveTime::parse_from_str(&time, "%H:%M:%S");
if let Ok(nt) = naive_time {
return match Local::now().with_time(nt) {
LocalResult::Single(dt) => Some(dt.into()),
LocalResult::Ambiguous(earliest, _latest) => Some(earliest.into()),
LocalResult::None => None
}
}
return None;
}
fn insert_training (args: TrainingArgs) {
let mut client = Client::connect("dbname=training host=/var/run/postgresql", NoTls).unwrap();
let dec_kilos = Decimal::from_f32(args.kilos).unwrap();
let dt = match args.time {
Some(time) => parse_time(time),
None => Some(Local::now().into()),
};
match dt {
None => println!("Invalid time/date specified"),
Some(time) => {
let res = client.execute("insert into training values
(default, (select id from account where login=$1), $2,
(select e.id from exercise e inner join shorthand s on s.exercise=e.id where s.name=$3), $4, $5, $6)",
&[&whoami::username(), &time, &args.exercise, &args.runs, &args.reps, &dec_kilos]);
match res {
Ok(_inserted) => println!("Training inserted"),
Err(e) => println!("Training not inserted, since: {}", e)
}
}
}
}