#![forbid(unsafe_code)]
#![forbid(non_fmt_panics)]
#![deny(missing_docs)]
#![deny(rustdoc::missing_crate_level_docs)]
#![warn(rustdoc::missing_doc_code_examples)]
#![deny(rustdoc::invalid_codeblock_attributes)]
#![warn(rustdoc::broken_intra_doc_links)]
#![warn(rustdoc::private_intra_doc_links)]
#![warn(rustdoc::unescaped_backticks)]
#![allow(rustdoc::private_doc_tests)]
#![allow(unused_parens)]
#![allow(non_camel_case_types)]
extern crate proc_macro;
use proc_macro::TokenStream;
use syn::{parse_macro_input, LitByteStr, LitStr};
use syn::parse::{Parse, ParseStream};
use quote::quote;
use std::fmt;
#[derive(Debug)]
struct MacroArgumentsAR {
id: String,
content: StrOrByte,
len: Option<usize>
}
impl Parse for MacroArgumentsAR {
fn parse(input: ParseStream) -> syn::Result<Self> {
use syn::Ident;
use syn::Token;
use syn::LitInt;
let ident: Ident = input.parse()?;
let _comma: Token![,] = input.parse()?;
let payload: StrOrByte = input.parse()?;
let maybecomma: Result<Token![,],_> = input.parse();
let maybesize: Result<LitInt,_> = input.parse();
let maybeusizedlen =
if maybesize.is_ok() && maybecomma.is_ok() {
maybesize.unwrap().base10_parse::<usize>()
} else {
Err(syn::Error::new(input.span(), "Failed to parse size as usize"))
};
match( maybeusizedlen ) {
Err(e) =>
if (maybecomma.is_ok()) {
Err(e)
} else {
Ok(MacroArgumentsAR { id: ident.to_string(), content: payload, len: None })
},
Ok(expectedlen) => Ok(MacroArgumentsAR { id: ident.to_string(), content: payload, len: Some(expectedlen) })
}
}
}
#[proc_macro]
pub fn str_array(tokens: TokenStream) -> TokenStream {
let input: MacroArgumentsAR = parse_macro_input!(tokens as MacroArgumentsAR);
let id = syn::Ident::new(&input.id, proc_macro2::Span::from(proc_macro::Span::call_site()));
let u8s = input.content.as_bytes();
let mut len = u8s.len();
if let Some(expectedlen) = input.len {
len = expectedlen;
};
proc_macro::TokenStream::from(
quote! {
const #id: [u8; #len] = [ #(#u8s),* ];
})
}
#[proc_macro]
pub fn str_array0(tokens: TokenStream) -> TokenStream {
let input: MacroArgumentsAR = parse_macro_input!(tokens as MacroArgumentsAR);
let id = syn::Ident::new(&input.id, proc_macro::Span::call_site().into());
let u8s = input.content.add0().as_bytes();
let mut len = u8s.len();
if let Some(expectedlen) = input.len {
len = expectedlen;
};
proc_macro::TokenStream::from(
quote! {
const #id: [ u8; #len ] = [ #(#u8s),* ];
})
}
#[proc_macro]
pub fn str_len(tokens: TokenStream) -> TokenStream {
let input = parse_macro_input!(tokens as StrOrByte);
let len = input.as_bytes().len();
proc_macro::TokenStream::from(
quote! {
#len
})
}
#[proc_macro]
pub fn str_len0(tokens: TokenStream) -> TokenStream {
let input = parse_macro_input!(tokens as StrOrByte);
let len = 1 + input.as_bytes().len();
proc_macro::TokenStream::from(
quote! {
#len
})
}
enum StrOrByte {
str(LitStr),
byte(LitByteStr)
}
impl fmt::Debug for StrOrByte {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StrOrByte::str(lit_str) => write!(f, "Str({:?})", lit_str.value()),
StrOrByte::byte(lit_byte_str) => write!(f, "Byte({:?})", std::str::from_utf8(&lit_byte_str.value()).map_err(|_| fmt::Error)?),
}
}
}
impl PartialEq<StrOrByte> for StrOrByte {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(StrOrByte::str(a), StrOrByte::str(b)) => a.value() == b.value(),
(StrOrByte::byte(a), StrOrByte::byte(b)) => a.value() == b.value(),
_ => false,
}
}
}
impl PartialEq<&str> for StrOrByte {
fn eq(&self, other: &&str) -> bool {
match self {
StrOrByte::str(lit_str) => lit_str.value() == *other,
_ => false
}
}
}
impl PartialEq<&[u8]> for StrOrByte {
fn eq(&self, other: &&[u8]) -> bool {
match self {
StrOrByte::byte(lit_byte) => lit_byte.value() == *other,
_ => false
}
}
}
impl Parse for StrOrByte {
fn parse(input: ParseStream) -> syn::Result<Self> {
if input.peek(LitStr) {
let lit_str: LitStr = input.parse()?;
Ok(StrOrByte::str(lit_str))
} else {
let lit_bytestr: LitByteStr = input.parse()?;
Ok(StrOrByte::byte(lit_bytestr))
}
}
}
impl quote::ToTokens for StrOrByte {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self {
StrOrByte::str(lit_str) => lit_str.to_tokens(tokens),
StrOrByte::byte(lit_byte_str) => lit_byte_str.to_tokens(tokens),
}
}
}
impl StrOrByte {
fn repeat(&self, times: usize) -> Self {
match self {
StrOrByte::str(lit_str) => {
let repeated = lit_str.value().repeat(times);
StrOrByte::str(LitStr::new(&repeated, lit_str.span()))
},
StrOrByte::byte(lit_byte_str) => {
let repeated_bytes = lit_byte_str.value().repeat(times);
StrOrByte::byte(LitByteStr::new(&repeated_bytes, lit_byte_str.span()))
},
}
}
fn add0(&self) -> Self {
match self {
StrOrByte::str(lit_str) => {
let with_null = format!("{}\0", lit_str.value());
StrOrByte::str(LitStr::new(&with_null, lit_str.span()))
},
StrOrByte::byte(lit_byte_str) => {
let mut with_null = lit_byte_str.value().clone();
with_null.push(0 as u8);
StrOrByte::byte(LitByteStr::new(&with_null, lit_byte_str.span()))
},
}
}
fn as_bytes(&self) -> Vec<u8> {
match self {
StrOrByte::str(lit_str) => lit_str.value().as_bytes().to_vec(),
StrOrByte::byte(lit_byte_str) => lit_byte_str.value()
}
}
}
#[derive(Debug)]
struct MacroArgumentsRP {
content: StrOrByte,
times: usize
}
impl Parse for MacroArgumentsRP {
fn parse(input: ParseStream) -> syn::Result<Self> {
use syn::Token;
use syn::LitInt;
let payload: StrOrByte = input.parse()?;
let _comma: Token![,] = input.parse()?;
let times: usize = input.parse::<LitInt>()?.base10_parse::<usize>()?;
Ok(MacroArgumentsRP { content: payload, times })
}
}
#[proc_macro]
pub fn str_repeat(tokens: TokenStream) -> TokenStream {
let input: MacroArgumentsRP = parse_macro_input!(tokens as MacroArgumentsRP);
let payload = input.content.repeat(input.times);
proc_macro::TokenStream::from(
quote! {
#payload
})
}
#[proc_macro]
pub fn str_repeat0(tokens: TokenStream) -> TokenStream {
let input: MacroArgumentsRP = parse_macro_input!(tokens as MacroArgumentsRP);
let payload = input.content.repeat(input.times).add0();
proc_macro::TokenStream::from(
quote! {
#payload
})
}
#[proc_macro]
pub fn str_repeat_bytes(tokens: TokenStream) -> TokenStream {
let input: MacroArgumentsRP = parse_macro_input!(tokens as MacroArgumentsRP);
let bytes = input.content.repeat(input.times).as_bytes();
proc_macro::TokenStream::from(
quote! { &[#(#bytes),*] }
)
}
#[proc_macro]
pub fn str_repeat_bytes0(tokens: TokenStream) -> TokenStream {
let input: MacroArgumentsRP = parse_macro_input!(tokens as MacroArgumentsRP);
let bytes = input.content.repeat(input.times).add0().as_bytes();
proc_macro::TokenStream::from(
quote! { &[#(#bytes),*] }
)
}
#[proc_macro]
pub fn str_bytes(tokens: TokenStream) -> TokenStream {
let input = parse_macro_input!(tokens as StrOrByte);
let bytes = input.as_bytes();
proc_macro::TokenStream::from(
quote! { &[#(#bytes),*] }
)
}
#[proc_macro]
pub fn str_bytes0(tokens: TokenStream) -> TokenStream {
let input = parse_macro_input!(tokens as StrOrByte);
let bytes = input.add0().as_bytes();
proc_macro::TokenStream::from(
quote! { &[#(#bytes),*] }
)
}
#[cfg(test)]
#[path= "./macro_tests.rs"]
mod tests;
#[cfg(test)]
#[path= "./macro_parseAR_tests.rs"]
mod ARparsetests;
#[cfg(test)]
#[path= "./macro_parseRP_tests.rs"]
mod RPparsetests;
#[cfg(test)]
#[path= "./macro_strorbyte_tests.rs"]
mod SoBparsetests;
#[cfg(doctest)]
#[path= "./macro_sized_tests.rs"]
mod sizedtests;
#[cfg(doctest)]
#[path= "./macro_0sized_tests.rs"]
mod zerosizedtests;