Macro Implementation
Relevant source files
This document covers the technical implementation details of the procedural macros in the axconfig-macros
crate, including their integration with the core axconfig-gen
library. It explains how the macros process TOML configurations at compile time and generate Rust code using the proc-macro infrastructure.
For information about how to use these macros in practice, see Macro Usage Patterns. For details about the underlying configuration processing logic, see Library API.
Procedural Macro Architecture
The axconfig-macros
crate implements two procedural macros that provide compile-time TOML configuration processing by leveraging the core functionality from axconfig-gen
. The architecture separates parsing and code generation concerns from the macro expansion logic.
flowchart TD subgraph subGraph3["File System Operations"] ENV_VAR_RESOLUTION["Environment VariableResolution"] FILE_READING["std::fs::read_to_stringTOML file loading"] PATH_RESOLUTION["CARGO_MANIFEST_DIRPath resolution"] end subgraph subGraph2["Proc-Macro Infrastructure"] TOKEN_STREAM["TokenStreamInput parsing"] SYN_PARSE["syn::parseSyntax tree parsing"] QUOTE_GEN["quote!Code generation"] COMPILE_ERROR["Error::to_compile_errorCompilation errors"] end subgraph subGraph1["Core Processing (axconfig-gen)"] CONFIG_FROM_TOML["Config::from_tomlTOML parsing"] CONFIG_DUMP["Config::dumpOutputFormat::Rust"] RUST_CODE_GEN["Rust Code Generationpub const, pub mod"] end subgraph subGraph0["Compile-Time Processing"] USER_CODE["User Rust Codeparse_configs! / include_configs!"] MACRO_INVOKE["Macro InvocationTokenStream input"] MACRO_IMPL["Macro Implementationlib.rs"] end COMPILE_ERROR --> USER_CODE CONFIG_DUMP --> RUST_CODE_GEN CONFIG_FROM_TOML --> CONFIG_DUMP ENV_VAR_RESOLUTION --> PATH_RESOLUTION FILE_READING --> CONFIG_FROM_TOML MACRO_IMPL --> COMPILE_ERROR MACRO_IMPL --> ENV_VAR_RESOLUTION MACRO_IMPL --> FILE_READING MACRO_IMPL --> TOKEN_STREAM MACRO_INVOKE --> MACRO_IMPL QUOTE_GEN --> USER_CODE RUST_CODE_GEN --> QUOTE_GEN SYN_PARSE --> CONFIG_FROM_TOML TOKEN_STREAM --> SYN_PARSE USER_CODE --> MACRO_INVOKE
Sources: axconfig-macros/src/lib.rs(L1 - L144)
Macro Implementation Details
parse_configs! Macro
The parse_configs!
macro processes inline TOML strings and expands them into Rust code at compile time. It handles both regular compilation and nightly compiler features for enhanced expression expansion.
flowchart TD INPUT_TOKENS["TokenStreamTOML string literal"] NIGHTLY_EXPAND["proc_macro::expand_exprNightly feature"] PARSE_LITSTR["parse_macro_input!as LitStr"] EXTRACT_VALUE["LitStr::valueExtract TOML content"] CONFIG_PARSE["Config::from_tomlParse TOML structure"] CONFIG_DUMP["Config::dumpOutputFormat::Rust"] TOKEN_PARSE["code.parseString to TokenStream"] ERROR_HANDLING["compiler_errorLexError handling"] FINAL_TOKENS["TokenStreamGenerated Rust code"] CONFIG_DUMP --> ERROR_HANDLING CONFIG_DUMP --> TOKEN_PARSE CONFIG_PARSE --> CONFIG_DUMP CONFIG_PARSE --> ERROR_HANDLING ERROR_HANDLING --> FINAL_TOKENS EXTRACT_VALUE --> CONFIG_PARSE INPUT_TOKENS --> NIGHTLY_EXPAND INPUT_TOKENS --> PARSE_LITSTR NIGHTLY_EXPAND --> PARSE_LITSTR PARSE_LITSTR --> EXTRACT_VALUE TOKEN_PARSE --> ERROR_HANDLING TOKEN_PARSE --> FINAL_TOKENS
The implementation includes conditional compilation for nightly features:
Feature | Functionality | Implementation |
---|---|---|
Nightly | Enhanced expression expansion | config_toml.expand_expr() |
Stable | Standard literal parsing | parse_macro_input!(config_toml as LitStr) |
Error Handling | Compilation error generation | compiler_error()function |
Sources: axconfig-macros/src/lib.rs(L22 - L41)
include_configs! Macro
The include_configs!
macro supports three different path specification methods and handles file system operations with comprehensive error handling.
flowchart TD subgraph subGraph2["File Processing"] FILE_READ["std::fs::read_to_stringTOML file reading"] PARSE_CONFIGS_CALL["parse_configs!Recursive macro call"] QUOTE_MACRO["quote!Token generation"] end subgraph subGraph1["Path Resolution"] ENV_VAR_LOOKUP["std::env::varEnvironment variable lookup"] FALLBACK_LOGIC["Fallback pathhandling"] CARGO_MANIFEST["CARGO_MANIFEST_DIRRoot directory resolution"] PATH_JOIN["std::path::Path::joinFull path construction"] end subgraph subGraph0["Argument Parsing"] ARGS_INPUT["TokenStreamMacro arguments"] PARSE_ARGS["IncludeConfigsArgs::parseCustom argument parser"] PATH_DIRECT["Path variantDirect file path"] PATH_ENV["PathEnv variantEnvironment variable"] PATH_ENV_FALLBACK["PathEnvFallback variantEnv var + fallback"] end ARGS_INPUT --> PARSE_ARGS CARGO_MANIFEST --> PATH_JOIN ENV_VAR_LOOKUP --> FALLBACK_LOGIC FALLBACK_LOGIC --> CARGO_MANIFEST FILE_READ --> PARSE_CONFIGS_CALL PARSE_ARGS --> PATH_DIRECT PARSE_ARGS --> PATH_ENV PARSE_ARGS --> PATH_ENV_FALLBACK PARSE_CONFIGS_CALL --> QUOTE_MACRO PATH_DIRECT --> CARGO_MANIFEST PATH_ENV --> ENV_VAR_LOOKUP PATH_ENV_FALLBACK --> ENV_VAR_LOOKUP PATH_JOIN --> FILE_READ
Sources: axconfig-macros/src/lib.rs(L58 - L87)
Argument Parsing Implementation
The IncludeConfigsArgs
enum and its Parse
implementation handle the complex argument parsing for the include_configs!
macro with proper error reporting.
stateDiagram-v2 [*] --> CheckFirstToken CheckFirstToken --> DirectPath : LitStr CheckFirstToken --> ParseParameters : Ident DirectPath --> [*] : Path(LitStr) ParseParameters --> ReadIdent ReadIdent --> CheckEquals : ident CheckEquals --> ReadString : = ReadString --> ProcessParameter : LitStr ProcessParameter --> SetPathEnv : "path_env" ProcessParameter --> SetFallback : "fallback" ProcessParameter --> Error : unknown parameter SetPathEnv --> CheckComma SetFallback --> CheckComma CheckComma --> ReadIdent : more tokens CheckComma --> ValidateResult : end of input ValidateResult --> PathEnvOnly : env only ValidateResult --> PathEnvWithFallback : env + fallback ValidateResult --> Error : invalid combination PathEnvOnly --> [*] : PathEnv(LitStr) PathEnvWithFallback --> [*] : PathEnvFallback(LitStr, LitStr) Error --> [*] : ParseError
The parsing logic handles these parameter combinations:
Syntax | Enum Variant | Behavior |
---|---|---|
"path/to/file.toml" | Path(LitStr) | Direct file path |
path_env = "ENV_VAR" | PathEnv(LitStr) | Environment variable only |
path_env = "ENV_VAR", fallback = "default.toml" | PathEnvFallback(LitStr, LitStr) | Environment variable with fallback |
Sources: axconfig-macros/src/lib.rs(L89 - L143)
Error Handling and Compilation
The macro implementation includes comprehensive error handling that generates meaningful compilation errors for various failure scenarios.
Compiler Error Generation
The compiler_error
function provides a centralized mechanism for generating compilation errors that integrate properly with the Rust compiler's diagnostic system.
flowchart TD subgraph subGraph2["Compiler Integration"] RUST_COMPILER["Rust CompilerError reporting"] BUILD_FAILURE["Build FailureClear error messages"] end subgraph subGraph1["Error Processing"] COMPILER_ERROR_FN["compiler_errorError::new_spanned"] TO_COMPILE_ERROR["Error::to_compile_errorTokenStream generation"] ERROR_SPAN["Span informationSource location"] end subgraph subGraph0["Error Sources"] TOML_PARSE_ERROR["TOML Parse ErrorConfig::from_toml"] FILE_READ_ERROR["File Read Errorstd::fs::read_to_string"] ENV_VAR_ERROR["Environment Variable Errorstd::env::var"] LEX_ERROR["Lexical ErrorTokenStream parsing"] SYNTAX_ERROR["Syntax ErrorArgument parsing"] end COMPILER_ERROR_FN --> ERROR_SPAN COMPILER_ERROR_FN --> TO_COMPILE_ERROR ENV_VAR_ERROR --> COMPILER_ERROR_FN FILE_READ_ERROR --> COMPILER_ERROR_FN LEX_ERROR --> COMPILER_ERROR_FN RUST_COMPILER --> BUILD_FAILURE SYNTAX_ERROR --> COMPILER_ERROR_FN TOML_PARSE_ERROR --> COMPILER_ERROR_FN TO_COMPILE_ERROR --> RUST_COMPILER
Sources: axconfig-macros/src/lib.rs(L12 - L14) axconfig-macros/src/lib.rs(L36 - L40) axconfig-macros/src/lib.rs(L63 - L67) axconfig-macros/src/lib.rs(L79 - L81)
Integration with Build System
The macros integrate with Cargo's build system through environment variable resolution and dependency tracking that ensures proper rebuilds when configuration files change.
Build-Time Path Resolution
The implementation uses CARGO_MANIFEST_DIR
to resolve relative paths consistently across different build environments:
Environment Variable | Purpose | Usage |
---|---|---|
CARGO_MANIFEST_DIR | Project root directory | Path resolution base |
User-defined env vars | Config file paths | Dynamic path specification |
Dependency Tracking
The file-based operations in include_configs!
automatically create implicit dependencies that Cargo uses for rebuild detection:
flowchart TD subgraph subGraph2["Build Outputs"] COMPILED_BINARY["Compiled binarywith embedded config"] BUILD_CACHE["Build cacheDependency information"] end subgraph subGraph1["Build Process"] MACRO_EXPANSION["Macro expansionFile read operation"] CARGO_TRACKING["Cargo dependency trackingImplicit file dependency"] REBUILD_DETECTION["Rebuild detectionFile modification time"] end subgraph subGraph0["Source Files"] RUST_SOURCE["Rust source filewith include_configs!"] TOML_CONFIG["TOML config filereferenced by macro"] end CARGO_TRACKING --> BUILD_CACHE CARGO_TRACKING --> REBUILD_DETECTION MACRO_EXPANSION --> CARGO_TRACKING REBUILD_DETECTION --> COMPILED_BINARY RUST_SOURCE --> MACRO_EXPANSION TOML_CONFIG --> MACRO_EXPANSION TOML_CONFIG --> REBUILD_DETECTION
Sources: axconfig-macros/src/lib.rs(L76 - L77) axconfig-macros/tests/example_config.rs(L4 - L5)
Testing Integration
The macro implementation includes comprehensive testing that validates both the generated code and the macro expansion process itself.
The test structure demonstrates proper integration between the macros and expected outputs:
flowchart TD subgraph subGraph2["Test Execution"] MOD_CMP_MACRO["mod_cmp! macroModule comparison"] ASSERT_STATEMENTS["assert_eq! callsValue validation"] TEST_FUNCTIONS["test functionsinclude and parse tests"] end subgraph subGraph1["Test Data"] DEFCONFIG_TOML["defconfig.tomlTest input"] OUTPUT_RS["output.rsExpected generated code"] INCLUDE_STR["include_str!String inclusion"] end subgraph subGraph0["Test Configuration"] TEST_FILE["example_config.rsTest definitions"] CONFIG_MODULE["config moduleinclude_configs! usage"] CONFIG2_MODULE["config2 moduleparse_configs! usage"] EXPECTED_MODULE["config_expect moduleExpected output"] end ASSERT_STATEMENTS --> CONFIG2_MODULE ASSERT_STATEMENTS --> CONFIG_MODULE ASSERT_STATEMENTS --> EXPECTED_MODULE CONFIG2_MODULE --> INCLUDE_STR CONFIG_MODULE --> DEFCONFIG_TOML EXPECTED_MODULE --> OUTPUT_RS INCLUDE_STR --> DEFCONFIG_TOML MOD_CMP_MACRO --> ASSERT_STATEMENTS TEST_FUNCTIONS --> MOD_CMP_MACRO