const_str_proc_macro/
lib.rs

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