diff --git a/editoast/editoast_derive/src/lib.rs b/editoast/editoast_derive/src/lib.rs index b03d3ab6da4..9e2dcb4926a 100644 --- a/editoast/editoast_derive/src/lib.rs +++ b/editoast/editoast_derive/src/lib.rs @@ -217,6 +217,7 @@ pub fn search_config_store(input: proc_macro::TokenStream) -> proc_macro::TokenS /// * `#[model(to_string)]`: calls `to_string()` before writing the field to the database and calls `String::from` after reading (diesel column type: String) /// * `#[model(to_enum)]`: is converted as `u8` before writing the field to the database and calls `FromRepr::from_repr` after reading (diesel column type: TinyInt) /// * `#[model(remote = "T")]`: calls `Into::::into` before writing the field to the database and calls `T::from` after reading (diesel column type: T) +/// * `#[model(uom_unit = "T")]`: the field is an uom quantity and stored in the database in the unit `T`, e.g. `"uom::si::length::meter"` /// * `#[model(geo)]` **TODO**: TBD /// /// #### A note on identifiers diff --git a/editoast/editoast_derive/src/model/args.rs b/editoast/editoast_derive/src/model/args.rs index 1d9479b5146..92f8f66a28f 100644 --- a/editoast/editoast_derive/src/model/args.rs +++ b/editoast/editoast_derive/src/model/args.rs @@ -79,6 +79,8 @@ pub(super) struct ModelFieldArgs { pub(super) to_enum: bool, #[darling(default)] pub(super) remote: Option, + #[darling(default)] + pub(super) uom_unit: Option, } impl GeneratedTypeArgs { diff --git a/editoast/editoast_derive/src/model/config.rs b/editoast/editoast_derive/src/model/config.rs index 1cfa0f52719..9060f016fd5 100644 --- a/editoast/editoast_derive/src/model/config.rs +++ b/editoast/editoast_derive/src/model/config.rs @@ -45,6 +45,7 @@ pub(crate) enum FieldTransformation { Geo, ToString, ToEnum(syn::Type), + UomUnit(syn::Type), } #[derive(Debug, PartialEq)] @@ -132,12 +133,14 @@ impl ModelField { Some(FieldTransformation::ToEnum(_)) => { parse_quote! { #expr as i16 } } + Some(FieldTransformation::UomUnit(ref unit)) => parse_quote! { #expr.get::<#unit>() }, None => parse_quote! { #expr }, } } #[allow(clippy::wrong_self_convention)] pub(crate) fn from_transformed(&self, expr: syn::Expr) -> syn::Expr { + let ty = &self.ty; match self.transform { Some(FieldTransformation::Remote(_)) => parse_quote! { #expr.into() }, Some(FieldTransformation::Json) => parse_quote! { #expr.0 }, @@ -146,6 +149,9 @@ impl ModelField { Some(FieldTransformation::ToEnum(ref ty)) => { parse_quote! { #ty::from_repr(#expr as usize).expect("Invalid variant repr") } } + Some(FieldTransformation::UomUnit(ref unit)) => { + parse_quote! { #ty::new::<#unit>(#expr) } + } None => parse_quote! { #expr }, } } @@ -158,6 +164,7 @@ impl ModelField { Some(FieldTransformation::Geo) => unimplemented!("to be designed"), Some(FieldTransformation::ToString) => parse_quote! { String }, Some(FieldTransformation::ToEnum(_)) => parse_quote! { i16 }, + Some(FieldTransformation::UomUnit(_)) => parse_quote! { f64 }, None => ty.clone(), } } diff --git a/editoast/editoast_derive/src/model/parsing.rs b/editoast/editoast_derive/src/model/parsing.rs index 368480fcaea..28f2f91f443 100644 --- a/editoast/editoast_derive/src/model/parsing.rs +++ b/editoast/editoast_derive/src/model/parsing.rs @@ -165,6 +165,7 @@ impl ModelField { value.geo, value.to_string, to_enum, + value.uom_unit, ) .map_err(|e| e.with_span(&ident))?; Ok(Self { @@ -188,16 +189,19 @@ impl FieldTransformation { geo: bool, to_string: bool, to_enum: Option, + uom_unit: Option, ) -> darling::Result> { - match (remote, json, geo, to_string, to_enum) { - (Some(ty), false, false, false, None) => Ok(Some(Self::Remote(ty))), - (None, true, false, false, None) => Ok(Some(Self::Json)), - (None, false, true, false, None) => Ok(Some(Self::Geo)), - (None, false, false, true, None) => Ok(Some(Self::ToString)), - (None, false, false, false, Some(ty)) => Ok(Some(Self::ToEnum(ty))), - (None, false, false, false, None) => Ok(None), + match (remote, json, geo, to_string, to_enum, uom_unit) { + (Some(ty), false, false, false, None, None) => Ok(Some(Self::Remote(ty))), + (None, true, false, false, None, None) => Ok(Some(Self::Json)), + (None, false, true, false, None, None) => Ok(Some(Self::Geo)), + (None, false, false, true, None, None) => Ok(Some(Self::ToString)), + (None, false, false, false, Some(ty), None) => Ok(Some(Self::ToEnum(ty))), + //(None, false, false, false, None, Some(ty)) => Ok(Some(Self::Unit(ty))), + (None, false, false, false, None, Some(ty)) => Ok(Some(Self::UomUnit(ty))), + (None, false, false, false, None, None) => Ok(None), _ => Err(Error::custom( - "Model: remote, json, geo, to_string and to_enum attributes are mutually exclusive", + "Model: remote, json, geo, to_string, uom_unit and to_enum attributes are mutually exclusive", )), } }