1#![warn(clippy::pedantic, rust_2018_idioms)]
62
63use std::ops::Range;
64
65use proc_macro2::Span;
66use quote::quote;
67use syn::{punctuated::Punctuated, Token};
68use to_arraystring::ToArrayString;
69
70struct ParsedRange(Range<u32>);
72
73impl syn::parse::Parse for ParsedRange {
74 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
75 let start = input.parse::<syn::LitInt>()?;
76 let needs_increment = if input.peek(Token![..=]) {
77 input.parse::<Token![..=]>()?;
78 true
79 } else if input.peek(Token![..]) {
80 input.parse::<Token![..]>()?;
81 false
82 } else {
83 return Err(input.error("Could not parse range syntax, expected `..` or `..=`"));
84 };
85
86 let end = input.parse::<syn::LitInt>()?;
87
88 let start = start.base10_parse()?;
89 let mut end = end.base10_parse()?;
90
91 if needs_increment {
92 end += 1;
93 }
94
95 Ok(Self(start..end))
96 }
97}
98
99struct RestGenerics(Punctuated<syn::GenericParam, Token![,]>);
101
102impl syn::parse::Parse for RestGenerics {
103 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
104 if input.parse::<Option<Token![,]>>()?.is_none() {
105 return Ok(Self(Punctuated::new()));
106 }
107
108 let mut out = Punctuated::new();
109 while !input.peek(Token![>]) {
110 out.push_value(input.parse()?);
111 if let Some(comma) = input.parse::<Option<Token![,]>>()? {
112 out.push_punct(comma);
113 } else {
114 break;
115 }
116 }
117
118 Ok(Self(out))
119 }
120}
121
122#[derive(Debug)]
123struct Arguments {
124 index: syn::Ident,
125 index_ty: syn::Type,
126 range: Range<u32>,
127 generics: Punctuated<syn::GenericParam, Token![,]>,
128 trait_name: syn::Ident,
129 trait_generics: syn::Generics,
130 where_clause: Option<syn::WhereClause>,
131 rest: proc_macro2::TokenStream,
132}
133
134impl syn::parse::Parse for Arguments {
135 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
136 input.parse::<Token![impl]>()?;
137
138 input.parse::<Token![<]>()?;
139 input.parse::<Token![const]>()?;
140 let index = input.parse()?;
141 input.parse::<Token![:]>()?;
142 let index_ty = input.parse::<syn::Type>()?;
143 input.parse::<Token![=]>()?;
144 let range = input.parse::<ParsedRange>()?.0;
145 let generics = input.parse::<RestGenerics>()?.0;
146 input.parse::<Token![>]>()?;
147
148 let trait_name = input.parse()?;
149 let trait_generics = input.parse()?;
150 input.parse::<Token![for]>()?;
151
152 if input.parse::<Token![#]>().is_err() || input.parse::<syn::Ident>()? != "TypeNumName" {
153 return Err(syn::Error::new(
154 Span::call_site(),
155 "Expected implementation for literal `#TypeNumName`",
156 ));
157 }
158
159 let where_clause = input.parse::<Option<syn::WhereClause>>()?;
160
161 let inside_parens;
162 syn::braced!(inside_parens in input);
163
164 let rest = inside_parens.parse::<proc_macro2::TokenStream>()?;
165
166 Ok(Self {
167 index,
168 index_ty,
169 range,
170 generics,
171 trait_name,
172 trait_generics,
173 where_clause,
174 rest,
175 })
176 }
177}
178
179#[derive(Debug)]
180struct GenerateTypenum(u32);
181
182impl quote::ToTokens for GenerateTypenum {
183 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
184 if self.0 == 0 {
185 return quote!(::typenum::UTerm).to_tokens(tokens);
186 }
187
188 let num_bits_set = u32::BITS - self.0.leading_zeros();
189 for _ in 0..num_bits_set {
190 quote!(::typenum::UInt<).to_tokens(tokens);
191 }
192
193 quote!(::typenum::UTerm,).to_tokens(tokens);
194
195 for n in (0..num_bits_set).rev() {
196 if self.0 & (1 << n) == 0 {
197 quote!(::typenum::B0>).to_tokens(tokens);
198 } else {
199 quote!(::typenum::B1>).to_tokens(tokens);
200 }
201
202 if n != 0 {
203 quote!(,).to_tokens(tokens);
204 }
205 }
206 }
207}
208
209#[proc_macro]
210pub fn impl_typenum_mapping(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
211 let Arguments {
212 index,
213 index_ty,
214 range,
215 generics,
216 trait_name,
217 trait_generics,
218 where_clause,
219 rest,
220 } = match syn::parse(tokens) {
221 Ok(args) => args,
222 Err(err) => return err.into_compile_error().into(),
223 };
224
225 let typenum_iter = range.clone().map(GenerateTypenum);
226 let range = range.map(|i| syn::LitInt::new(&i.to_arraystring(), Span::call_site()));
227
228 quote!(
229 #(const _: () = {
230 const #index: #index_ty = #range;
231 impl<#generics> #trait_name #trait_generics for #typenum_iter #where_clause {
232 #rest
233 }
234 };)*
235 )
236 .into()
237}