1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#![forbid(unsafe_code)]
#![deny(missing_docs, clippy::all, clippy::cargo)]
#![allow(
clippy::missing_docs_in_private_items,
clippy::missing_inline_in_public_items,
clippy::implicit_return
)]
#[allow(unused_macros)]
macro_rules! emit_error {
($token:expr, $msg: expr) => {
return TokenStream::from(syn::Error::new($token.span(), $msg).to_compile_error())
};
}
mod case;
mod fmt;
#[cfg(feature = "regex")]
mod verify_regex;
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::parse::Parse;
use syn::spanned::Spanned;
use syn::{parse_macro_input, LitByteStr, LitStr};
fn direct_convert<T, E, F>(input: TokenStream, f: F) -> TokenStream
where
T: Parse + Spanned,
E: ToString,
F: FnOnce(&T) -> Result<String, E>,
{
let src_token: T = parse_macro_input!(input as T);
let s = match f(&src_token) {
Ok(s) => s,
Err(e) => emit_error!(src_token, e.to_string()),
};
let dst_token = LitStr::new(&s, src_token.span());
dst_token.into_token_stream().into()
}
#[proc_macro]
pub fn from_utf8(input: TokenStream) -> TokenStream {
direct_convert(input, |src_token: &LitByteStr| {
let src = src_token.value();
let err_msg = "the byte string literal is not a valid UTF-8 string";
String::from_utf8(src).map_err(|_| err_msg)
})
}
#[proc_macro]
pub fn format(input: TokenStream) -> TokenStream {
use crate::fmt::ConstFormat;
let m = parse_macro_input!(input as ConstFormat);
m.eval()
}
#[proc_macro]
pub fn convert_case(input: TokenStream) -> TokenStream {
use crate::case::ConvertCase;
let m = parse_macro_input!(input as ConvertCase);
m.eval()
}
#[cfg(feature = "http")]
#[proc_macro]
pub fn verified_header_name(input: TokenStream) -> TokenStream {
use http::header::HeaderName;
direct_convert(input, |s: &LitStr| {
let s = s.value();
HeaderName::from_lowercase(s.as_bytes()).map(|_| s)
})
}
#[cfg(feature = "regex")]
#[proc_macro]
pub fn verified_regex(input: TokenStream) -> TokenStream {
direct_convert(input, |s: &LitStr| {
let s = s.value();
regex::Regex::new(&s).map(|_| s)
})
}
#[cfg(feature = "regex")]
#[proc_macro]
pub fn regex_assert_match(input: TokenStream) -> TokenStream {
use crate::verify_regex::RegexAssertMatch;
let m = parse_macro_input!(input as RegexAssertMatch);
m.eval()
}