summaryrefslogtreecommitdiffstats
path: root/third_party/rust/error-support-macros/src
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/error-support-macros/src')
-rw-r--r--third_party/rust/error-support-macros/src/argument.rs21
-rw-r--r--third_party/rust/error-support-macros/src/lib.rs98
2 files changed, 119 insertions, 0 deletions
diff --git a/third_party/rust/error-support-macros/src/argument.rs b/third_party/rust/error-support-macros/src/argument.rs
new file mode 100644
index 0000000000..ad7bf87a6e
--- /dev/null
+++ b/third_party/rust/error-support-macros/src/argument.rs
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use syn::spanned::Spanned;
+
+const ERR_MSG: &str = "Expected #[handle_error(path::to::Error)]";
+
+/// Returns the path to the type of the "internal" error.
+pub(crate) fn validate(arguments: &syn::AttributeArgs) -> syn::Result<&syn::Path> {
+ if arguments.len() != 1 {
+ return Err(syn::Error::new(proc_macro2::Span::call_site(), ERR_MSG));
+ }
+
+ let nested_meta = arguments.iter().next().unwrap();
+ if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = nested_meta {
+ Ok(path)
+ } else {
+ Err(syn::Error::new(nested_meta.span(), ERR_MSG))
+ }
+}
diff --git a/third_party/rust/error-support-macros/src/lib.rs b/third_party/rust/error-support-macros/src/lib.rs
new file mode 100644
index 0000000000..22686b466b
--- /dev/null
+++ b/third_party/rust/error-support-macros/src/lib.rs
@@ -0,0 +1,98 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{parse_quote, spanned::Spanned};
+
+mod argument;
+
+/// A procedural macro that exposes internal errors to external errors the
+/// consuming applications should handle. It requires that the internal error
+/// implements [`error_support::ErrorHandling`].
+///
+/// Additionally, this procedural macro has side effects, including:
+/// * It would log the error based on a pre-defined log level. The log level is defined
+/// in the [`error_support::ErrorHandling`] implementation.
+/// * It would report some errors using an external error reporter, in practice, this
+/// is implemented using Sentry in the app.
+///
+/// # Example
+/// ```ignore
+/// use error_support::{handle_error, GetErrorHandling, ErrorHandling};
+/// use std::fmt::Display
+///#[derive(Debug, thiserror::Error)]
+/// struct Error {}
+/// type Result<T, E = Error> = std::result::Result<T, E>;
+
+/// impl Display for Error {
+/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+/// write!(f, "Internal Error!")
+/// }
+/// }
+///
+/// #[derive(Debug, thiserror::Error)]
+/// struct ExternalError {}
+///
+/// impl Display for ExternalError {
+/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+/// write!(f, "External Error!")
+/// }
+/// }
+///
+/// impl GetErrorHandling for Error {
+/// type ExternalError = ExternalError;
+///
+/// fn get_error_handling(&self) -> ErrorHandling<Self::ExternalError> {
+/// ErrorHandling::convert(ExternalError {})
+/// }
+/// }
+///
+/// // The `handle_error` macro maps from the error supplied in the mandatory argument
+/// // (ie, `Error` in this example) to the error returned by the function (`ExternalError`
+/// // in this example)
+/// #[handle_error(Error)]
+/// fn do_something() -> std::result::Result<String, ExternalError> {
+/// Err(Error{})
+/// }
+///
+/// // The error here is an `ExternalError`
+/// let _: ExternalError = do_something().unwrap_err();
+/// ```
+#[proc_macro_attribute]
+pub fn handle_error(args: TokenStream, input: TokenStream) -> TokenStream {
+ let args = syn::parse_macro_input!(args as syn::AttributeArgs);
+ let parsed = syn::parse_macro_input!(input as syn::Item);
+ TokenStream::from(match impl_handle_error(&parsed, &args) {
+ Ok(res) => res,
+ Err(e) => e.to_compile_error(),
+ })
+}
+
+fn impl_handle_error(
+ input: &syn::Item,
+ arguments: &syn::AttributeArgs,
+) -> syn::Result<proc_macro2::TokenStream> {
+ if let syn::Item::Fn(item_fn) = input {
+ let err_path = argument::validate(arguments)?;
+ let original_body = &item_fn.block;
+
+ let mut new_fn = item_fn.clone();
+ new_fn.block = parse_quote! {
+ {
+ (|| -> ::std::result::Result<_, #err_path> {
+ #original_body
+ })().map_err(::error_support::convert_log_report_error)
+ }
+ };
+
+ Ok(quote! {
+ #new_fn
+ })
+ } else {
+ Err(syn::Error::new(
+ input.span(),
+ "#[handle_error(..)] can only be used on functions",
+ ))
+ }
+}