bitbuffer_derive 0.11.3

Reading bit sequences from a byte slice
Documentation
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<Expr> for Discriminant {
    type Error = Error;

    fn try_from(value: Expr) -> Result<Self, Self::Error> {
        match value {
            Expr::Lit(ExprLit { lit, .. }) => lit.try_into(),
            _ => err("non literal discriminant", value.span())?,
        }
    }
}

impl TryFrom<Lit> for Discriminant {
    type Error = Error;

    fn try_from(value: Lit) -> Result<Self, Self::Error> {
        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
            }
        }
    }
}