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");
}
let has_default_attrib = attribs.get("default").map(|def| def.span());
let schema = {
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 derives_default = util::derives_trait(&enum_ty.attrs, "Default");
let mut default_value = None;
let mut variants = TokenStream::new();
for variant in &mut enum_ty.variants {
@ -64,6 +68,21 @@ pub fn handle_enum(
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() =>
::proxmox_schema::EnumEntry {
value: #variant_string,
@ -74,6 +93,11 @@ pub fn handle_enum(
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() =>
#enum_ty
@ -81,6 +105,7 @@ pub fn handle_enum(
const API_SCHEMA: ::proxmox_schema::Schema =
#schema
.format(&::proxmox_schema::ApiStringFormat::Enum(&[#variants]))
#default_value
.schema();
}

View File

@ -404,7 +404,7 @@ fn derive_updater(
let original_name = &original_struct.ident;
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(
Span::call_site(),
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.
pub struct DerivedItems<'a> {
current: Option<<Punctuated<syn::NestedMeta, Token![,]> as IntoIterator>::IntoIter>,

View File

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