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}