use crate::err; use proc_macro2::{Span, TokenStream}; use quote::{quote, quote_spanned}; use std::convert::{TryFrom, TryInto}; use syn::spanned::Spanned; use syn::{Error, Expr, ExprLit, Lit, LitInt}; #[derive(Copy, Clone)] pub enum Discriminant { Int(usize), Default, Wildcard, } impl TryFrom for Discriminant { type Error = Error; fn try_from(value: Expr) -> Result { match value { Expr::Lit(ExprLit { lit, .. }) => lit.try_into(), _ => err("non literal discriminant", value.span())?, } } } impl TryFrom for Discriminant { type Error = Error; fn try_from(value: Lit) -> Result { let span = value.span(); match value { Lit::Int(lit) => Ok(Discriminant::Int(lit.base10_parse()?)), Lit::Str(lit) => match lit.value().as_str() { "_" => Ok(Discriminant::Wildcard), _ => err( "discriminant is required to be an integer literal or \"_\"", span, ), }, _ => err( "discriminant is required to be an integer literal or \"_\"", span, ), } } } impl Discriminant { pub fn read_token(&self, last_discriminant: &mut isize, span: Span) -> TokenStream { match self { Discriminant::Int(discriminant) => { let lit = LitInt::new(&format!("{}", discriminant), span); *last_discriminant = *discriminant as isize; quote! { #lit } } Discriminant::Wildcard => quote! { _ }, Discriminant::Default => { let new_discriminant = (*last_discriminant + 1) as usize; let lit = LitInt::new(&format!("{}", new_discriminant), span); *last_discriminant += 1; quote! { #lit } } } } pub fn write_token( &self, last_discriminant: &mut isize, max_discriminant: usize, span: Span, ) -> TokenStream { match self { Discriminant::Int(discriminant) => { let lit = LitInt::new(&format!("{}", discriminant), span); *last_discriminant = *discriminant as isize; quote_spanned! { span => #lit } } Discriminant::Wildcard => { let free_discriminant = max_discriminant + 1; let lit = LitInt::new(&format!("{}", free_discriminant), span); quote_spanned! { span => #lit } } Discriminant::Default => { let new_discriminant = (*last_discriminant + 1) as usize; let lit = LitInt::new(&format!("{}", new_discriminant), span); *last_discriminant += 1; quote_spanned! { span => #lit } } } } pub fn max_value(&self, last_discriminant: &mut isize) -> usize { match self { Discriminant::Int(discriminant) => { *last_discriminant = *discriminant as isize; *discriminant } Discriminant::Wildcard => 0, Discriminant::Default => { let new_discriminant = (*last_discriminant + 1) as usize; *last_discriminant += 1; new_discriminant } } } }