diff options
Diffstat (limited to 'third_party/rust/uniffi_macros/src/error.rs')
-rw-r--r-- | third_party/rust/uniffi_macros/src/error.rs | 101 |
1 files changed, 73 insertions, 28 deletions
diff --git a/third_party/rust/uniffi_macros/src/error.rs b/third_party/rust/uniffi_macros/src/error.rs index a2ee7cf603..804b438003 100644 --- a/third_party/rust/uniffi_macros/src/error.rs +++ b/third_party/rust/uniffi_macros/src/error.rs @@ -6,11 +6,11 @@ use syn::{ }; use crate::{ - enum_::{rich_error_ffi_converter_impl, variant_metadata}, + enum_::{rich_error_ffi_converter_impl, variant_metadata, EnumAttr}, util::{ - chain, create_metadata_items, derive_ffi_traits, either_attribute_arg, ident_to_string, kw, - mod_path, parse_comma_separated, tagged_impl_header, try_metadata_value_from_usize, - AttributeSliceExt, UniffiAttributeArgs, + chain, create_metadata_items, derive_ffi_traits, either_attribute_arg, extract_docstring, + ident_to_string, kw, mod_path, parse_comma_separated, tagged_impl_header, + try_metadata_value_from_usize, AttributeSliceExt, UniffiAttributeArgs, }, }; @@ -30,13 +30,14 @@ pub fn expand_error( } }; let ident = &input.ident; + let docstring = extract_docstring(&input.attrs)?; let mut attr: ErrorAttr = input.attrs.parse_uniffi_attr_args()?; if let Some(attr_from_udl_mode) = attr_from_udl_mode { attr = attr.merge(attr_from_udl_mode)?; } - let ffi_converter_impl = error_ffi_converter_impl(ident, &enum_, &attr, udl_mode); + let ffi_converter_impl = error_ffi_converter_impl(ident, &enum_, &attr, udl_mode)?; let meta_static_var = (!udl_mode).then(|| { - error_meta_static_var(ident, &enum_, attr.flat.is_some()) + error_meta_static_var(ident, docstring, &enum_, &attr) .unwrap_or_else(syn::Error::into_compile_error) }); @@ -67,23 +68,23 @@ fn error_ffi_converter_impl( enum_: &DataEnum, attr: &ErrorAttr, udl_mode: bool, -) -> TokenStream { - if attr.flat.is_some() { - flat_error_ffi_converter_impl(ident, enum_, udl_mode, attr.with_try_read.is_some()) +) -> syn::Result<TokenStream> { + Ok(if attr.flat.is_some() { + flat_error_ffi_converter_impl(ident, enum_, udl_mode, attr) } else { - rich_error_ffi_converter_impl(ident, enum_, udl_mode) - } + rich_error_ffi_converter_impl(ident, enum_, udl_mode, &attr.clone().try_into()?) + }) } // FfiConverters for "flat errors" // -// These are errors where we only lower the to_string() value, rather than any assocated data. +// These are errors where we only lower the to_string() value, rather than any associated data. // We lower the to_string() value unconditionally, whether the enum has associated data or not. fn flat_error_ffi_converter_impl( ident: &Ident, enum_: &DataEnum, udl_mode: bool, - implement_lift: bool, + attr: &ErrorAttr, ) -> TokenStream { let name = ident_to_string(ident); let lower_impl_spec = tagged_impl_header("Lower", ident, udl_mode); @@ -95,7 +96,7 @@ fn flat_error_ffi_converter_impl( }; let lower_impl = { - let match_arms = enum_.variants.iter().enumerate().map(|(i, v)| { + let mut match_arms: Vec<_> = enum_.variants.iter().enumerate().map(|(i, v)| { let v_ident = &v.ident; let idx = Index::from(i + 1); @@ -105,7 +106,12 @@ fn flat_error_ffi_converter_impl( <::std::string::String as ::uniffi::Lower<crate::UniFfiTag>>::write(error_msg, buf); } } - }); + }).collect(); + if attr.non_exhaustive.is_some() { + match_arms.push(quote! { + _ => panic!("Unexpected variant in non-exhaustive enum"), + }) + } quote! { #[automatically_derived] @@ -128,7 +134,7 @@ fn flat_error_ffi_converter_impl( } }; - let lift_impl = if implement_lift { + let lift_impl = if attr.with_try_read.is_some() { let match_arms = enum_.variants.iter().enumerate().map(|(i, v)| { let v_ident = &v.ident; let idx = Index::from(i + 1); @@ -192,42 +198,53 @@ fn flat_error_ffi_converter_impl( pub(crate) fn error_meta_static_var( ident: &Ident, + docstring: String, enum_: &DataEnum, - flat: bool, + attr: &ErrorAttr, ) -> syn::Result<TokenStream> { let name = ident_to_string(ident); let module_path = mod_path()?; + let flat = attr.flat.is_some(); + let non_exhaustive = attr.non_exhaustive.is_some(); let mut metadata_expr = quote! { - ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::ERROR) - // first our is-flat flag - .concat_bool(#flat) - // followed by an enum + ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::ENUM) .concat_str(#module_path) .concat_str(#name) + .concat_option_bool(Some(#flat)) + .concat_bool(false) // discr_type: None }; if flat { metadata_expr.extend(flat_error_variant_metadata(enum_)?) } else { metadata_expr.extend(variant_metadata(enum_)?); } + metadata_expr.extend(quote! { + .concat_bool(#non_exhaustive) + .concat_long_str(#docstring) + }); Ok(create_metadata_items("error", &name, metadata_expr, None)) } pub fn flat_error_variant_metadata(enum_: &DataEnum) -> syn::Result<Vec<TokenStream>> { let variants_len = try_metadata_value_from_usize(enum_.variants.len(), "UniFFI limits enums to 256 variants")?; - Ok(std::iter::once(quote! { .concat_value(#variants_len) }) + std::iter::once(Ok(quote! { .concat_value(#variants_len) })) .chain(enum_.variants.iter().map(|v| { let name = ident_to_string(&v.ident); - quote! { .concat_str(#name) } + let docstring = extract_docstring(&v.attrs)?; + Ok(quote! { + .concat_str(#name) + .concat_long_str(#docstring) + }) })) - .collect()) + .collect() } -#[derive(Default)] +#[derive(Clone, Default)] pub struct ErrorAttr { - flat: Option<kw::flat_error>, - with_try_read: Option<kw::with_try_read>, + pub flat: Option<kw::flat_error>, + pub with_try_read: Option<kw::with_try_read>, + pub non_exhaustive: Option<kw::non_exhaustive>, } impl UniffiAttributeArgs for ErrorAttr { @@ -243,8 +260,13 @@ impl UniffiAttributeArgs for ErrorAttr { with_try_read: input.parse()?, ..Self::default() }) + } else if lookahead.peek(kw::non_exhaustive) { + Ok(Self { + non_exhaustive: input.parse()?, + ..Self::default() + }) } else if lookahead.peek(kw::handle_unknown_callback_error) { - // Not used anymore, but still lallowed + // Not used anymore, but still allowed Ok(Self::default()) } else { Err(lookahead.error()) @@ -255,6 +277,7 @@ impl UniffiAttributeArgs for ErrorAttr { Ok(Self { flat: either_attribute_arg(self.flat, other.flat)?, with_try_read: either_attribute_arg(self.with_try_read, other.with_try_read)?, + non_exhaustive: either_attribute_arg(self.non_exhaustive, other.non_exhaustive)?, }) } } @@ -265,3 +288,25 @@ impl Parse for ErrorAttr { parse_comma_separated(input) } } + +impl TryFrom<ErrorAttr> for EnumAttr { + type Error = syn::Error; + + fn try_from(error_attr: ErrorAttr) -> Result<Self, Self::Error> { + if error_attr.flat.is_some() { + Err(syn::Error::new( + Span::call_site(), + "flat attribute not valid for rich enum errors", + )) + } else if error_attr.with_try_read.is_some() { + Err(syn::Error::new( + Span::call_site(), + "with_try_read attribute not valid for rich enum errors", + )) + } else { + Ok(EnumAttr { + non_exhaustive: error_attr.non_exhaustive, + }) + } + } +} |