use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SetType {
Warmup,
Normal,
Failure,
Dropset,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CustomExerciseType {
WeightReps,
RepsOnly,
BodyweightReps,
BodyweightAssistedReps,
Duration,
WeightDuration,
DistanceDuration,
ShortDistanceWeight,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MuscleGroup {
Abdominals,
Shoulders,
Biceps,
Triceps,
Forearms,
Quadriceps,
Hamstrings,
Calves,
Glutes,
Abductors,
Adductors,
Lats,
UpperBack,
Traps,
LowerBack,
Chest,
Cardio,
Neck,
FullBody,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EquipmentCategory {
None,
Barbell,
Dumbbell,
Kettlebell,
Machine,
Plate,
ResistanceBand,
Suspension,
Other,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
pub struct RepRange {
#[serde(default)]
pub start: Option<f64>,
#[serde(default)]
pub end: Option<f64>,
}
impl RepRange {
pub fn new(start: f64, end: f64) -> Self {
Self {
start: Some(start),
end: Some(end),
}
}
}
pub(crate) fn flexible_f64<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
where
D: Deserializer<'de>,
{
let value: Option<Value> = Option::deserialize(deserializer)?;
match value {
None | Some(Value::Null) => Ok(None),
Some(Value::Number(n)) => Ok(n.as_f64()),
Some(Value::String(s)) if s.is_empty() => Ok(None),
Some(Value::String(s)) => s
.parse::<f64>()
.map(Some)
.map_err(|e| serde::de::Error::custom(format!("invalid number `{s}`: {e}"))),
Some(other) => Err(serde::de::Error::custom(format!(
"expected a number or numeric string, got {other}"
))),
}
}