cirious_codex_derive 0.2.0

Centralized Procedural Macros for the Cirious Codex Ecosystem
Documentation
//! # Cirious Codex Derive
//!
//! The foundational procedural macro engine for the Cirious Codex ecosystem.
//!
//! This crate centralizes all `#[derive(...)]` and attribute macros used across
//! the Cirious workspace. It is designed to provide zero-cost abstractions, strict
//! compile-time validation, and a seamless developer experience (DX).
//!
//! ## Core Features
//!
//! * **Codex CLI**: Generates strict, predictable command-line argument parsers
//!   that enforce clear execution flows and eliminate ambiguous terminal inputs.
//! * **Ecosystem Synergy**: Built to interoperate flawlessly with `cirious_codex_config`
//!   and `cirious_codex_logger`.

#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(missing_docs)]
#![deny(unsafe_code)]
#![deny(clippy::all)]
#![deny(clippy::pedantic)]

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

mod parser;

/// Derives a strict, zero-ambiguity command-line parser for the target struct.
///
/// By deriving `CodexParser`, the annotated struct gains the ability to seamlessly
/// parse standard terminal arguments (`std::env::args()`) directly into its strongly-typed
/// fields. It securely shares iterator state, making it perfect for nested subcommand routing.
///
/// # Attributes
///
/// Parsing behavior must be fine-tuned using the `#[codex(...)]` attribute on struct fields:
///
/// * `aliase = "..."` - Maps the field to a short flag (e.g., `aliase = "p"` becomes `/p`).
/// * `command = "..."` - Maps the field to a full-word flag (e.g., `command = "port"` becomes `/port`).
/// * `default_value = "..."` - Specifies a fallback value if the argument is omitted.
///
/// # Example
///
/// ```rust
/// use cirious_codex_derive::{CodexCommand, CodexParser};
///
/// /// Global arguments shared across all CLI commands.
/// #[derive(Debug, Clone)]
/// pub struct GlobalArgs {
///  /// Enables verbose mode for detailed diagnostic logging.
///  pub verbose: bool,
///
///  /// Optional path to a configuration file.
///  pub config_path: Option<String>,
/// }
///
/// /// A trait defining the contract for any command that utilizes global arguments.
/// pub trait CodexCommand {
///  /// Returns a reference to the global arguments.
///  fn global_args(&self) -> &GlobalArgs;
/// }
///
/// #[derive(CodexParser, CodexCommand, Debug)]
/// pub struct ApplicationArgs {
///     pub global: GlobalArgs,
///     
///     /// Specifies a custom path for the configuration file.
///     #[codex(aliase = "c", command = "config-path")]
///     pub config_path: String,
/// }
/// ```
#[proc_macro_derive(CodexParser, attributes(codex))]
pub fn derive_codex_parser(input: TokenStream) -> TokenStream {
  let ast = parse_macro_input!(input as DeriveInput);
  parser::generate_cli_parser(&ast).unwrap_or_else(|err| err.to_compile_error().into())
}

/// Derives the `CodexCommand` trait for a struct.
///
/// This macro enables the struct to participate in the Cirious Codex CLI lifecycle,
/// providing access to global arguments.
///
/// **⚠️ Architecture Warning:** Currently, this generates the `GlobalArgs` struct and
/// `CodexCommand` trait directly. Deriving this on multiple structs in the same module
/// will cause duplicate definition errors. In the future, these base types should be
/// moved to a common crate.
#[proc_macro_derive(CodexCommand)]
pub fn derive_codex_command(input: TokenStream) -> TokenStream {
  let ast = parse_macro_input!(input as DeriveInput);
  let name = &ast.ident;

  let expanded = quote! {
    impl CodexCommand for #name {
      fn global_args(&self) -> &GlobalArgs {
        &self.global
      }
    }
  };

  expanded.into()
}

/// Derives subcommand routing logic for an `Enum`.
///
/// The `CodexSubcommand` derive macro automatically converts each variant of the enum
/// into a CLI command. Variant names are converted to `kebab-case` to adhere to standard
/// CLI naming conventions (e.g., `BuildApp` becomes `build-app`).
///
/// ### Usage Example
///
/// ```rust,no_run
/// use cirious_codex_derive::{CodexCommand, CodexSubcommand, CodexParser};
///
/// /// Global arguments shared across all CLI commands.
/// #[derive(Debug, Clone)]
/// pub struct GlobalArgs {
///  /// Enables verbose mode for detailed diagnostic logging.
///  pub verbose: bool,
///
///  /// Optional path to a configuration file.
///  pub config_path: Option<String>,
/// }
///
/// /// A trait defining the contract for any command that utilizes global arguments.
/// pub trait CodexCommand {
///  /// Returns a reference to the global arguments.
///  fn global_args(&self) -> &GlobalArgs;
/// }
///
///
/// /// Arguments specific to the build command.
/// #[derive(CodexParser, CodexCommand, Debug)]
/// pub struct BuildArgs {
///  /// Global arguments injected by the `CodexCommand` macro.
///  pub global: GlobalArgs,
///  /// Example argument for the build process.
///  #[codex(aliase = "", command = "build-example", default_value = "app-example")]
///  pub build_example: String,
/// }
///
/// /// Arguments specific to the run command.
/// #[derive(CodexParser, CodexCommand, Debug)]
/// pub struct RunArgs {
///  /// Global arguments injected by the `CodexCommand` macro.
///  pub global: GlobalArgs,
///  /// Example argument for the execution process.
///  #[codex(aliase = "", command = "run-example", default_value = "app-example")]
///   pub run_example: String,
/// }
///
/// //The central subcommand router for the CLI application.
/// #[derive(CodexSubcommand)]
/// enum MyCLI {
///   /// Build command variant.
///   Build(BuildArgs),
///   /// Run command variant.
///   Run(RunArgs),
/// }
///
///
/// fn main() {
///   let command = MyCLI::parse();
///   match command {
///     MyCLI::Build(args) => println!("Construindo com {args:?}"),
///     MyCLI::Run(args) => println!("Executando com {args:?}"),
///   }
/// }
///
/// ```
///
/// ### How it works
/// * **Routing:** It consumes the first command-line argument (after the executable name) to determine the enum variant.
/// * **Iterator Sharing:** It passes the remaining arguments directly to the inner struct's `parse_cli` method.
/// * **Validation:** If the provided command does not match any enum variant, it prints a stylized error message.
#[proc_macro_derive(CodexSubcommand)]
pub fn derive_codex_subcommand(input: TokenStream) -> TokenStream {
  let ast = parse_macro_input!(input as DeriveInput);
  match parser::generate_subcommand_router(&ast) {
    Ok(ts) => ts,
    Err(e) => e.to_compile_error().into(),
  }
}