From 0a2ef958c0dca21d19e4ffc38ba5a7c4078e182a Mon Sep 17 00:00:00 2001 From: Weicao-CatilGrass <1992414357@qq.com> Date: Sat, 23 May 2026 23:41:04 +0800 Subject: Rework examples and add entry macro for testing --- examples/example-custom-pickable/src/main.rs | 125 +++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 examples/example-custom-pickable/src/main.rs (limited to 'examples/example-custom-pickable/src/main.rs') diff --git a/examples/example-custom-pickable/src/main.rs b/examples/example-custom-pickable/src/main.rs new file mode 100644 index 0000000..466ae43 --- /dev/null +++ b/examples/example-custom-pickable/src/main.rs @@ -0,0 +1,125 @@ +//! Example Custom Pickable +//! +//! > This example demonstrates how to use the Pickable trait to add parsing for your types +//! +//! Run: +//! ```bash +//! cargo run --manifest-path examples/example-custom-pickable/Cargo.toml --quiet -- connect 127.0.0.1:5012 +//! cargo run --manifest-path examples/example-custom-pickable/Cargo.toml --quiet -- connect 127.0.0.1 +//! ``` +//! +//! Output: +//! ```plaintext +//! Connected to "127.0.0.1:5012" +//! Failed to parse address +//! ``` + +use mingling::{Groupped, macros::route, parser::Pickable, prelude::*}; + +// Define types that can be recognized by Mingling +// ________________________ `Pickable` trait needs to implement Default +// / ________ The Groupped derive macro registers an ID for this type +// | / Mingling uses this ID to identify the type +// vvvvvvv vvvvvvvv +#[derive(Debug, Default, Clone, Groupped)] +pub struct Address { + pub ip: [u8; 4], + pub port: u16, +} + +// --------- IMPORTANT --------- +impl Pickable for Address { + type Output = Address; + fn pick(args: &mut mingling::parser::Argument, flag: mingling::Flag) -> Option { + // Extract the raw string from Argument using the Flag + let raw: String = args.pick_argument(flag)?.to_string(); + + // Use TryFrom to parse the address + Address::try_from(raw).ok() + } +} +// --------- IMPORTANT --------- + +dispatcher!("connect", CMDConnect => EntryConnect); +pack!(ErrorParseAddressFailed = ()); + +#[chain] +fn handle_connect(prev: EntryConnect) -> Next { + let connect: Address = + route! { prev.pick_or_route((), ErrorParseAddressFailed::default().to_chain()).unpack() }; + connect.to_chain() +} + +#[renderer] +fn render_address(addr: Address) { + r_println!("Connected to \"{}\"", addr.to_string()); +} + +#[renderer] +fn render_error_parse_address_failed(_: ErrorParseAddressFailed) { + r_println!("Failed to parse address"); +} + +gen_program!(); + +fn main() { + let mut program = ThisProgram::new(); + program.with_dispatcher(CMDConnect); + program.exec_and_exit(); +} + +// Address conversion + +impl TryFrom for Address { + type Error = String; + + fn try_from(raw: String) -> Result { + // Expected format: "192.168.1.1:8080" + let parts: Vec<&str> = raw.split(':').collect(); + if parts.len() != 2 { + return Err("Invalid format: expected 'IP:PORT'".to_string()); + } + + let ip_str = parts[0]; + let port_str = parts[1]; + + // Parse IP address (4 octets separated by dots) + let ip_parts: Vec<&str> = ip_str.split('.').collect(); + if ip_parts.len() != 4 { + return Err("Invalid IP address format".to_string()); + } + + let mut ip = [0u8; 4]; + for (i, part) in ip_parts.iter().enumerate() { + ip[i] = part + .parse::() + .map_err(|_| format!("Invalid IP octet: {}", part))?; + } + + // Parse port + let port = port_str + .parse::() + .map_err(|_| format!("Invalid port: {}", port_str))?; + + Ok(Address { ip, port }) + } +} + +impl From
for String { + fn from(addr: Address) -> String { + format!( + "{}.{}.{}.{}:{}", + addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], addr.port + ) + } +} + +impl std::fmt::Display for Address { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}.{}.{}.{}:{}", + self.ip[0], self.ip[1], self.ip[2], self.ip[3], self.port + ) + } +} -- cgit