From 4588e17f7ddacd8391a5c881a209735e08d48797 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Tue, 4 Aug 2026 13:26:48 +0800 Subject: feat: add entry metadata system with compile-time typed values Add `Metadata` trait and `#[metadata(Entry)]` attribute macro to attach arbitrary, compile-time-typed metadata to entries. Implement `ProgramCollect::get_metadata()` for runtime retrieval, backed by a global registry populated by `register_metadata!`. Extend `pathf` with `MetadataPattern` to resolve metadata types across modules at build time. Add two examples demonstrating metadata usage with and without pathf integration. --- examples/example-metadata/src/main.rs | 120 ++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 examples/example-metadata/src/main.rs (limited to 'examples/example-metadata/src') diff --git a/examples/example-metadata/src/main.rs b/examples/example-metadata/src/main.rs new file mode 100644 index 0000000..855241e --- /dev/null +++ b/examples/example-metadata/src/main.rs @@ -0,0 +1,120 @@ +//! Example: Entry Metadata (no `pathf`) +//! +//! > Demonstrates attaching arbitrary, compile-time-typed metadata (`Description`) +//! > to an entry via `#[metadata(Entry)]`, and retrieving it at runtime through +//! > `ProgramCollect::get_metadata`. The `desc` and `nodoc` subcommands dispatch +//! > through the normal chain/render pipeline — exactly like `example-basic`. +//! +//! Run: +//! ```bash +//! cargo run --manifest-path examples/example-metadata/Cargo.toml --quiet -- greet Alice +//! cargo run --manifest-path examples/example-metadata/Cargo.toml --quiet -- greet +//! cargo run --manifest-path examples/example-metadata/Cargo.toml --quiet -- desc +//! cargo run --manifest-path examples/example-metadata/Cargo.toml --quiet -- nodoc +//! ``` +//! +//! Output: +//! ```plaintext +//! Hello, Alice! +//! Hello, World! +//! EntryGreet desc = ok +//! EntryDescription has no description +//! ``` + +use mingling::{macros::metadata, prelude::*}; +use std::io::Write; + +// Define the `greet` subcommand +dispatcher!("greet", CMDGreet => EntryGreet); + +// Define the `desc` subcommand, which queries metadata bound to EntryGreet +dispatcher!("desc", CMDDescription => EntryDescription); + +// Define the `nodoc` subcommand, which queries metadata for an entry that has none +dispatcher!("nodoc", CMDNoDescription => EntryNoDescription); + +fn main() { + let mut program = ThisProgram::new(); + program.with_dispatcher(CMDGreet); + program.with_dispatcher(CMDDescription); + program.with_dispatcher(CMDNoDescription); + program.exec_and_exit(); +} + +/// The metadata type attached to an entry. +#[derive(Debug, PartialEq, Eq)] +pub struct Description { + pub desc: String, +} + +// --------- IMPORTANT --------- +/// Attach a `Description` to `EntryGreet`. +/// +/// - `BindType` = `EntryGreet` (the enum variant / entry type) +/// - `DataType` = `Description` (the function's return type) +#[metadata(EntryGreet)] +pub fn greet_desc() -> Description { + Description { + desc: "ok".to_string(), + } +} +// --------- IMPORTANT --------- + +pack!(ResultName = String); +pack!(DescResult = String); + +/// Chain for `greet` — reads the name and produces a `ResultName`. +#[chain] +fn handle_greet(args: EntryGreet) -> Next { + let name: ResultName = args + .inner + .first() + .cloned() + .unwrap_or_else(|| "World".to_string()) + .into(); + name.into() +} + +/// Chain for `desc` — looks up the metadata bound to `EntryGreet`. +#[chain] +fn handle_desc(_args: EntryDescription) -> Next { + use mingling::ProgramCollect; + // --------- IMPORTANT --------- + let msg = match ThisProgram::get_metadata::(ThisProgram::EntryGreet) { + Some(d) => format!("EntryGreet desc = {}", d.desc), + None => "EntryGreet has no description".to_string(), + }; + // --------- IMPORTANT --------- + DescResult::new(msg).to_render() +} + +/// Chain for `nodoc` — asks for metadata on an entry that has none. +#[chain] +fn handle_nodoc(_args: EntryNoDescription) -> Next { + use mingling::ProgramCollect; + // --------- IMPORTANT --------- + let msg = match ThisProgram::get_metadata::(ThisProgram::EntryDescription) { + Some(d) => format!("EntryDescription desc = {}", d.desc), + None => "EntryDescription has no description".to_string(), + }; + // --------- IMPORTANT --------- + DescResult::new(msg).to_render() +} + +/// Renders the greeting message with the provided name. +#[renderer] +fn render_name(name: ResultName) -> RenderResult { + let mut render_result = RenderResult::new(); + writeln!(render_result, "Hello, {}!", *name).ok(); + render_result +} + +/// Renders the metadata query result. +#[renderer] +fn render_desc(msg: DescResult) -> RenderResult { + let mut render_result = RenderResult::new(); + writeln!(render_result, "{}", *msg).ok(); + render_result +} + +gen_program!(); -- cgit