diff --git a/proxmox-api-macro/src/api/enums.rs b/proxmox-api-macro/src/api/enums.rs index 595760ea..af5c44bc 100644 --- a/proxmox-api-macro/src/api/enums.rs +++ b/proxmox-api-macro/src/api/enums.rs @@ -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(); } diff --git a/proxmox-api-macro/src/api/structs.rs b/proxmox-api-macro/src/api/structs.rs index 6bb8f91b..34560480 100644 --- a/proxmox-api-macro/src/api/structs.rs +++ b/proxmox-api-macro/src/api/structs.rs @@ -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 }, diff --git a/proxmox-api-macro/src/util.rs b/proxmox-api-macro/src/util.rs index ddd2084d..d58a6838 100644 --- a/proxmox-api-macro/src/util.rs +++ b/proxmox-api-macro/src/util.rs @@ -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< as IntoIterator>::IntoIter>, diff --git a/proxmox-api-macro/tests/types.rs b/proxmox-api-macro/tests/types.rs index efba8391..b4b7250e 100644 --- a/proxmox-api-macro/tests/types.rs +++ b/proxmox-api-macro/tests/types.rs @@ -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);