golem_schema/schema/conversion/
quantity.rs1use std::marker::PhantomData;
23
24use super::{
25 FromSchema, FromSchemaError, IntoSchema, SchemaBuilder, type_id_with_args, value_kind,
26};
27use crate::schema::metadata::TypeId;
28use crate::schema::schema_type::{QuantitySpec, QuantityValue, SchemaType};
29use crate::schema::schema_value::SchemaValue;
30
31pub trait QuantityUnit: 'static {
33 fn type_id() -> TypeId;
35
36 fn base_unit() -> &'static str;
38
39 fn allowed_suffixes() -> &'static [&'static str] {
45 &[]
46 }
47}
48
49pub struct Quantity<U: QuantityUnit> {
55 value: QuantityValue,
56 _unit: PhantomData<U>,
57}
58
59impl<U: QuantityUnit> Clone for Quantity<U> {
60 fn clone(&self) -> Self {
61 Self {
62 value: self.value.clone(),
63 _unit: PhantomData,
64 }
65 }
66}
67
68impl<U: QuantityUnit> std::fmt::Debug for Quantity<U> {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 f.debug_struct("Quantity")
71 .field("value", &self.value)
72 .finish()
73 }
74}
75
76impl<U: QuantityUnit> PartialEq for Quantity<U> {
77 fn eq(&self, other: &Self) -> bool {
78 self.value == other.value
79 }
80}
81
82impl<U: QuantityUnit> Eq for Quantity<U> {}
83
84impl<U: QuantityUnit> Quantity<U> {
85 pub fn new(
87 mantissa: i64,
88 scale: i32,
89 unit: impl Into<String>,
90 ) -> Result<Self, FromSchemaError> {
91 Self::from_quantity_value(QuantityValue {
92 mantissa,
93 scale,
94 unit: unit.into(),
95 })
96 }
97
98 pub fn from_quantity_value(value: QuantityValue) -> Result<Self, FromSchemaError> {
100 if !Self::unit_allowed(&value.unit) {
101 return Err(FromSchemaError::custom(format!(
102 "quantity unit `{}` is not allowed for `{}`",
103 value.unit,
104 U::type_id().as_str()
105 )));
106 }
107 Ok(Self {
108 value,
109 _unit: PhantomData,
110 })
111 }
112
113 pub fn as_quantity_value(&self) -> &QuantityValue {
114 &self.value
115 }
116
117 pub fn into_quantity_value(self) -> QuantityValue {
118 self.value
119 }
120
121 fn unit_allowed(unit: &str) -> bool {
122 let allowed = U::allowed_suffixes();
123 if allowed.is_empty() {
124 unit == U::base_unit()
125 } else {
126 allowed.contains(&unit)
127 }
128 }
129
130 fn spec() -> QuantitySpec {
131 QuantitySpec {
132 base_unit: U::base_unit().to_string(),
133 allowed_suffixes: U::allowed_suffixes()
134 .iter()
135 .map(|s| (*s).to_string())
136 .collect(),
137 min: None,
138 max: None,
139 }
140 }
141}
142
143impl<U: QuantityUnit> IntoSchema for Quantity<U> {
144 fn type_id() -> TypeId {
145 type_id_with_args("golem.schema.Quantity", &[U::type_id()])
146 }
147 fn register_in(_b: &mut SchemaBuilder) -> SchemaType {
148 SchemaType::quantity(Self::spec())
149 }
150 fn to_value(&self) -> SchemaValue {
151 SchemaValue::Quantity(self.value.clone())
152 }
153}
154
155impl<U: QuantityUnit> FromSchema for Quantity<U> {
156 fn from_value(v: &SchemaValue) -> Result<Self, FromSchemaError> {
157 match v {
158 SchemaValue::Quantity(q) => Self::from_quantity_value(q.clone()),
159 other => Err(FromSchemaError::shape_mismatch(
160 "quantity",
161 value_kind(other),
162 "Quantity",
163 )),
164 }
165 }
166}