1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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
)
}
}
|