diff options
Diffstat (limited to 'third_party/rust/error-support-macros')
4 files changed, 152 insertions, 0 deletions
diff --git a/third_party/rust/error-support-macros/.cargo-checksum.json b/third_party/rust/error-support-macros/.cargo-checksum.json new file mode 100644 index 0000000000..337ad631b2 --- /dev/null +++ b/third_party/rust/error-support-macros/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"f982ce5b6a8dd3907bb201c06bd4a882f5a6a315d87210bc4c73999a1cd87e17","src/argument.rs":"bb97e801ce2c80b878328b15783678b913c2f34cf6f26a60d894c8da6b4e47aa","src/lib.rs":"8dd5b6225791730881a3500c3013c48678879430d859d0b92ac9dad4c42b04e0"},"package":null}
\ No newline at end of file diff --git a/third_party/rust/error-support-macros/Cargo.toml b/third_party/rust/error-support-macros/Cargo.toml new file mode 100644 index 0000000000..bf2f8053f6 --- /dev/null +++ b/third_party/rust/error-support-macros/Cargo.toml @@ -0,0 +1,32 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "error-support-macros" +version = "0.1.0" +publish = false +license = "MPL-2.0" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" + +[dependencies.syn] +version = "1.0" +features = [ + "derive", + "parsing", + "full", +] 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", + )) + } +} |