Skip to content

Commit

Permalink
editoast: derive: add macro to add utoipa schema for units
Browse files Browse the repository at this point in the history
Signed-off-by: Tristram Gräbener <[email protected]>
  • Loading branch information
Tristramg committed Dec 9, 2024
1 parent d003aaa commit d5e9940
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 0 deletions.
49 changes: 49 additions & 0 deletions editoast/editoast_derive/src/annotate_units.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use darling::Result;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse_quote, DeriveInput, LitStr};

pub fn get_abbreviation(value: String) -> Option<&'static str> {
match value.replace("::option", "").as_str() {
"meter" => Some("Length in m"),
"millimeter" => Some("Length in mm"),
"meter_per_second" => Some("Velocity in m·s⁻¹"),
"kilometer_per_hour" => Some("Velocity in km·h⁻¹"),
"meter_per_second_squared" => Some("Acceleration in m·s⁻²"),
_ => None,
}
}

pub fn annotate_units(input: &mut DeriveInput) -> Result<TokenStream> {
// We look for fields that have #[sered(with="meter")] attributes
// and we push two new attributes to it to improve the OpenAPI
match &mut input.data {
syn::Data::Struct(s) => {
for f in s.fields.iter_mut() {
for attr in f.attrs.clone() {
if attr.path().is_ident("serde") {
let _ = attr.parse_nested_meta(|meta| {
if meta.path.is_ident("with") {
let value = meta.value()?;
let s: LitStr = value.parse()?;
if let Some(abbreviation) = get_abbreviation(s.value()) {
if s.value().ends_with("::option") {
f.attrs.push(
parse_quote! {#[schema(value_type = Option<f64>)]},
);
} else {
f.attrs.push(parse_quote! {#[schema(value_type = f64)]});
}
f.attrs.push(parse_quote! {#[doc = #abbreviation]});
}
}
Ok(())
});
}
}
}
}
_ => {}
}
Ok(quote! {#input})
}
19 changes: 19 additions & 0 deletions editoast/editoast_derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
extern crate proc_macro;

mod annotate_units;
mod error;
mod model;
mod search;
Expand Down Expand Up @@ -238,3 +239,21 @@ pub fn model(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
.unwrap_or_else(darling::Error::write_errors)
.into()
}

/// Annotates fields of a structs with documentation and value_type for a better utoipa schema
///
/// It must be used on structs that use #[derive(ToSchema)]
///
/// On every field that has an attribute such as #[serde(with="millimeter")]
/// It will add:
/// * #[schema(value_type = f64)]
/// * /// Length in mm
#[proc_macro_attribute]
pub fn annotate_units(_attr: TokenStream, input: TokenStream) -> TokenStream {
// We are using a macro attribute to modify in place the attributes of fields to annotate
// This requires to mutate the input
let mut input = parse_macro_input!(input as DeriveInput);
annotate_units::annotate_units(&mut input)
.unwrap_or_else(darling::Error::write_errors)
.into()
}

0 comments on commit d5e9940

Please sign in to comment.