diff --git a/README.md b/README.md
index de34da4..8296928 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
[](https://github.com/mcmah309/error_set/actions?query=branch%3Amaster)
-A concise way to define errors and ergonomically coerce a subset into a superset with with just `.into()` or `?`, and coerce between intersecting sets with `coerce!`.
+A concise way to define errors and ergonomically coerce a subset into a superset with with just `.into()` or `?`.
`error_set` was inspired by zig's [error set](https://ziglang.org/documentation/master/#Error-Set-Type)
and works functionally the same.
@@ -500,82 +500,4 @@ fn main() {
}
}
```
-
-
-## The `coerce!` Macro
-The `coerce!` macro handles coercing between intersecting sets (sets where some of the error types are in common). This allows only being explicit where relevant, such as the disjointedness.
-e.g. given:
-```rust
-error_set! {
- SetX = {
- X
- } || Common;
- SetY = {
- Y
- } || Common;
- Common = {
- A,
- B,
- C,
- D,
- E,
- F,
- G,
- H,
- };
-}
-```
-rather than writting:
-```rust
-fn setx_result_to_sety_result() -> Result<(), SetY> {
- let _ok = match setx_result() {
- Ok(ok) => ok,
- Err(SetX::X) => {} // handle disjointedness
- Err(SetX::A) => {
- return Err(SetY::A);
- }
- Err(SetX::B) => {
- return Err(SetY::B);
- }
- Err(SetX::C) => {
- return Err(SetY::C);
- }
- Err(SetX::D) => {
- return Err(SetY::D);
- }
- Err(SetX::E) => {
- return Err(SetY::E);
- }
- Err(SetX::F) => {
- return Err(SetY::F);
- }
- Err(SetX::G) => {
- return Err(SetY::G);
- }
- Err(SetX::H) => {
- return Err(SetY::H);
- }
- };
- Ok(())
-}
-```
-one can write this, which compiles to the `match` statement above:
-```rust
-fn setx_result_to_sety_result() -> Result<(),SetY> {
- let _ok = coerce!(setx_result() => {
- Ok(ok) => ok,
- Err(SetX::X) => {}, // handle disjointedness
- { Err(SetX) => return Err(SetY) } // terminal coercion
- });
- Ok(())
-}
-```
-The `coerce!` macro is a flat fast (no tt muncher 🦫) declarative macro created by the `error_set!` macro for the set.
-`coerce!` behaves like a regular `match` statement, except it allows a terminal coercion statement between sets. e.g.
-```rust
-{ Err(SetX) => return Err(SetY) }
-{ Err(SetX) => Err(SetY) }
-{ SetX => return SetY }
-{ SetX => SetY }
-```
-With `coerce!`, one can concisely handle specific variants of errors as they bubble up the call stack and propagate the rest.
\ No newline at end of file
+
\ No newline at end of file
diff --git a/impl/src/expand.rs b/impl/src/expand.rs
index 001e970..df8e4db 100644
--- a/impl/src/expand.rs
+++ b/impl/src/expand.rs
@@ -1,5 +1,7 @@
use std::{cell::RefCell, rc::Rc};
+#[cfg(feature = "coerce_macro")]
+use coerce_macro::add_coerce_macro;
use proc_macro2::TokenStream;
use quote::TokenStreamExt;
use syn::{Attribute, Ident};
@@ -253,58 +255,198 @@ fn impl_froms(error_enum_node: &ErrorEnumGraphNode, token_stream: &mut TokenStre
})
}
}
+//************************************************************************//
+#[derive(Clone)]
+struct ErrorEnumGraphNode {
+ pub(crate) error_enum: ErrorEnum,
+ /// nodes where all error variants of the error enum are in this error enum's error variants.
+ pub(crate) subsets: Vec>>,
+}
+
+impl PartialEq for ErrorEnumGraphNode {
+ fn eq(&self, other: &Self) -> bool {
+ self.error_enum == other.error_enum
+ }
+}
+
+impl ErrorEnumGraphNode {
+ pub(crate) fn new(node: ErrorEnum) -> ErrorEnumGraphNode {
+ ErrorEnumGraphNode {
+ error_enum: node,
+ subsets: Vec::new(),
+ }
+ }
+}
+
+#[derive(Clone)]
+pub(crate) struct ErrorEnum {
+ pub(crate) attributes: Vec,
+ pub(crate) error_name: Ident,
+ pub(crate) error_variants: Vec,
+}
+
+impl std::hash::Hash for ErrorEnum {
+ fn hash(&self, state: &mut H) {
+ self.error_name.hash(state);
+ }
+}
+
+impl Eq for ErrorEnum {}
+
+impl PartialEq for ErrorEnum {
+ fn eq(&self, other: &Self) -> bool {
+ self.error_name == other.error_name
+ }
+}
//************************************************************************//
#[cfg(feature = "coerce_macro")]
-fn add_coerce_macro(error_enums: &Vec, token_stream: &mut TokenStream) {
- let enum_intersections: Vec = construct_set_intersections(&error_enums);
- let mut macro_pattern_token_stream = TokenStream::new();
- for enum_interscetion in enum_intersections {
- let EnumIntersection {
- enum1: enum1_name,
- enum2: enum2_name,
- intersection,
- } = enum_interscetion;
- let mut match_arms_return_err = TokenStream::new();
- let mut match_arms_err = TokenStream::new();
- let mut match_arms_return = TokenStream::new();
- let mut match_arms = TokenStream::new();
- for variant in intersection {
- match variant {
- AstErrorEnumVariant::SourceErrorVariant(source_variant) => {
- let variant = source_variant.name;
- match_arms_return_err.append_all(quote::quote! {
+mod coerce_macro {
+ //! ## The `coerce!` Macro
+ //!
+ //! The `coerce!` macro handles coercing between intersecting sets (sets where some of the error types are in common). This allows only being explicit where relevant, such as the disjointedness.
+ //!
+ //! e.g. given:
+ //!
+ //! ```rust
+ //! error_set! {
+ //! SetX = {
+ //! X
+ //! } || Common;
+ //! SetY = {
+ //! Y
+ //! } || Common;
+ //! Common = {
+ //! A,
+ //! B,
+ //! C,
+ //! D,
+ //! E,
+ //! F,
+ //! G,
+ //! H,
+ //! };
+ //! }
+ //! ```
+ //!
+ //! rather than writing:
+ //!
+ //! ```rust
+ //! fn setx_result_to_sety_result() -> Result<(), SetY> {
+ //! let _ok = match setx_result() {
+ //! Ok(ok) => ok,
+ //! Err(SetX::X) => {} // handle disjointedness
+ //! Err(SetX::A) => {
+ //! return Err(SetY::A);
+ //! }
+ //! Err(SetX::B) => {
+ //! return Err(SetY::B);
+ //! }
+ //! Err(SetX::C) => {
+ //! return Err(SetY::C);
+ //! }
+ //! Err(SetX::D) => {
+ //! return Err(SetY::D);
+ //! }
+ //! Err(SetX::E) => {
+ //! return Err(SetY::E);
+ //! }
+ //! Err(SetX::F) => {
+ //! return Err(SetY::F);
+ //! }
+ //! Err(SetX::G) => {
+ //! return Err(SetY::G);
+ //! }
+ //! Err(SetX::H) => {
+ //! return Err(SetY::H);
+ //! }
+ //! };
+ //! Ok(())
+ //! }
+ //! ```
+ //!
+ //! one can write this, which compiles to the `match` statement above:
+ //!
+ //! ```rust
+ //! fn setx_result_to_sety_result() -> Result<(), SetY> {
+ //! let _ok = coerce!(setx_result() => {
+ //! Ok(ok) => ok,
+ //! Err(SetX::X) => {}, // handle disjointedness
+ //! { Err(SetX) => return Err(SetY) } // terminal coercion
+ //! });
+ //! Ok(())
+ //! }
+ //! ```
+ //!
+ //! The `coerce!` macro is a flat fast (no tt muncher 🦫) declarative macro created by the `error_set!` macro for the set.
+ //! `coerce!` behaves like a regular `match` statement, except it allows a terminal coercion statement between sets. e.g.
+ //!
+ //! ```rust
+ //! { Err(SetX) => return Err(SetY) }
+ //! { Err(SetX) => Err(SetY) }
+ //! { SetX => return SetY }
+ //! { SetX => SetY }
+ //! ```
+ //!
+ //! With `coerce!`, one can concisely handle specific variants of errors as they bubble up the call stack and propagate the rest.
+
+ use proc_macro2::TokenStream;
+ use quote::TokenStreamExt;
+ use syn::Ident;
+
+ use crate::ast::AstErrorEnumVariant;
+
+ use super::ErrorEnum;
+
+ pub(crate) fn add_coerce_macro(error_enums: &Vec, token_stream: &mut TokenStream) {
+ let enum_intersections: Vec = construct_set_intersections(&error_enums);
+ let mut macro_pattern_token_stream = TokenStream::new();
+ for enum_interscetion in enum_intersections {
+ let EnumIntersection {
+ enum1: enum1_name,
+ enum2: enum2_name,
+ intersection,
+ } = enum_interscetion;
+ let mut match_arms_return_err = TokenStream::new();
+ let mut match_arms_err = TokenStream::new();
+ let mut match_arms_return = TokenStream::new();
+ let mut match_arms = TokenStream::new();
+ for variant in intersection {
+ match variant {
+ AstErrorEnumVariant::SourceErrorVariant(source_variant) => {
+ let variant = source_variant.name;
+ match_arms_return_err.append_all(quote::quote! {
Err(#enum1_name::#variant(source)) => { return Err(#enum2_name::#variant(source)); },
});
- match_arms_err.append_all(quote::quote! {
+ match_arms_err.append_all(quote::quote! {
Err(#enum1_name::#variant(source)) => { Err(#enum2_name::#variant(source)) },
});
- match_arms_return.append_all(quote::quote! {
+ match_arms_return.append_all(quote::quote! {
#enum1_name::#variant(source) => { return #enum2_name::#variant(source); },
});
- match_arms.append_all(quote::quote! {
- #enum1_name::#variant(source) => { #enum2_name::#variant(source) },
- });
- },
- AstErrorEnumVariant::Variant(variant) => {
- let variant = variant.name;
- match_arms_return_err.append_all(quote::quote! {
- Err(#enum1_name::#variant) => { return Err(#enum2_name::#variant); },
- });
- match_arms_err.append_all(quote::quote! {
- Err(#enum1_name::#variant) => { Err(#enum2_name::#variant) },
- });
- match_arms_return.append_all(quote::quote! {
- #enum1_name::#variant => { return #enum2_name::#variant; },
- });
- match_arms.append_all(quote::quote! {
- #enum1_name::#variant => { #enum2_name::#variant },
- });
- },
+ match_arms.append_all(quote::quote! {
+ #enum1_name::#variant(source) => { #enum2_name::#variant(source) },
+ });
+ }
+ AstErrorEnumVariant::Variant(variant) => {
+ let variant = variant.name;
+ match_arms_return_err.append_all(quote::quote! {
+ Err(#enum1_name::#variant) => { return Err(#enum2_name::#variant); },
+ });
+ match_arms_err.append_all(quote::quote! {
+ Err(#enum1_name::#variant) => { Err(#enum2_name::#variant) },
+ });
+ match_arms_return.append_all(quote::quote! {
+ #enum1_name::#variant => { return #enum2_name::#variant; },
+ });
+ match_arms.append_all(quote::quote! {
+ #enum1_name::#variant => { #enum2_name::#variant },
+ });
+ }
+ }
}
- }
- macro_pattern_token_stream.append_all(quote::quote! {
+ macro_pattern_token_stream.append_all(quote::quote! {
($expr:expr => { $($patterns:pat => $results:expr$(,)?)+, {Err(#enum1_name) => return Err(#enum2_name)} }) => {
match $expr {
$($patterns => $results,)+
@@ -330,19 +472,19 @@ fn add_coerce_macro(error_enums: &Vec, token_stream: &mut TokenStream
}
};
});
- }
- // when no default coercion
- macro_pattern_token_stream.append_all(quote::quote! {
- ($expr:expr => { $($patterns:pat => $results:expr$(,)?)+ }) => {
- match $expr {
- $($patterns => $results,)+
- }
- };
- });
- //
- macro_pattern_token_stream.append_all(quote::quote! {
- ($($other:tt)*) => {
- compile_error!(r#"
+ }
+ // when no default coercion
+ macro_pattern_token_stream.append_all(quote::quote! {
+ ($expr:expr => { $($patterns:pat => $results:expr$(,)?)+ }) => {
+ match $expr {
+ $($patterns => $results,)+
+ }
+ };
+ });
+ //
+ macro_pattern_token_stream.append_all(quote::quote! {
+ ($($other:tt)*) => {
+ compile_error!(r#"
No patterns matched.
Possible reasons:
1. There are no intersections between the sets.
@@ -360,98 +502,61 @@ coerce!($VAR => {
});
```
"#)
- };
- });
- token_stream.append_all(quote::quote! {
- #[allow(unused_macros)]
- macro_rules! coerce {
- #macro_pattern_token_stream
- }
+ };
+ });
+ token_stream.append_all(quote::quote! {
+ #[allow(unused_macros)]
+ macro_rules! coerce {
+ #macro_pattern_token_stream
+ }
- pub(crate) use coerce;
- });
-}
+ pub(crate) use coerce;
+ });
+ }
-#[cfg(feature = "coerce_macro")]
-fn construct_set_intersections(error_enums: &Vec) -> Vec {
- let mut enum_intersections: Vec = Vec::new();
- let length = error_enums.len();
- for index1 in 0..length {
- for index2 in 0..length {
- let enum1 = &error_enums[index1];
- let enum2 = &error_enums[index2];
- let mut intersections = Vec::new();
- for variant in &enum1.error_variants {
- if enum2.error_variants.contains(&variant) {
- intersections.push(variant.clone());
+ fn construct_set_intersections(error_enums: &Vec) -> Vec {
+ let mut enum_intersections: Vec = Vec::new();
+ let length = error_enums.len();
+ for index1 in 0..length {
+ for index2 in 0..length {
+ let enum1 = &error_enums[index1];
+ let enum2 = &error_enums[index2];
+ let mut intersections = Vec::new();
+ for variant in &enum1.error_variants {
+ if enum2.error_variants.contains(&variant) {
+ intersections.push(variant.clone());
+ }
+ }
+ if !intersections.is_empty() {
+ let enum_intersection = EnumIntersection::new(
+ enum1.error_name.clone(),
+ enum2.error_name.clone(),
+ intersections,
+ );
+ enum_intersections.push(enum_intersection);
}
}
- if !intersections.is_empty() {
- let enum_intersection = EnumIntersection::new(enum1.error_name.clone(), enum2.error_name.clone(), intersections);
- enum_intersections.push(enum_intersection);
- }
- }
- }
- enum_intersections
-}
-
-#[cfg(feature = "coerce_macro")]
-struct EnumIntersection {
- pub(crate) enum1: Ident,
- pub(crate) enum2: Ident,
- pub(crate) intersection: Vec,
-}
-
-#[cfg(feature = "coerce_macro")]
-impl EnumIntersection {
- pub(crate) fn new(enum1: Ident, enum2: Ident, intersection: Vec) -> EnumIntersection {
- EnumIntersection {
- enum1,
- enum2,
- intersection,
}
+ enum_intersections
}
-}
-//************************************************************************//
-#[derive(Clone)]
-struct ErrorEnumGraphNode {
- pub(crate) error_enum: ErrorEnum,
- /// nodes where all error variants of the error enum are in this error enum's error variants.
- pub(crate) subsets: Vec>>,
-}
-impl PartialEq for ErrorEnumGraphNode {
- fn eq(&self, other: &Self) -> bool {
- self.error_enum == other.error_enum
+ struct EnumIntersection {
+ pub(crate) enum1: Ident,
+ pub(crate) enum2: Ident,
+ pub(crate) intersection: Vec,
}
-}
-impl ErrorEnumGraphNode {
- pub(crate) fn new(node: ErrorEnum) -> ErrorEnumGraphNode {
- ErrorEnumGraphNode {
- error_enum: node,
- subsets: Vec::new(),
+ impl EnumIntersection {
+ pub(crate) fn new(
+ enum1: Ident,
+ enum2: Ident,
+ intersection: Vec,
+ ) -> EnumIntersection {
+ EnumIntersection {
+ enum1,
+ enum2,
+ intersection,
+ }
}
}
}
-
-#[derive(Clone)]
-pub(crate) struct ErrorEnum {
- pub(crate) attributes: Vec,
- pub(crate) error_name: Ident,
- pub(crate) error_variants: Vec,
-}
-
-impl std::hash::Hash for ErrorEnum {
- fn hash(&self, state: &mut H) {
- self.error_name.hash(state);
- }
-}
-
-impl Eq for ErrorEnum {}
-
-impl PartialEq for ErrorEnum {
- fn eq(&self, other: &Self) -> bool {
- self.error_name == other.error_name
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index ba92c94..7edd04c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,5 @@
+#![doc = include_str!("../README.md")]
+
pub use error_set_impl::*;
pub trait Coerce {