proc-macro-regex 1.1.0

A proc macro regex library
Documentation
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use regex_syntax::hir::{Class, ClassBytes, ClassUnicode, Literal};
use std::collections::BTreeSet;
use thiserror::Error;

fn to_byte(c: char) -> CharacterResult<u8> {
    if c.len_utf8() == 1 {
        let mut bytes = [0; 1];
        c.encode_utf8(&mut bytes);
        Ok(bytes[0])
    } else {
        Err(CharacterError::Unicode(c))
    }
}

fn to_char(b: u8) -> CharacterResult<char> {
    match char::try_from(b) {
        Ok(c) => Ok(c),
        Err(_) => Err(CharacterError::Byte(b)),
    }
}

#[derive(Debug, Error)]
pub enum CharacterError {
    #[error("got byte: {0}")]
    Byte(u8),
    #[error("got class bytes: {0:?}")]
    ClassBytes(ClassBytes),
    #[error("got unicode: {0}")]
    Unicode(char),
    #[error("got class unicode: {0:?}")]
    ClassUnicode(ClassUnicode),
}

pub type CharacterResult<T> = Result<T, CharacterError>;

pub trait Character: Sized + Ord + TryFrom<u32> + Into<u32> {
    fn new_line() -> Self;

    fn from_literal(literal: Literal) -> CharacterResult<Self>;

    fn from_class(class: Class) -> CharacterResult<BTreeSet<Self>>;

    fn to_byte(&self) -> Option<u8>;

    fn is_byte(&self) -> bool;

    fn is_next(&self, other: &Self) -> bool;

    fn get_iterator_function(is_byte: bool) -> Ident;

    fn to_usize(element: Ident, is_byte: bool) -> TokenStream;

    fn allow_invalid_utf8() -> bool;

    fn unicode() -> bool;
}

impl Character for char {
    fn new_line() -> Self {
        '\n'
    }

    fn from_literal(literal: Literal) -> CharacterResult<Self> {
        match literal {
            Literal::Unicode(c) => Ok(c),
            Literal::Byte(b) => to_char(b),
        }
    }

    fn from_class(class: Class) -> CharacterResult<BTreeSet<Self>> {
        let mut cs = BTreeSet::new();
        match class {
            Class::Unicode(class_unicode) => {
                for class_unicode_range in class_unicode.iter() {
                    let start = class_unicode_range.start();
                    let end = class_unicode_range.end();
                    for c in start..=end {
                        cs.insert(c);
                    }
                }
            }
            Class::Bytes(class_bytes) => {
                for class_bytes_range in class_bytes.iter() {
                    let start = class_bytes_range.start();
                    let end = class_bytes_range.end();
                    for b in start..=end {
                        let c = to_char(b)?;
                        cs.insert(c);
                    }
                }
            }
        }
        Ok(cs)
    }

    fn to_byte(&self) -> Option<u8> {
        to_byte(*self).ok()
    }

    fn is_byte(&self) -> bool {
        self.len_utf8() == 1
    }

    fn is_next(&self, other: &Self) -> bool {
        let self_u32: u32 = *self as u32;
        if let Some(next) = self_u32.checked_add(1) {
            let other_u32 = *other as u32;
            next == other_u32
        } else {
            false
        }
    }

    fn get_iterator_function(is_byte: bool) -> Ident {
        if is_byte {
            Ident::new("bytes", Span::call_site())
        } else {
            Ident::new("chars", Span::call_site())
        }
    }

    fn to_usize(element: Ident, _is_byte: bool) -> TokenStream {
        quote! {
            #element as usize
        }
    }

    fn allow_invalid_utf8() -> bool {
        false
    }

    fn unicode() -> bool {
        true
    }
}

impl Character for u8 {
    fn new_line() -> Self {
        b'\n'
    }

    fn from_literal(literal: Literal) -> CharacterResult<Self> {
        match literal {
            Literal::Unicode(c) => to_byte(c),
            Literal::Byte(b) => Ok(b),
        }
    }

    fn from_class(class: Class) -> CharacterResult<BTreeSet<Self>> {
        let mut bs = BTreeSet::new();
        match class {
            Class::Unicode(class_unicode) => {
                for class_unicode_range in class_unicode.iter() {
                    let start = class_unicode_range.start();
                    let end = class_unicode_range.end();
                    for c in start..=end {
                        let b = to_byte(c)?;
                        bs.insert(b);
                    }
                }
            }
            Class::Bytes(class_bytes) => {
                for class_bytes_range in class_bytes.iter() {
                    let start = class_bytes_range.start();
                    let end = class_bytes_range.end();
                    for b in start..=end {
                        bs.insert(b);
                    }
                }
            }
        }
        Ok(bs)
    }

    fn to_byte(&self) -> Option<u8> {
        Some(*self)
    }

    fn is_byte(&self) -> bool {
        true
    }

    fn is_next(&self, other: &u8) -> bool {
        if let Some(next) = other.checked_add(1) {
            next == *other
        } else {
            false
        }
    }

    fn get_iterator_function(_is_byte: bool) -> Ident {
        Ident::new("into_iter", Span::call_site())
    }

    fn to_usize(element: Ident, _is_byte: bool) -> TokenStream {
        quote! {
            *#element as usize
        }
    }

    fn allow_invalid_utf8() -> bool {
        true
    }

    fn unicode() -> bool {
        false
    }
}