#[cfg(feature = "clap")]
use clap::ValueEnum;
use kittycad_unit_conversion_derive::UnitConversion;
use parse_display_derive::{Display, FromStr};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[cfg(feature = "cxx")]
use crate::impl_extern_type;
#[derive(
Default,
Display,
FromStr,
Copy,
Eq,
PartialEq,
Debug,
JsonSchema,
Deserialize,
Serialize,
Clone,
Ord,
PartialOrd,
UnitConversion,
Hash,
)]
#[cfg_attr(feature = "clap", derive(ValueEnum))]
#[cfg_attr(feature = "tabled", derive(tabled::Tabled))]
#[display(style = "snake_case")]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
#[cfg_attr(
feature = "python",
pyo3::pyclass(eq, from_py_object),
pyo3_stub_gen::derive::gen_stub_pyclass_enum
)]
pub enum UnitLength {
#[serde(rename = "cm")]
#[display("cm")]
#[cfg_attr(feature = "clap", value(name = "cm"))]
Centimeters,
#[serde(rename = "ft")]
#[display("ft")]
#[cfg_attr(feature = "clap", value(name = "ft"))]
Feet,
#[serde(rename = "in")]
#[display("in")]
#[cfg_attr(feature = "clap", value(name = "in"))]
Inches,
#[default]
#[serde(rename = "m")]
#[display("m")]
#[cfg_attr(feature = "clap", value(name = "m"))]
Meters,
#[serde(rename = "mm")]
#[display("mm")]
#[cfg_attr(feature = "clap", value(name = "mm"))]
Millimeters,
#[serde(rename = "yd")]
#[display("yd")]
#[cfg_attr(feature = "clap", value(name = "yd"))]
Yards,
}
impl UnitLength {
pub fn as_measurement(self, value: f64) -> measurements::Length {
match self {
Self::Centimeters => measurements::Length::from_centimeters(value),
Self::Feet => measurements::Length::from_feet(value),
Self::Inches => measurements::Length::from_inches(value),
Self::Meters => measurements::Length::from_meters(value),
Self::Millimeters => measurements::Length::from_millimeters(value),
Self::Yards => measurements::Length::from_yards(value),
}
}
}
#[cfg(feature = "cxx")]
impl_extern_type! {
[Trivial]
UnitLength = "Enums::_UnitLength"
}
#[derive(
Default,
Display,
FromStr,
Copy,
Eq,
PartialEq,
Debug,
JsonSchema,
Deserialize,
Serialize,
Clone,
Ord,
PartialOrd,
UnitConversion,
Hash,
)]
#[cfg_attr(feature = "clap", derive(ValueEnum))]
#[cfg_attr(feature = "tabled", derive(tabled::Tabled))]
#[serde(rename_all = "snake_case")]
#[display(style = "snake_case")]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
#[cfg_attr(
feature = "python",
pyo3::pyclass(eq, from_py_object),
pyo3_stub_gen::derive::gen_stub_pyclass_enum
)]
pub enum UnitAngle {
#[default]
#[display("deg")]
#[cfg_attr(feature = "clap", value(name = "deg"))]
Degrees,
#[display("rad")]
#[cfg_attr(feature = "clap", value(name = "rad"))]
Radians,
}
#[derive(
Display,
FromStr,
Copy,
Eq,
PartialEq,
Debug,
JsonSchema,
Deserialize,
Serialize,
Clone,
Ord,
PartialOrd,
UnitConversion,
Default,
Hash,
)]
#[cfg_attr(feature = "clap", derive(ValueEnum))]
#[cfg_attr(feature = "tabled", derive(tabled::Tabled))]
#[serde(rename_all = "snake_case")]
#[display(style = "snake_case")]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
#[cfg_attr(
feature = "python",
pyo3::pyclass(eq, from_py_object),
pyo3_stub_gen::derive::gen_stub_pyclass_enum
)]
pub enum UnitArea {
#[serde(rename = "cm2")]
#[display("cm2")]
#[cfg_attr(feature = "clap", value(name = "cm2"))]
SquareCentimeters,
#[serde(rename = "dm2")]
#[display("dm2")]
#[cfg_attr(feature = "clap", value(name = "dm2"))]
SquareDecimeters,
#[serde(rename = "ft2")]
#[display("ft2")]
#[cfg_attr(feature = "clap", value(name = "ft2"))]
SquareFeet,
#[serde(rename = "in2")]
#[display("in2")]
#[cfg_attr(feature = "clap", value(name = "in2"))]
SquareInches,
#[serde(rename = "km2")]
#[display("km2")]
#[cfg_attr(feature = "clap", value(name = "km2"))]
SquareKilometers,
#[default]
#[serde(rename = "m2")]
#[display("m2")]
#[cfg_attr(feature = "clap", value(name = "m2"))]
SquareMeters,
#[serde(rename = "mm2")]
#[display("mm2")]
#[cfg_attr(feature = "clap", value(name = "mm2"))]
SquareMillimeters,
#[serde(rename = "yd2")]
#[display("yd2")]
#[cfg_attr(feature = "clap", value(name = "yd2"))]
SquareYards,
}
impl UnitArea {
pub fn as_measurement(self, value: f64) -> measurements::Area {
match self {
Self::SquareCentimeters => measurements::Area::from_square_centimeters(value),
Self::SquareDecimeters => measurements::Area::from_square_decimeters(value),
Self::SquareFeet => measurements::Area::from_square_feet(value),
Self::SquareInches => measurements::Area::from_square_inches(value),
Self::SquareKilometers => measurements::Area::from_square_kilometers(value),
Self::SquareMeters => measurements::Area::from_square_meters(value),
Self::SquareMillimeters => measurements::Area::from_square_millimeters(value),
Self::SquareYards => measurements::Area::from_square_yards(value),
}
}
}
#[derive(
Display,
Copy,
Eq,
PartialEq,
Debug,
JsonSchema,
Deserialize,
Serialize,
Clone,
Ord,
PartialOrd,
Default,
UnitConversion,
Hash,
)]
#[cfg_attr(feature = "clap", derive(ValueEnum))]
#[cfg_attr(feature = "tabled", derive(tabled::Tabled))]
#[display(style = "snake_case")]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
#[cfg_attr(
feature = "python",
pyo3::pyclass(eq, from_py_object),
pyo3_stub_gen::derive::gen_stub_pyclass_enum
)]
pub enum UnitDensity {
#[serde(rename = "lb:ft3")]
#[display("lb:ft3")]
#[cfg_attr(feature = "clap", value(name = "lb:ft3", aliases = ["lbft3", "lb-ft3"]))]
PoundsPerCubicFeet,
#[default]
#[serde(rename = "kg:m3")]
#[display("kg:m3")]
#[cfg_attr(feature = "clap", value(name = "kg:m3", aliases = ["kgm3", "kg-m3"]))]
KilogramsPerCubicMeter,
}
impl std::str::FromStr for UnitDensity {
type Err = parse_display::ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"lbft3" | "lb:ft3" | "lb-ft3" => Ok(Self::PoundsPerCubicFeet),
"kgm3" | "kg:m3" | "kg-m3" => Ok(Self::KilogramsPerCubicMeter),
_other => Err(Default::default()),
}
}
}
impl UnitDensity {
pub fn as_measurement(self, value: f64) -> measurements::Density {
match self {
Self::PoundsPerCubicFeet => measurements::Density::from_pounds_per_cubic_feet(value),
Self::KilogramsPerCubicMeter => measurements::Density::from_kilograms_per_cubic_meter(value),
}
}
}
#[derive(
Default,
Display,
FromStr,
Copy,
Eq,
PartialEq,
Debug,
JsonSchema,
Deserialize,
Serialize,
Clone,
Ord,
PartialOrd,
UnitConversion,
Hash,
)]
#[cfg_attr(feature = "clap", derive(ValueEnum))]
#[cfg_attr(feature = "tabled", derive(tabled::Tabled))]
#[serde(rename_all = "snake_case")]
#[display(style = "snake_case")]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
#[cfg_attr(
feature = "python",
pyo3::pyclass(eq, from_py_object),
pyo3_stub_gen::derive::gen_stub_pyclass_enum
)]
pub enum UnitMass {
#[default]
#[serde(rename = "g")]
#[display("g")]
#[cfg_attr(feature = "clap", value(name = "g"))]
Grams,
#[serde(rename = "kg")]
#[display("kg")]
#[cfg_attr(feature = "clap", value(name = "kg"))]
Kilograms,
#[serde(rename = "lb")]
#[display("lb")]
#[cfg_attr(feature = "clap", value(name = "lb"))]
Pounds,
}
impl UnitMass {
pub fn as_measurement(self, value: f64) -> measurements::Mass {
match self {
Self::Grams => measurements::Mass::from_grams(value),
Self::Kilograms => measurements::Mass::from_kilograms(value),
Self::Pounds => measurements::Mass::from_pounds(value),
}
}
}
#[derive(
Default,
Display,
FromStr,
Copy,
Eq,
PartialEq,
Debug,
JsonSchema,
Deserialize,
Serialize,
Clone,
Ord,
PartialOrd,
UnitConversion,
Hash,
)]
#[cfg_attr(feature = "clap", derive(ValueEnum))]
#[cfg_attr(feature = "tabled", derive(tabled::Tabled))]
#[display(style = "snake_case")]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
#[cfg_attr(
feature = "python",
pyo3::pyclass(eq, from_py_object),
pyo3_stub_gen::derive::gen_stub_pyclass_enum
)]
pub enum UnitVolume {
#[serde(rename = "mm3")]
#[display("mm3")]
#[cfg_attr(feature = "clap", value(name = "mm3"))]
CubicMillimeters,
#[serde(rename = "cm3")]
#[display("cm3")]
#[cfg_attr(feature = "clap", value(name = "cm3"))]
CubicCentimeters,
#[serde(rename = "ft3")]
#[display("ft3")]
#[cfg_attr(feature = "clap", value(name = "ft3"))]
CubicFeet,
#[serde(rename = "in3")]
#[display("in3")]
#[cfg_attr(feature = "clap", value(name = "in3"))]
CubicInches,
#[default]
#[serde(rename = "m3")]
#[display("m3")]
#[cfg_attr(feature = "clap", value(name = "m3"))]
CubicMeters,
#[serde(rename = "yd3")]
#[display("yd3")]
#[cfg_attr(feature = "clap", value(name = "yd3"))]
CubicYards,
#[serde(rename = "usfloz")]
#[display("usfloz")]
#[cfg_attr(feature = "clap", value(name = "usfloz"))]
FluidOunces,
#[serde(rename = "usgal")]
#[display("usgal")]
#[cfg_attr(feature = "clap", value(name = "usgal"))]
Gallons,
#[serde(rename = "l")]
#[display("l")]
#[cfg_attr(feature = "clap", value(name = "l"))]
Liters,
#[serde(rename = "ml")]
#[display("ml")]
#[cfg_attr(feature = "clap", value(name = "ml"))]
Milliliters,
}
impl UnitVolume {
pub fn as_measurement(self, value: f64) -> measurements::Volume {
match self {
Self::CubicMillimeters => measurements::Volume::from_cubic_millimeters(value),
Self::CubicCentimeters => measurements::Volume::from_cubic_centimeters(value),
Self::CubicFeet => measurements::Volume::from_cubic_feet(value),
Self::CubicInches => measurements::Volume::from_cubic_inches(value),
Self::CubicMeters => measurements::Volume::from_cubic_meters(value),
Self::CubicYards => measurements::Volume::from_cubic_yards(value),
Self::FluidOunces => measurements::Volume::from_fluid_ounces(value),
Self::Gallons => measurements::Volume::from_gallons(value),
Self::Liters => measurements::Volume::from_liters(value),
Self::Milliliters => measurements::Volume::from_milliliters(value),
}
}
}
#[cfg(all(test, feature = "clap"))]
mod clap_tests {
use super::*;
fn assert_clap_spellings<T: ValueEnum + std::fmt::Display + std::fmt::Debug + PartialEq + 'static>() {
for unit in T::value_variants() {
let spelling = unit.to_string();
assert_eq!(unit.to_possible_value().unwrap().get_name(), spelling);
assert_eq!(&T::from_str(&spelling, false).unwrap(), unit);
}
}
#[test]
fn clap_unit_spellings_match_display() {
assert_clap_spellings::<UnitLength>();
assert_clap_spellings::<UnitAngle>();
assert_clap_spellings::<UnitArea>();
assert_clap_spellings::<UnitDensity>();
assert_clap_spellings::<UnitMass>();
assert_clap_spellings::<UnitVolume>();
}
#[test]
fn clap_preserves_density_aliases() {
for spelling in ["lbft3", "lb:ft3", "lb-ft3", "kgm3", "kg:m3", "kg-m3"] {
assert_eq!(
<UnitDensity as ValueEnum>::from_str(spelling, false).unwrap(),
<UnitDensity as std::str::FromStr>::from_str(spelling).unwrap()
);
}
}
}