quick arrays
Diffstat (limited to 'src/lib.rs')
| -rw-r--r-- | src/lib.rs | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..78ba724 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,89 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{ + parse::{self, Parse, ParseStream}, + parse_macro_input, + punctuated::Punctuated, + Error, Expr, LitInt, Token, +}; + +#[derive(Clone)] +struct Index { + index: usize, + value: Expr, +} + +impl std::fmt::Debug for Index { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.index) + } +} + +impl Parse for Index { + fn parse(input: ParseStream<'_>) -> parse::Result<Index> { + let index = input.parse::<LitInt>()?; + let index = index.base10_parse()?; + input.parse::<Token![=>]>()?; + let value = input.parse()?; + Ok(Index { index, value }) + } +} + +struct Map(Vec<Option<Index>>); +impl Parse for Map { + fn parse(input: ParseStream) -> syn::Result<Self> { + let parsed = Punctuated::<Index, Token![,]>::parse_terminated(input)?; + let mut all = parsed.into_iter().collect::<Vec<_>>(); + if all.len() == 0 { + return Err(input.error("no keys")); + } + all.sort_unstable_by(|a, b| a.index.cmp(&b.index)); + let max = all[all.len() - 1].index; + let mut out: Vec<Option<Index>> = vec![None; max + 1]; + for Index { value, index } in all { + let o = out.get_mut(index).unwrap(); + match o { + Some(_) => { + // err.combine(Error::new_spanned(&v.value, "other duplicate key")); + return Err(Error::new_spanned(&value, "duplicate keys")); + } + None => *o = Some(Index { value, index }), + } + } + Ok(Map(out)) + } +} + +/// Easily make a `[Option<T>; N]` +/// +/// ``` +/// # use amap::amap; +/// #[derive(Debug, PartialEq)] +/// enum Y { +/// A, +/// B, +/// C, +/// } +/// static X: [Option<Y>; 46] = amap! { +/// 2 => Y::A, +/// 5 => Y::C, +/// 45 => Y::B, +/// }; +/// assert_eq!(X[45].as_ref().unwrap(), &Y::B); +/// ``` +#[proc_macro] +pub fn amap(input: TokenStream) -> TokenStream { + let map = parse_macro_input!(input as Map); + let map = map.0.iter().map(|index| { + if let Some(index) = index { + let v = &index.value; + quote!(Some(#v)) + } else { + quote!(None) + } + }); + quote! { + [#(#map), *] + } + .into() +} |