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