use std::marker::PhantomData;
use super::{
FromSchema, FromSchemaError, IntoSchema, SchemaBuilder, type_id_with_args, value_kind,
};
use crate::schema::metadata::TypeId;
use crate::schema::schema_type::{QuantitySpec, QuantityValue, SchemaType};
use crate::schema::schema_value::SchemaValue;
pub trait QuantityUnit: 'static {
fn type_id() -> TypeId;
fn base_unit() -> &'static str;
fn allowed_suffixes() -> &'static [&'static str] {
&[]
}
}
pub struct Quantity<U: QuantityUnit> {
value: QuantityValue,
_unit: PhantomData<U>,
}
impl<U: QuantityUnit> Clone for Quantity<U> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
_unit: PhantomData,
}
}
}
impl<U: QuantityUnit> std::fmt::Debug for Quantity<U> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Quantity")
.field("value", &self.value)
.finish()
}
}
impl<U: QuantityUnit> PartialEq for Quantity<U> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<U: QuantityUnit> Eq for Quantity<U> {}
impl<U: QuantityUnit> Quantity<U> {
pub fn new(
mantissa: i64,
scale: i32,
unit: impl Into<String>,
) -> Result<Self, FromSchemaError> {
Self::from_quantity_value(QuantityValue {
mantissa,
scale,
unit: unit.into(),
})
}
pub fn from_quantity_value(value: QuantityValue) -> Result<Self, FromSchemaError> {
if !Self::unit_allowed(&value.unit) {
return Err(FromSchemaError::custom(format!(
"quantity unit `{}` is not allowed for `{}`",
value.unit,
U::type_id().as_str()
)));
}
Ok(Self {
value,
_unit: PhantomData,
})
}
pub fn as_quantity_value(&self) -> &QuantityValue {
&self.value
}
pub fn into_quantity_value(self) -> QuantityValue {
self.value
}
fn unit_allowed(unit: &str) -> bool {
let allowed = U::allowed_suffixes();
if allowed.is_empty() {
unit == U::base_unit()
} else {
allowed.contains(&unit)
}
}
fn spec() -> QuantitySpec {
QuantitySpec {
base_unit: U::base_unit().to_string(),
allowed_suffixes: U::allowed_suffixes()
.iter()
.map(|s| (*s).to_string())
.collect(),
min: None,
max: None,
}
}
}
impl<U: QuantityUnit> IntoSchema for Quantity<U> {
fn type_id() -> TypeId {
type_id_with_args("golem.schema.Quantity", &[U::type_id()])
}
fn register_in(_b: &mut SchemaBuilder) -> SchemaType {
SchemaType::quantity(Self::spec())
}
fn to_value(&self) -> SchemaValue {
SchemaValue::Quantity(self.value.clone())
}
}
impl<U: QuantityUnit> FromSchema for Quantity<U> {
fn from_value(v: &SchemaValue) -> Result<Self, FromSchemaError> {
match v {
SchemaValue::Quantity(q) => Self::from_quantity_value(q.clone()),
other => Err(FromSchemaError::shape_mismatch(
"quantity",
value_kind(other),
"Quantity",
)),
}
}
}