include_mdtests/
lib.rs
1use proc_macro2::Span;
3use quote::quote;
4use syn::{Ident, LitStr, parse_macro_input};
5
6#[doc = include_str!("../README.md")]
7#[proc_macro]
8pub fn include_mdtests(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
9 let current_dir = std::env::current_dir().unwrap();
10 let input_glob = parse_macro_input!(input as LitStr);
11
12 let doc_mods = glob::glob(input_glob.value().as_str())
13 .expect("Failed to read glob pattern")
14 .map(|entry| entry.expect("Failed to read glob entry"))
15 .map(|path| {
16 let path_lit_str = {
17 let path_abs = current_dir.join(path.clone());
18 let path_abs_str = path_abs.to_str().expect("Failed to convert path to string");
19 LitStr::new(path_abs_str, Span::call_site())
20 };
21
22 let mod_ident = {
23 let mut ident_string = path
24 .to_string_lossy()
25 .chars()
26 .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
27 .collect::<String>();
28 if ident_string
29 .chars()
30 .next()
31 .is_none_or(|c| c.is_ascii_digit())
32 {
33 ident_string.insert(0, '_');
35 }
36 Ident::new(&ident_string, Span::call_site())
37 };
38
39 quote! {
40 #[doc = include_str!(#path_lit_str)]
41 mod #mod_ident {}
42 }
43 });
44
45 let out = quote! {
46 #( #doc_mods )*
47 };
48 out.into()
49}