1use encoding::Encoding;
2use proc_macro::TokenStream;
3use proc_macro_error::{abort_call_site, proc_macro_error};
4use proc_macro2::Span;
5use quote::quote;
6use syn::{
7 Ident, LitInt, LitStr, Token,
8 parse::{Parse, ParseStream},
9 parse_macro_input,
10};
11
12mod encoding;
13
14struct BStrInput {
15 max: usize,
16 encoding: Ident,
17 str: LitStr,
18}
19
20impl Parse for BStrInput {
21 fn parse(input: ParseStream) -> syn::Result<Self> {
22 let max: LitInt = input.parse()?;
23 let max = max.base10_parse()?;
24 input.parse::<Token![,]>()?;
25
26 let lookahead = input.lookahead1();
28 let encoding = if lookahead.peek(Ident) {
29 let ident: Ident = input.parse()?;
31 input.parse::<Token![,]>()?;
32 ident
33 } else if lookahead.peek(LitStr) {
34 Ident::new("Utf8", Span::mixed_site())
36 } else {
37 return Err(lookahead.error());
38 };
39
40 let str: LitStr = input.parse()?;
41
42 Ok(Self { max, encoding, str })
43 }
44}
45
46#[proc_macro_error]
47#[proc_macro]
48pub fn bstr(input: TokenStream) -> TokenStream {
49 let input = parse_macro_input!(input as BStrInput);
50
51 let length = match input.encoding.to_string().as_str() {
52 "Utf8" => encoding::Utf8::length,
53 "Cesu8" => encoding::Cesu8::length,
54 "MCesu8" => encoding::MCesu8::length,
55 other => {
56 abort_call_site!("Unknown encoding {:?}", other);
57 }
58 };
59
60 if length(&input.str.value()) > input.max {
61 abort_call_site!("Length exceeded! Max length {}", input.max);
62 }
63
64 let str = input.str;
65 let encoding = input.encoding;
66 let max = input.max;
67 quote! {
68 unsafe {
69 ::maxlen::BStr::<#max, ::maxlen::encoding::#encoding>::from_str_unchecked(#str)
70 }
71 }
72 .into()
73}