api: support #[default] attribute

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2022-12-12 11:30:46 +01:00
parent 0719e1db1c
commit 38a60d3acb
4 changed files with 34 additions and 2 deletions

View File

@ -25,6 +25,8 @@ pub fn handle_enum(
error!(fmt.span(), "illegal key 'format', will be autogenerated"); error!(fmt.span(), "illegal key 'format', will be autogenerated");
} }
let has_default_attrib = attribs.get("default").map(|def| def.span());
let schema = { let schema = {
let mut schema: Schema = attribs.try_into()?; let mut schema: Schema = attribs.try_into()?;
@ -39,6 +41,8 @@ pub fn handle_enum(
}; };
let container_attrs = serde::ContainerAttrib::try_from(&enum_ty.attrs[..])?; let container_attrs = serde::ContainerAttrib::try_from(&enum_ty.attrs[..])?;
let derives_default = util::derives_trait(&enum_ty.attrs, "Default");
let mut default_value = None;
let mut variants = TokenStream::new(); let mut variants = TokenStream::new();
for variant in &mut enum_ty.variants { for variant in &mut enum_ty.variants {
@ -64,6 +68,21 @@ pub fn handle_enum(
syn::LitStr::new(&name.to_string(), name.span()) syn::LitStr::new(&name.to_string(), name.span())
}; };
if derives_default {
if let Some(attr) = variant.attrs.iter().find(|a| a.path.is_ident("default")) {
if let Some(default_value) = &default_value {
error!(attr => "multiple default values defined");
error!(default_value => "default previously defined here");
} else {
default_value = Some(variant_string.clone());
if let Some(span) = has_default_attrib {
error!(attr => "#[default] attribute in use with 'default' #[api] key");
error!(span, "'default' also defined here");
}
}
}
}
variants.extend(quote_spanned! { variant.ident.span() => variants.extend(quote_spanned! { variant.ident.span() =>
::proxmox_schema::EnumEntry { ::proxmox_schema::EnumEntry {
value: #variant_string, value: #variant_string,
@ -74,6 +93,11 @@ pub fn handle_enum(
let name = &enum_ty.ident; let name = &enum_ty.ident;
let default_value = match default_value {
Some(value) => quote_spanned!(value.span() => .default(#value)),
None => TokenStream::new(),
};
Ok(quote_spanned! { name.span() => Ok(quote_spanned! { name.span() =>
#enum_ty #enum_ty
@ -81,6 +105,7 @@ pub fn handle_enum(
const API_SCHEMA: ::proxmox_schema::Schema = const API_SCHEMA: ::proxmox_schema::Schema =
#schema #schema
.format(&::proxmox_schema::ApiStringFormat::Enum(&[#variants])) .format(&::proxmox_schema::ApiStringFormat::Enum(&[#variants]))
#default_value
.schema(); .schema();
} }

View File

@ -404,7 +404,7 @@ fn derive_updater(
let original_name = &original_struct.ident; let original_name = &original_struct.ident;
stru.ident = Ident::new(&format!("{}Updater", stru.ident), stru.ident.span()); stru.ident = Ident::new(&format!("{}Updater", stru.ident), stru.ident.span());
if !util::derived_items(&original_struct.attrs).any(|p| p.is_ident("Default")) { if !util::derives_trait(&original_struct.attrs, "Default") {
stru.attrs.push(util::make_derive_attribute( stru.attrs.push(util::make_derive_attribute(
Span::call_site(), Span::call_site(),
quote::quote! { Default }, quote::quote! { Default },

View File

@ -699,6 +699,11 @@ pub fn derived_items(attributes: &[syn::Attribute]) -> DerivedItems {
} }
} }
/// Helper to check if a certain trait is being derived.
pub fn derives_trait(attributes: &[syn::Attribute], ident: &str) -> bool {
derived_items(&attributes).any(|p| p.is_ident(ident))
}
/// Iterator over the types found in `#[derive(...)]` attributes. /// Iterator over the types found in `#[derive(...)]` attributes.
pub struct DerivedItems<'a> { pub struct DerivedItems<'a> {
current: Option<<Punctuated<syn::NestedMeta, Token![,]> as IntoIterator>::IntoIter>, current: Option<<Punctuated<syn::NestedMeta, Token![,]> as IntoIterator>::IntoIter>,

View File

@ -103,7 +103,7 @@ fn renamed_struct() {
} }
#[api] #[api]
#[derive(Deserialize)] #[derive(Default, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// A selection of either 'onekind', 'another-kind' or 'selection-number-three'. /// A selection of either 'onekind', 'another-kind' or 'selection-number-three'.
pub enum Selection { pub enum Selection {
@ -111,6 +111,7 @@ pub enum Selection {
#[serde(rename = "onekind")] #[serde(rename = "onekind")]
OneKind, OneKind,
/// Some other kind. /// Some other kind.
#[default]
AnotherKind, AnotherKind,
/// And yet another. /// And yet another.
SelectionNumberThree, SelectionNumberThree,
@ -126,6 +127,7 @@ fn selection_test() {
EnumEntry::new("another-kind", "Some other kind."), EnumEntry::new("another-kind", "Some other kind."),
EnumEntry::new("selection-number-three", "And yet another."), EnumEntry::new("selection-number-three", "And yet another."),
])) ]))
.default("another-kind")
.schema(); .schema();
assert_eq!(TEST_SCHEMA, Selection::API_SCHEMA); assert_eq!(TEST_SCHEMA, Selection::API_SCHEMA);