1mod classify;
5mod composition;
6mod failure;
7mod instrument;
8mod lift;
9mod rejection;
10mod std_error;
11
12use proc_macro::TokenStream;
13use syn::{DeriveInput, parse_macro_input};
14
15fn runtime_path() -> proc_macro2::TokenStream {
18 use proc_macro_crate::{FoundCrate, crate_name};
19 match crate_name("errlanes") {
20 Ok(FoundCrate::Name(name)) => {
21 let name = quote::format_ident!("{name}");
22 quote::quote!(#name)
23 }
24 Ok(FoundCrate::Itself) => quote::quote!(errlanes),
25 Err(_) => match crate_name("es-entity") {
26 Ok(FoundCrate::Name(name)) => {
27 let name = quote::format_ident!("{name}");
28 quote::quote!(#name::errlanes)
29 }
30 Ok(FoundCrate::Itself) => quote::quote!(es_entity::errlanes),
31 Err(_) => quote::quote!(errlanes),
32 },
33 }
34}
35
36fn resolve_runtime(tokens: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
37 use proc_macro2::{Group, TokenTree};
38 let mut output = proc_macro2::TokenStream::new();
39 let mut input = tokens.into_iter().peekable();
40 let mut qualified = false;
41 while let Some(token) = input.next() {
42 let was_qualified = qualified;
43 qualified = matches!(&token, TokenTree::Punct(p) if p.as_char() == ':');
44 match token {
45 TokenTree::Ident(ref name)
46 if name == "errlanes"
47 && !was_qualified
48 && matches!(input.peek(), Some(TokenTree::Punct(p)) if p.as_char() == ':') =>
49 {
50 output.extend(runtime_path());
51 }
52 TokenTree::Group(group) => {
53 let mut rewritten = Group::new(group.delimiter(), resolve_runtime(group.stream()));
54 rewritten.set_span(group.span());
55 output.extend([TokenTree::Group(rewritten)]);
56 }
57 other => output.extend([other]),
58 }
59 }
60 output
61}
62
63fn expand(
64 input: TokenStream,
65 f: impl FnOnce(&DeriveInput) -> darling::Result<proc_macro2::TokenStream>,
66) -> TokenStream {
67 let ast = parse_macro_input!(input as DeriveInput);
68 match f(&ast) {
69 Ok(tokens) => resolve_runtime(tokens).into(),
70 Err(e) => e.write_errors().into(),
71 }
72}
73
74#[proc_macro_derive(Rejection, attributes(rejection, lift, error, source, from))]
75pub fn derive_rejection(input: TokenStream) -> TokenStream {
76 expand(input, rejection::derive)
77}
78
79#[proc_macro_derive(Classify, attributes(classify, rejection, lift, error, source, from))]
80pub fn derive_classify(input: TokenStream) -> TokenStream {
81 expand(input, classify::derive)
82}
83
84#[proc_macro_derive(Lift, attributes(lift))]
88pub fn derive_lift(input: TokenStream) -> TokenStream {
89 expand(input, |ast| lift::derive(ast).map_err(darling::Error::from))
90}
91
92#[proc_macro_derive(Failure, attributes(failure))]
93pub fn derive_failure(input: TokenStream) -> TokenStream {
94 expand(input, failure::derive)
95}
96
97#[proc_macro_attribute]
105pub fn compose(args: TokenStream, input: TokenStream) -> TokenStream {
106 match syn::parse::<syn::ItemEnum>(input)
107 .and_then(|item| composition::compose(args.into(), item))
108 {
109 Ok(t) => resolve_runtime(t).into(),
110 Err(e) => e.to_compile_error().into(),
111 }
112}
113
114#[doc(hidden)]
115#[proc_macro]
116pub fn __compose_rejection(input: TokenStream) -> TokenStream {
117 match composition::callback(input.into()) {
118 Ok(t) => resolve_runtime(t).into(),
119 Err(e) => e.to_compile_error().into(),
120 }
121}
122
123#[proc_macro_attribute]
124pub fn instrument(args: TokenStream, input: TokenStream) -> TokenStream {
125 match instrument::expand(args.into(), input.into()) {
126 Ok(t) => resolve_runtime(t).into(),
127 Err(e) => e.to_compile_error().into(),
128 }
129}