#[cfg(feature = "diesel")]
use std::str::FromStr;
#[cfg(feature = "diesel")]
use diesel::{mysql::Mysql, serialize::ToSql, sql_types::Text};
#[cfg(feature = "diesel")]
use diesel_derives::{AsExpression, FromSqlRow};
use kittycad_execution_plan_macros::ExecutionPlanValue;
use kittycad_unit_conversion_derive::UnitConversion;
use parse_display_derive::{Display, FromStr};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
macro_rules! impl_string_enum_sql {
    {$name:ident} => {
        #[cfg(feature = "diesel")]
        impl diesel::serialize::ToSql<Text, Mysql> for $name {
            fn to_sql<'a>(&'a self, out: &mut diesel::serialize::Output<'a, '_, Mysql>) -> diesel::serialize::Result {
                <String as ToSql<Text, Mysql>>::to_sql(&self.to_string(), &mut out.reborrow())
            }
        }
        #[cfg(feature = "diesel")]
        impl<DB> diesel::deserialize::FromSql<Text, DB> for $name
        where
            DB: diesel::backend::Backend,
            String: diesel::deserialize::FromSql<Text, DB>,
        {
            fn from_sql(bytes: <DB as diesel::backend::Backend>::RawValue<'_>) -> diesel::deserialize::Result<Self> {
                Ok(Self::from_str(&String::from_sql(bytes)?)?)
            }
        }
    };
}
#[derive(
    Default,
    Display,
    FromStr,
    Copy,
    Eq,
    PartialEq,
    Debug,
    JsonSchema,
    Deserialize,
    Serialize,
    Clone,
    Ord,
    PartialOrd,
    UnitConversion,
    Hash,
    ExecutionPlanValue,
)]
#[display(style = "snake_case")]
pub enum UnitLength {
    #[serde(rename = "cm")]
    #[display("cm")]
    Centimeters,
    #[serde(rename = "ft")]
    #[display("ft")]
    Feet,
    #[serde(rename = "in")]
    #[display("in")]
    Inches,
    #[default]
    #[serde(rename = "m")]
    #[display("m")]
    Meters,
    #[serde(rename = "mm")]
    #[display("mm")]
    Millimeters,
    #[serde(rename = "yd")]
    #[display("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),
        }
    }
}
#[derive(
    Display,
    FromStr,
    Copy,
    Eq,
    PartialEq,
    Debug,
    JsonSchema,
    Deserialize,
    Serialize,
    Clone,
    Ord,
    PartialOrd,
    UnitConversion,
)]
#[cfg_attr(feature = "diesel", derive(AsExpression, FromSqlRow))]
#[cfg_attr(feature = "diesel", diesel(sql_type = Text))]
#[serde(rename_all = "snake_case")]
#[display(style = "snake_case")]
pub enum UnitAngle {
    Degrees,
    Radians,
}
impl_string_enum_sql!(UnitAngle);
#[derive(
    Display,
    FromStr,
    Copy,
    Eq,
    PartialEq,
    Debug,
    JsonSchema,
    Deserialize,
    Serialize,
    Clone,
    Ord,
    PartialOrd,
    UnitConversion,
    Default,
    Hash,
    ExecutionPlanValue,
)]
#[serde(rename_all = "snake_case")]
#[display(style = "snake_case")]
pub enum UnitArea {
    #[serde(rename = "cm2")]
    #[display("cm2")]
    SquareCentimeters,
    #[serde(rename = "dm2")]
    #[display("dm2")]
    SquareDecimeters,
    #[serde(rename = "ft2")]
    #[display("ft2")]
    SquareFeet,
    #[serde(rename = "in2")]
    #[display("in2")]
    SquareInches,
    #[serde(rename = "km2")]
    #[display("km2")]
    SquareKilometers,
    #[default]
    #[serde(rename = "m2")]
    #[display("m2")]
    SquareMeters,
    #[serde(rename = "mm2")]
    #[display("mm2")]
    SquareMillimeters,
    #[serde(rename = "yd2")]
    #[display("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,
    FromStr,
    Copy,
    Eq,
    PartialEq,
    Debug,
    JsonSchema,
    Deserialize,
    Serialize,
    Clone,
    Ord,
    PartialOrd,
    Default,
    UnitConversion,
    Hash,
    ExecutionPlanValue,
)]
#[display(style = "snake_case")]
pub enum UnitDensity {
    #[serde(rename = "lb:ft3")]
    #[display("lb:ft3")]
    PoundsPerCubicFeet,
    #[default]
    #[serde(rename = "kg:m3")]
    #[display("kg:m3")]
    KilogramsPerCubicMeter,
}
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,
    ExecutionPlanValue,
)]
#[serde(rename_all = "snake_case")]
#[display(style = "snake_case")]
pub enum UnitMass {
    #[default]
    #[serde(rename = "g")]
    #[display("g")]
    Grams,
    #[serde(rename = "kg")]
    #[display("kg")]
    Kilograms,
    #[serde(rename = "lb")]
    #[display("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,
    ExecutionPlanValue,
)]
#[display(style = "snake_case")]
pub enum UnitVolume {
    #[serde(rename = "cm3")]
    #[display("cm3")]
    CubicCentimeters,
    #[serde(rename = "ft3")]
    #[display("ft3")]
    CubicFeet,
    #[serde(rename = "in3")]
    #[display("in3")]
    CubicInches,
    #[default]
    #[serde(rename = "m3")]
    #[display("m3")]
    CubicMeters,
    #[serde(rename = "yd3")]
    #[display("yd3")]
    CubicYards,
    #[serde(rename = "usfloz")]
    #[display("usfloz")]
    FluidOunces,
    #[serde(rename = "usgal")]
    #[display("usgal")]
    Gallons,
    #[serde(rename = "l")]
    #[display("l")]
    Liters,
    #[serde(rename = "ml")]
    #[display("ml")]
    Milliliters,
}
impl UnitVolume {
    pub fn as_measurement(self, value: f64) -> measurements::Volume {
        match self {
            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),
        }
    }
}