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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
//! # List-Modules Procedural Macro
//!
//! This macro creates a constant string slice list of all the module names
//! which are children of an indicated crate module folder. Paths are specified
//! relative to the cargo manifest directory.
//!
//! For example, calling this macro from `mod.rs` in the following file tree
//! with `list_modules::here!("parent/");`...
//!
//! ```none
//! parent/
//! mod.rs
//! child_1.rs
//! child_2/
//! mod.rs
//! internal.rs
//! other_internal/
//! ...
//! ...
//! child_3.rs
//! child_4.rs
//! ...
//! child_N/
//! mod.rs
//! ```
//!
//! ...will result in the following list expansion:
//!
//! ```rust
//! pub const LIST: [&str; N] = [
//! "child_1",
//! "child_2",
//! "child_3",
//! ...
//! "child_N",
//! ];
//! ```
//!
//! Note that this is the only guaranteed behavior.
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use std::env;
use std::fs;
/// # List-Modules Procedural Macro
///
/// This macro creates a constant string slice list of all the module names
/// which are children of an indicated crate module folder. Paths are specified
/// relative to the cargo manifest directory.
///
/// For example, calling this macro from `mod.rs` in the following file tree
/// with `list_modules::here!("parent/");`...
///
/// ```none
/// parent/
/// mod.rs
/// child_1.rs
/// child_2/
/// mod.rs
/// internal.rs
/// other_internal/
/// ...
/// ...
/// child_3.rs
/// child_4.rs
/// ...
/// child_N/
/// mod.rs
/// ```
///
/// ...will result in the following list expansion:
///
/// ```rust
/// pub const LIST: [&str; N] = [
/// "child_1",
/// "child_2",
/// "child_3",
/// ...
/// "child_N",
/// ];
/// ```
///
/// Note that this is the only guaranteed behavior.
#[proc_macro]
pub fn here(__input: TokenStream) -> TokenStream {
// Get the absolute path of the directory where the macro was called from
let __manifest_dir =
env::var("CARGO_MANIFEST_DIR").expect("Failed to get Cargo manifest directory");
let mut __macro_call_path =
fs::canonicalize(__manifest_dir).expect("Failed to canonicalize Cargo manifest directory");
__macro_call_path.push(__input.to_string().trim_matches('"'));
// Collect the module names by iterating over the entries in the full base directory
let __internal_module_names: Vec<String> = fs::read_dir(__macro_call_path)
.expect("Failed to read directory")
.filter_map(|entry| {
if let Ok(entry) = entry {
if let Some(entry_name) = entry.file_name().to_str() {
if entry_name.ends_with(".rs") && entry_name != "mod.rs" {
return Some(entry_name[..entry_name.len() - 3].to_owned());
} else if entry_name != "mod.rs" {
return Some(entry_name.to_owned());
}
}
}
None
})
.collect();
// Generate array of module names
let __internal_module_array = quote! {
[
#(#__internal_module_names),*
]
};
// Get number of iterms in array to later insert them
let __number_of_modules_in_array = __internal_module_names.len();
// Generate the static list of string slices with the custom list name
let __internal_macro_output: proc_macro2::TokenStream = quote! {
pub const LIST: [&str; #__number_of_modules_in_array] = #__internal_module_array;
};
__internal_macro_output.into()
}