api-macro: more option type handling

infer_type now also returns whether it was encapsualted in
an Option<>. So `type: String, optional: true` is now
inferred propertly from `Option<String>`.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2020-01-07 13:37:33 +01:00
parent 45af06f090
commit 3626f57d2c
3 changed files with 43 additions and 5 deletions

View File

@ -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;
};

View File

@ -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<bool, Error> {
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
}

View File

@ -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<String>,
}
// generates the following without the '_' prefix in the constant: