Skip to content

Commit

Permalink
feat: Working coerce macro
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmah309 committed Apr 9, 2024
1 parent 40eece6 commit c2bbe6d
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 78 deletions.
85 changes: 66 additions & 19 deletions impl/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::{cell::RefCell, rc::Rc};
use proc_macro2::TokenStream;
use quote::TokenStreamExt;
use syn::{Attribute, Ident};
use std::collections::HashMap;

use crate::ast::{is_type_path_equal, AstErrorEnumVariant};

Expand Down Expand Up @@ -291,57 +290,105 @@ fn add_coerce_macro(enum_intersections: Vec<EnumIntersection>, token_stream: &mu
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;
//let source = source_variant.source;
match_arms.append_all(quote::quote! {
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! {
Err(#enum1_name::#variant(source)) => { Err(#enum2_name::#variant(source)) },
});
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.append_all(quote::quote! {
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! {
($expr:expr => { $($patterns:pat => $results:expr),+ ; Err(#enum1_name) => return Err(#enum2_name) }) => {
($expr:expr => { $($patterns:pat => $results:expr),+ } || Err(#enum1_name) => return Err(#enum2_name)) => {
match $expr {
$($patterns => $results,)+
#match_arms_return_err
}
};
($expr:expr => { $($patterns:pat => $results:expr),+ } || Err(#enum1_name) => Err(#enum2_name)) => {
match $expr {
$($patterns => $results,)+
#match_arms_err
}
};
($expr:expr => { $($patterns:pat => $results:expr),+ } || #enum1_name => return #enum2_name) => {
match $expr {
$($patterns => $results,)+
#match_arms_return
}
};
($expr:expr => { $($patterns:pat => $results:expr),+ } || #enum1_name => #enum2_name) => {
match $expr {
$($patterns => $results,)+
#match_arms
}
};
});
}
// when no default coercion
macro_pattern_token_stream.append_all(quote::quote! {
($expr:expr => { $($patterns:pat => $results:expr),+ ;}) => {
($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. You pattern is either incorrect or there are no intersection between the sets. `coerce` is expected to follow the pattern:
coerce!($VAR => {
$arms
one_of<
($FROM => $TO),
($FROM => return $TO),
(Err($FROM) => Err($TO),
(Err($FROM) => return Err($TO))
>?
})
"#)
compile_error!(r#"
No patterns matched.
Possible reasons:
1. There are no intersections between the sets.
2. The last arm of of "$arms" has a trailing comma.
3. The pattern is incorrect.
`coerce` is expected to follow the pattern:
```
coerce!($VAR => {
<$arms>+
}< || one_of<
<$FROM => $TO>,
<$FROM => return $TO>,
<Err($FROM) => Err($TO)>,
<Err($FROM) => return Err($TO)>
>
>?)
```
"#)
};
});
token_stream.append_all(quote::quote! {
#[allow(unused_macros)]
macro_rules! coerce {
#macro_pattern_token_stream
}
Expand Down
118 changes: 88 additions & 30 deletions tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
mod temp;

#[cfg(test)]
pub mod regular {
use error_set::error_set;
Expand Down Expand Up @@ -93,6 +91,7 @@ pub mod error_sources_of_same_name {
};
}


#[test]
fn test() {
let x = X::IoError(std::io::Error::new(
Expand Down Expand Up @@ -232,43 +231,102 @@ pub mod readme_example_aggregation {
}

pub mod coerce_trait {
use error_set::{error_set, Coerce, CoerceResult};
use error_set::error_set;

error_set! {
MediaError = {
IoError(std::io::Error)
} || BookParsingError || DownloadError || UploadError;
BookParsingError = {
MissingDescriptionArg
} || BookSectionParsingError;
BookSectionParsingError = {
MissingNameArg,
NoContents,
};
DownloadError = {
CouldNotConnect,
OutOfMemory(std::io::Error),
SetX = {
X
} || Common;
#[derive(PartialEq,Eq)]
SetY = {
Y
} || Common;
Common = {
A,
B,
C,
D,
E,
F,
G,
H,
};
UploadError = {
NoConnection(std::io::Error),
}

fn setx_result() -> Result<(),SetX> {
Err(SetX::A)
}
fn setx() -> SetX {
SetX::A
}

fn setx_result_to_sety_result_coerce_return() -> Result<(),SetY> {
let _ok = coerce!(setx_result() => {
Ok(ok) => ok,
Err(SetX::X) => () // handle
} || Err(SetX) => return Err(SetY));
Ok(())
}
fn setx_result_to_sety_result_coerce() -> Result<(),SetY> {
let result: Result<(),SetY> = coerce!(setx_result() => {
Ok(_) => Err(SetY::D),
Err(SetX::X) => Err(SetY::F) // handle
} || Err(SetX) => Err(SetY));
result
}
fn setx_to_sety_coerce() -> SetY {
let sety = coerce!(setx() => {
SetX::X => SetY::C // handle
} || SetX => SetY);
sety
}
fn setx_to_sety_coerce_return() -> SetY {
let sety = coerce!(setx() => {
SetX::X => SetY::G // handle
} || SetX => return SetY);
sety
}

fn setx_result_to_sety_result() -> Result<(), SetY> {
let _ok = match setx_result() {
Ok(ok) => ok,
Err(SetX::X) => {}
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(())
}

#[test]
fn test() {
let book_section_parsing_error = BookSectionParsingError::MissingNameArg;
let book_parsing_error: BookParsingError = book_section_parsing_error.coerce();
assert!(matches!(
book_parsing_error,
BookParsingError::MissingNameArg
));
let media_error: MediaError = book_parsing_error.coerce();
assert!(matches!(media_error, MediaError::MissingNameArg));
assert_eq!(setx_result_to_sety_result_coerce_return().unwrap_err(), SetY::A);
assert_eq!(setx_result_to_sety_result_coerce().unwrap_err(), SetY::A);
assert_eq!(setx_to_sety_coerce(), SetY::A);
assert_eq!(setx_to_sety_coerce_return(), SetY::A);

let io_error =std::io::Error::new(std::io::ErrorKind::OutOfMemory, "oops out of memory");
let result_download_error: Result<(), DownloadError> = Err(io_error).coerce();
let result_media_error: Result<(), MediaError> = result_download_error.coerce();
assert!(matches!(result_media_error, Err(MediaError::IoError(_))));
assert_eq!(setx_result_to_sety_result().unwrap_err(), SetY::A);
}
}

Expand Down
29 changes: 0 additions & 29 deletions tests/temp.rs

This file was deleted.

0 comments on commit c2bbe6d

Please sign in to comment.