maxlen_macro/
lib.rs

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		// Look ahead to determine if encoding specified
27		let lookahead = input.lookahead1();
28		let encoding = if lookahead.peek(Ident) {
29			// with encoding
30			let ident: Ident = input.parse()?;
31			input.parse::<Token![,]>()?;
32			ident
33		} else if lookahead.peek(LitStr) {
34			// no encoding
35			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}