goglob_proc_macro/
lib.rs

1//! # Do not use this library directly!
2//!
3//! See the `goglob` crate instead.
4//!
5//! A substantial portion of the token parsing done in this crate is
6//! based on the [`cstr!()` procedural macro](https://crates.io/crates/cstr)
7//! (available under the MIT License).
8
9// Note from cstr:
10// While this isn't necessary when using Cargo >= 1.42, omitting it actually requires path-less
11// `--extern proc_macro` to be passed to `rustc` when building this crate. Some tools may not do
12// this correctly. So it's added as a precaution.
13extern crate proc_macro;
14
15mod parse;
16mod stream;
17
18use goglob_common::{
19    error::Error as GlobTokenError, literal::Literal as GlobTokenLiteral, scan_patterns, GlobToken,
20};
21use proc_macro::TokenStream as RawTokenStream;
22use proc_macro2::{Span, TokenStream};
23use quote::quote_spanned;
24
25pub(crate) mod internal {
26    pub use goglob_common::*;
27}
28
29pub(crate) enum Error {
30    GlobTokenError(Span, GlobTokenError),
31    ParseError(parse::ParseError),
32}
33impl From<parse::ParseError> for Error {
34    fn from(pe: parse::ParseError) -> Self {
35        Self::ParseError(pe)
36    }
37}
38
39/// Compile the given `pattern` into tokens at code-compile time, emitting a
40/// `GlobPattern` on success or a compile-error if `pattern` is syntactically
41/// invalid.
42///
43/// This is useful in contexts when the pattern is a known constant and can thus
44/// be declared as such:
45///
46/// ```no_compile
47/// const MY_PATTERN: GlobPattern = glob!("a*b*c*d*e*/f");
48/// ```
49///
50/// That way, there is no runtime penalty when compiling the pattern for the first
51/// time as it will be pre-compiled into the resulting binary.
52///
53/// # Further reading
54///
55/// See the `goglob` crate's documentation for the appropriate syntax, as well
56/// as [goglob::error::Error] for possible syntax errors.
57#[proc_macro]
58pub fn glob(lit: RawTokenStream) -> RawTokenStream {
59    let mut glob_tokens = Vec::new();
60    let result_tokens = if let Err(e) = glob_tokens_from(lit.into(), &mut glob_tokens) {
61        match e {
62            Error::GlobTokenError(span, gte) => {
63                let gte = format!("pattern malformed: {}", gte);
64                quote_spanned!(span => compile_error!(#gte))
65            }
66            Error::ParseError(parse::ParseError(span, msg)) => quote_spanned!(
67                span => compile_error!(#msg)
68            ),
69        }
70    } else {
71        stream::glob_tokens_into_stream(glob_tokens)
72    };
73    result_tokens.into()
74}
75
76fn glob_tokens_from(lit: TokenStream, glob_tokens: &mut Vec<GlobToken>) -> Result<(), Error> {
77    let (pattern, span) = parse::parse_input(lit)?;
78    scan_patterns(&*pattern, glob_tokens).map_err(|gte| Error::GlobTokenError(span, gte))
79}