diff --git a/proxmox-api-macro/src/api/method.rs b/proxmox-api-macro/src/api/method.rs index 6d809023..6f239994 100644 --- a/proxmox-api-macro/src/api/method.rs +++ b/proxmox-api-macro/src/api/method.rs @@ -140,11 +140,14 @@ fn handle_function_signature( let (pat_type, pat) = check_input_type(input)?; // For any named type which exists on the function signature... - if let Some((_ident, _optional, ref mut schema)) = + if let Some((_ident, optional, ref mut schema)) = input_schema.find_obj_property_by_ident_mut(&pat.ident.to_string()) { // try to infer the type in the schema if it is not specified explicitly: - util::infer_type(schema, &*pat_type.ty)?; + let is_option = util::infer_type(schema, &*pat_type.ty)?; + if !is_option && *optional { + bail!(pat_type => "non-optional `Option` type"); + } } else { continue; }; diff --git a/proxmox-api-macro/src/util.rs b/proxmox-api-macro/src/util.rs index bcd20c79..748d6986 100644 --- a/proxmox-api-macro/src/util.rs +++ b/proxmox-api-macro/src/util.rs @@ -442,13 +442,18 @@ pub fn derive_descriptions( Ok(()) } -pub fn infer_type(schema: &mut Schema, ty: &syn::Type) -> Result<(), Error> { +pub fn infer_type(schema: &mut Schema, ty: &syn::Type) -> Result { if let SchemaItem::Inferred(_) = schema.item { // } else { - return Ok(()); + return Ok(is_option_type(ty).is_some()); } + let (ty, is_option) = match is_option_type(ty) { + Some(ty) => (ty, true), + None => (ty, false), + }; + // infer the type from a rust type: match ty { syn::Type::Path(path) if path.qself.is_none() => { @@ -467,5 +472,32 @@ pub fn infer_type(schema: &mut Schema, ty: &syn::Type) -> Result<(), Error> { _ => (), } - Ok(()) + Ok(is_option) +} + +/// Note that we cannot handle renamed imports at all here... +fn is_option_type(ty: &syn::Type) -> Option<&syn::Type> { + if let syn::Type::Path(p) = ty { + if p.qself.is_some() { + return None; + } + let segs = &p.path.segments; + let is_option = match segs.len() { + 1 => segs.last().unwrap().ident == "Option", + 2 => segs.first().unwrap().ident == "std" && segs.last().unwrap().ident == "Option", + _ => false, + }; + if !is_option { + return None; + } + + if let syn::PathArguments::AngleBracketed(generic) = &segs.last().unwrap().arguments { + if generic.args.len() == 1 { + if let syn::GenericArgument::Type(ty) = generic.args.first().unwrap() { + return Some(ty); + } + } + } + } + None } diff --git a/proxmox-api-macro/tests/types.rs b/proxmox-api-macro/tests/types.rs index 0c3c6481..a491a49a 100644 --- a/proxmox-api-macro/tests/types.rs +++ b/proxmox-api-macro/tests/types.rs @@ -23,6 +23,9 @@ pub struct OkString(String); pub struct Foo { /// A test string. test: String, + + /// An optional auto-derived value for testing: + another: Option, } // generates the following without the '_' prefix in the constant: