visa-rs-proc 0.7.0

procedural macros used in visa-rs
Documentation
//#![feature(proc_macro_diagnostic)]
use std::str::FromStr;

use proc_macro::TokenStream;
use proc_macro2::{Delimiter, TokenStream as TokenStream2, TokenTree};
use quote::quote;
use syn::{
    parse::{Parse, ParseStream},
    parse_macro_input, Ident, Path, Result, Token,
};

mod repr;
mod rusty_ident;

fn screaming_snake_case_to_pascal_case(input: &str) -> String {
    input
        .split('_')
        .map(|x| x[..1].to_owned() + &x[1..].to_ascii_lowercase())
        .collect()
}

fn subst_ident(t: Ident) -> Ident {
    if let Some(new_id) = t.to_string().strip_prefix("VI_") {
        Ident::new(&screaming_snake_case_to_pascal_case(new_id), t.span())
    } else {
        t
    }
}
#[proc_macro]
pub fn rusty_ident(input: TokenStream) -> TokenStream {
    let macros = parse_macro_input!(input as rusty_ident::Macros);
    quote! {#macros}.into()
}

#[proc_macro]
pub fn repr(input: TokenStream) -> TokenStream {
    let input2: TokenStream2 = input.into();
    match syn::parse2::<repr::Input>(input2) {
        Ok(macros) => quote! {#macros}.into(),
        Err(err) => err.to_compile_error().into(),
    }
}

fn get_visa_num(input: TokenTree) -> TokenTree {
    fn parse_to_u64(s: &str) -> std::result::Result<u64, std::num::ParseIntError> {
        u64::from_str_radix(s, 16)
    }
    fn u64_to_lit(n: u64) -> String {
        format!("{:#010X}", n)
    }
    match input {
        TokenTree::Group(g) => {
            let mut ret = proc_macro2::Group::new(
                g.delimiter(),
                g.stream().into_iter().map(get_visa_num).collect(),
            );
            ret.set_span(g.span());
            TokenTree::Group(ret)
        }
        TokenTree::Ident(id) => {
            if let Some(Ok(d)) = id.to_string().strip_suffix('h').map(parse_to_u64) {
                let mut ret = proc_macro2::Literal::from_str(&u64_to_lit(d)).unwrap();
                ret.set_span(id.span());
                TokenTree::Literal(ret)
            } else {
                TokenTree::Ident(id)
            }
        }
        TokenTree::Literal(lit) => {
            if let Some(Ok(d)) = lit.to_string().strip_suffix('h').map(parse_to_u64) {
                let mut ret = proc_macro2::Literal::from_str(&u64_to_lit(d)).unwrap();
                ret.set_span(lit.span());
                TokenTree::Literal(ret)
            } else {
                TokenTree::Literal(lit)
            }
        }
        _ => input,
    }
}

#[test]
fn test_visa_num() {
    let stream = TokenStream2::from_str(r#"FFFFFFFFh,{Ah,0h,-1},0000001h"#).unwrap();
    let stream: TokenStream2 = stream.into_iter().map(get_visa_num).collect();
    assert_eq!(
        stream.to_string(),
        r#"0xFFFFFFFF , { 0x0000000A , 0x00000000 ,- 1 } , 0x00000001"#
    );
}

fn match_tokens(input: ParseStream, str: &str) -> Option<proc_macro2::Span> {
    let stream: TokenStream2 = syn::parse_str(str).unwrap();
    let fork = input.fork();
    for token in stream {
        if token.to_string() != fork.parse::<TokenTree>().unwrap().to_string() {
            return None;
        }
    }
    use syn::parse::discouraged::Speculative;
    let start = input.span();
    input.advance_to(&fork);
    let end = input.span();

    Some(start.join(end).unwrap_or(start))
}
mod attrs;

#[proc_macro]
pub fn visa_attrs(input: TokenStream) -> TokenStream {
    let input: TokenStream2 = input.into();
    let input: TokenStream2 = input.into_iter().map(get_visa_num).collect();
    let input: TokenStream = input.into();
    let attrs = parse_macro_input!(input as attrs::Attributes);
    quote! {#attrs}.into()
}

struct OneLayer {
    mac: Path,
    _exc: Token![!],
    body: Body,
}

impl Parse for OneLayer {
    fn parse(input: ParseStream) -> Result<Self> {
        Ok(Self {
            mac: input.call(Path::parse_mod_style)?,
            _exc: input.parse()?,
            body: input.parse()?,
        })
    }
}

struct Body {
    _delim: Delimiter,
    content: TokenStream2,
}

impl Parse for Body {
    fn parse(input: ParseStream) -> Result<Self> {
        let g: proc_macro2::Group = input.parse()?;
        Ok(Self {
            _delim: g.delimiter(),
            content: g.stream(),
        })
    }
}