checkito_macro 0.5.8

A set of macros to accompany the `checkito` crate.
Documentation
use proc_macro::TokenStream;
use quote::quote;
use regex_syntax::Parser;
use syn::{
    Error, Expr, ExprLit, Lit, LitStr,
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
    spanned::Spanned,
    token::Comma,
};

pub struct Regex(pub LitStr, pub Option<Expr>);

impl Parse for Regex {
    fn parse(input: ParseStream) -> Result<Self, Error> {
        let span = input.span();
        let expressions = Punctuated::<Expr, Comma>::parse_terminated(input)?;
        let mut expressions = expressions.into_iter();
        let pattern = match expressions.next() {
            Some(Expr::Lit(ExprLit {
                lit: Lit::Str(string),
                ..
            })) => string,
            Some(expression) => {
                return Err(Error::new(expression.span(), "expected a string literal"));
            }
            None => return Err(Error::new(span, "expected a string literal")),
        };
        let repeats = expressions.next();
        if let Some(expression) = expressions.next() {
            return Err(Error::new(expression.span(), "unexpected expression"));
        }
        match Parser::new().parse(&pattern.value()) {
            Ok(_) => Ok(Regex(pattern, repeats)),
            Err(error) => Err(Error::new(pattern.span(), format!("{error}"))),
        }
    }
}

impl From<Regex> for TokenStream {
    fn from(Regex(pattern, repeats): Regex) -> Self {
        let pattern = pattern.token();
        let repeats = match repeats {
            Some(repeats) => quote!({ #repeats }.into()),
            None => quote!(None),
        };
        quote!(::checkito::regex(#pattern, #repeats).expect("valid regex")).into()
    }
}