1use proc_macro::TokenStream;
2use quote::quote;
3use syn::parse::{Parse, ParseStream, Result};
4use syn::punctuated::Punctuated;
5use syn::{parse_macro_input, Attribute, Ident, Token, Visibility};
6
7struct TypeItem {
8 attrs: Vec<Attribute>,
9 vis: Visibility,
10 name: Ident,
11 cases: Punctuated<Ident, Token![+]>,
12}
13
14impl Parse for TypeItem {
15 fn parse(input: ParseStream) -> Result<Self> {
16 let attrs = input.call(Attribute::parse_outer)?;
17 let vis = input.parse()?;
18 let _ = input.parse::<Token![type]>()?;
19 let name = input.parse()?;
20 let _ = input.parse::<Token![=]>()?;
21 let mut cases = Punctuated::new();
22
23 loop {
24 cases.push_value(input.parse()?);
25 if input.peek(Token![;]) {
26 let _ = input.parse::<Token![;]>()?;
27 break;
28 }
29 cases.push_punct(input.parse()?);
30 }
31
32 Ok(Self {
33 attrs,
34 vis,
35 name,
36 cases,
37 })
38 }
39}
40
41struct Args {
42 superset: Option<Ident>,
43}
44
45impl Parse for Args {
46 fn parse(input: ParseStream) -> Result<Self> {
47 Ok(if let Ok(_) = input.parse::<Token![super]>() {
48 let _ = input.parse::<Token![=]>()?;
49 Self {
50 superset: input.parse()?,
51 }
52 } else {
53 Self { superset: None }
54 })
55 }
56}
57
58#[proc_macro_attribute]
98pub fn type_union(attr: TokenStream, item: TokenStream) -> TokenStream {
99 let Args { superset } = parse_macro_input!(attr as Args);
100 let TypeItem {
101 attrs,
102 vis,
103 name,
104 cases,
105 } = parse_macro_input!(item as TypeItem);
106 let cases = cases.into_iter().map(|ident| ident).collect::<Vec<_>>();
107
108 let impls = if let Some(superset) = superset {
109 quote! {
110 impl From<#name> for #superset {
111 fn from(value: #name) -> Self {
112 match value {
113 #(#name::#cases(case) => #superset::#cases(case)),*
114 }
115 }
116 }
117 }
118 } else {
119 quote!()
120 };
121
122 quote! {
123 #(#attrs)*
124 #vis enum #name {
125 #(#cases(#cases)),*
126 }
127
128 #impls
129
130 #(
131 impl From<#cases> for #name {
132 fn from(value: #cases) -> Self {
133 #name::#cases(value)
134 }
135 }
136 )*
137 }
138 .into()
139}