aboutsummaryrefslogtreecommitdiff
path: root/examples/example-custom-pickable/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/example-custom-pickable/src/main.rs')
-rw-r--r--examples/example-custom-pickable/src/main.rs125
1 files changed, 125 insertions, 0 deletions
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<Self::Output> {
+ // 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<String> for Address {
+ type Error = String;
+
+ fn try_from(raw: String) -> Result<Self, Self::Error> {
+ // 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::<u8>()
+ .map_err(|_| format!("Invalid IP octet: {}", part))?;
+ }
+
+ // Parse port
+ let port = port_str
+ .parse::<u16>()
+ .map_err(|_| format!("Invalid port: {}", port_str))?;
+
+ Ok(Address { ip, port })
+ }
+}
+
+impl From<Address> 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
+ )
+ }
+}