Files
confique/macro/src/util.rs
Lukas Kalbertodt ebd8aba2fa Disallow Option<_> type for fields with #[nested] or #[default]
Regarding nested fields: I cannot imagine a situation where that
distinction is useful. Also, specifying an empty nested object looks
stupid in TOML and YAML anyway.

Regarding default fields: If there is a default value, then the field
should not be declared as optional to begin with.
2021-07-25 10:46:06 +02:00

47 lines
1.3 KiB
Rust

/// Checks if the given type is an `Option` and if so, return the inner type.
///
/// Note: this function clearly shows one of the major shortcomings of proc
/// macros right now: we do not have access to the compiler's type tables and
/// can only check if it "looks" like an `Option`. Of course, stuff can go
/// wrong. But that's the best we can do and it's highly unlikely that someone
/// shadows `Option`.
pub(crate) fn unwrap_option(ty: &syn::Type) -> Option<&syn::Type> {
let ty = match ty {
syn::Type::Path(path) => path,
_ => return None,
};
if ty.qself.is_some() || ty.path.leading_colon.is_some() {
return None;
}
let valid_paths = [
&["Option"] as &[_],
&["std", "option", "Option"],
&["core", "option", "Option"],
];
if !valid_paths.iter().any(|vp| ty.path.segments.iter().map(|s| &s.ident).eq(*vp)) {
return None;
}
let args = match &ty.path.segments.last().unwrap().arguments {
syn::PathArguments::AngleBracketed(args) => args,
_ => return None,
};
if args.args.len() != 1 {
return None;
}
match &args.args[0] {
syn::GenericArgument::Type(t) => Some(t),
_ => None,
}
}
/// Returns `true` if the given type is `Option<_>`.
pub(crate) fn is_option(ty: &syn::Type) -> bool {
unwrap_option(ty).is_some()
}