nonzero 0.2.0

Statically checked non-zero integers
Documentation
use crate::SignedInteger;
use num_traits::Zero;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{Error, Result};

pub(crate) fn nonzero(integer: SignedInteger) -> Result<TokenStream> {
    let SignedInteger {
        is_negative,
        literal: lit,
        span,
    } = integer;

    let neg = if is_negative {
        quote! {-}
    } else {
        quote! {}
    };

    let tokens = match (is_negative, lit.suffix()) {
        (false, "usize") => {
            let val: usize = lit.base10_parse()?;
            check_zero(&lit, val)?;
            quote! {
                unsafe {
                    core::num::NonZeroUsize::new_unchecked(#lit)
                }
            }
        }
        (false, "u8") => {
            let val: u8 = lit.base10_parse()?;
            check_zero(&lit, val)?;
            quote! {
                unsafe {
                    core::num::NonZeroU8::new_unchecked(#lit)
                }
            }
        }
        (false, "u16") => {
            let val: u16 = lit.base10_parse()?;
            check_zero(&lit, val)?;
            quote! {
                unsafe {
                    core::num::NonZeroU16::new_unchecked(#lit)
                }
            }
        }
        (false, "u32") => {
            let val: u32 = lit.base10_parse()?;
            check_zero(&lit, val)?;
            quote! {
                unsafe {
                    core::num::NonZeroU32::new_unchecked(#lit)
                }
            }
        }
        (false, "u64") => {
            let val: u64 = lit.base10_parse()?;
            check_zero(&lit, val)?;
            quote! {
                unsafe {
                    core::num::NonZeroU64::new_unchecked(#lit)
                }
            }
        }
        (true, "usize" | "u8" | "u16" | "u32" | "u64") => {
            return Err(Error::new(span, "unsigned integer cannot be negative"))
        }
        (_, "isize") => {
            let val: isize = lit.base10_parse()?;
            check_zero(&lit, val)?;
            quote! {
                unsafe {
                    core::num::NonZeroIsize::new_unchecked(#neg #lit)
                }
            }
        }
        (_, "i8") => {
            let val: i8 = lit.base10_parse()?;
            check_zero(&lit, val)?;
            quote! {
                unsafe {
                    core::num::NonZeroI8::new_unchecked(#neg #lit)
                }
            }
        }
        (_, "i16") => {
            let val: i16 = lit.base10_parse()?;
            check_zero(&lit, val)?;
            quote! {
                unsafe {
                    core::num::NonZeroI16::new_unchecked(#neg #lit)
                }
            }
        }
        (_, "i32") => {
            let val: i32 = lit.base10_parse()?;
            check_zero(&lit, val)?;
            quote! {
                unsafe {
                    core::num::NonZeroI32::new_unchecked(#neg #lit)
                }
            }
        }
        (_, "i64") => {
            let val: i64 = lit.base10_parse()?;
            check_zero(&lit, val)?;
            quote! {
                unsafe {
                    core::num::NonZeroI64::new_unchecked(#neg #lit)
                }
            }
        }
        (_, "") => {
            return Err(Error::new(span, "suffix is required"));
        }
        (_, suffix) => {
            return Err(Error::new(
                span,
                format!("the suffix '{}' is not supported", suffix),
            ));
        }
    };

    Ok(tokens)
}

fn check_zero(lit: impl ToTokens, val: impl Zero) -> Result<()> {
    if val.is_zero() {
        Err(Error::new_spanned(lit, "zero is not allowed"))
    } else {
        Ok(())
    }
}