expanda-macros 0.3.0

macros for expanda crate
Documentation
use std::{
    ffi::CString,
    num::ParseIntError,
};

use proc_macro::{Ident, Literal, Span};

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum StringType {
    String,
    ByteString,
    CString,
}

impl StringType {
    pub fn make_literal(self, string: &str) -> Literal {
        match self {
            StringType::String => Literal::string(string),
            StringType::ByteString => Literal::byte_string(string.as_bytes()),
            StringType::CString => Literal::c_string(&CString::new(string).unwrap()),
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum LitKind {
    Char,
    String,
    RawString(u8),
    ByteChar,
    ByteString,
    RawByteString(u8),
    CString,
    RawCString(u8),
    Number,
    Unknown,
}

impl LitKind {
    pub fn of(lit: &Literal) -> Self {
        let s = lit.to_string();
        let mut s = s.chars();
        let Some(c) = s.next() else { return Self::Unknown };
        match c {
            '\'' => Self::Char,
            '"' => Self::String,
            'r' => Self::RawString(s.take_while(|&c| c == '#').count() as _),
            'b' => {
                let Some(c) = s.next() else { return Self::Unknown };
                match c {
                    '\'' => Self::ByteChar,
                    '"' => Self::ByteString,
                    'r' => Self::RawByteString(s.take_while(|&c| c == '#').count() as _),
                    _ => Self::Unknown,
                }
            },
            'c' => {
                let Some(c) = s.next() else { return Self::Unknown };
                match c {
                    '"' => Self::CString,
                    'r' => Self::RawCString(s.take_while(|&c| c == '#').count() as _),
                    _ => Self::Unknown,
                }
            }
            '0'..='9' => Self::Number,
            _ => Self::Unknown,
        }
    }

    pub fn string_type(self) -> Option<StringType> {
        Some(match self {
            LitKind::String | LitKind::RawString(_) => StringType::String,
            LitKind::ByteString | LitKind::RawByteString(_) => StringType::ByteString,
            LitKind::CString | LitKind::RawCString(_) => StringType::CString,
            _ => return None
        })
    }

    pub fn is_raw(self) -> bool {
        matches!(self, LitKind::RawString(_) | LitKind::RawCString(_) | LitKind::RawByteString(_))
    }
}

pub fn parse_string(lit: &str) -> (&str, &str) {
    let i = lit.bytes().enumerate().find(|&(_, c)| c == b'"').unwrap().0;
    let j = lit.bytes().enumerate().rev().find(|&(_, c)| c == b'"').unwrap().0;
    let k = lit.bytes().enumerate().rev().find(|&(_, c)| matches!(c, b'"' | b'#')).unwrap().0;
    (&lit[i + 1..j], &lit[k + 1..])
}

pub fn unescape(str: &str) -> String {
    let mut chars = str.chars();
    let mut result = String::new();
    while let Some(char) = chars.next() {
        if char != '\\' {
            result.push(char);
            continue;
        }
        result.push(match chars.next().unwrap() {
            '\\' => '\\',
            '\'' => '\'',
            '"' => '"',
            '0' => '\0',
            'n' => '\n',
            'r' => '\r',
            't' => '\t',
            'x' => {
                u8::from_str_radix(&chars.by_ref().take(2).collect::<String>(), 0x10).unwrap().into()
            }
            'u' => {
                chars.next();
                u32::from_str_radix(&chars.by_ref().take_while(|&c| c != '}').collect::<String>(), 0x10).unwrap().try_into().unwrap()
            }
            _ => panic!(),
        })
    }
    result
}

pub fn parse_int(lit: &str) -> Result<u64, ParseIntError> {
    u64::from_str_radix(
        lit, 
        match lit.get(0..2) {
            Some("0x") => 16,
            Some("0b") => 2,
            Some("0o") => 8,
            _ => 10,
        },
    )
}

pub fn make_ident(string: &str, span: Span) -> Ident {
    match string {
        "as" | "async" | "await" | "break" | "const" | "continue" |
        "dyn" | "else" | "enum" | "extern" | "false" | "fn" | "for" |
        "if" | "impl" | "in" | "let" | "loop" | "match" | "mod" | "move" |
        "mut" | "pub" | "ref" | "return" | "static" | "struct" | "trait" |
        "true" | "type" | "unsafe" | "use" | "where" | "while" => Ident::new_raw(string, span),
        _ => Ident::new(string, span),
    }
}