Skip to main content

elicitation/primitives/
network.rs

1//! Network type implementations for IP addresses and socket addresses.
2
3use crate::{ElicitClient, ElicitError, ElicitErrorKind, ElicitResult, Elicitation, Prompt};
4use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
5
6// Generate default-only style enums for all network types
7crate::default_style!(IpAddr => IpAddrStyle);
8crate::default_style!(Ipv4Addr => Ipv4AddrStyle);
9crate::default_style!(Ipv6Addr => Ipv6AddrStyle);
10crate::default_style!(SocketAddr => SocketAddrStyle);
11crate::default_style!(SocketAddrV4 => SocketAddrV4Style);
12crate::default_style!(SocketAddrV6 => SocketAddrV6Style);
13
14// IpAddr (enum: V4 | V6)
15impl Prompt for IpAddr {
16    fn prompt() -> Option<&'static str> {
17        Some("Please enter an IP address (IPv4 or IPv6):")
18    }
19}
20
21impl Elicitation for IpAddr {
22    type Style = IpAddrStyle;
23
24    #[tracing::instrument(skip(client))]
25    async fn elicit(client: &ElicitClient) -> ElicitResult<Self> {
26        tracing::debug!("Eliciting IpAddr");
27
28        let ip_str = String::elicit(client).await?;
29
30        match ip_str.parse::<IpAddr>() {
31            Ok(addr) => {
32                tracing::debug!(addr = %addr, "Valid IP address");
33                Ok(addr)
34            }
35            Err(e) => {
36                tracing::warn!(error = ?e, input = %ip_str, "Invalid IP address format");
37                Err(ElicitError::new(ElicitErrorKind::InvalidFormat {
38                    expected: "valid IP address (IPv4 or IPv6)".to_string(),
39                    received: ip_str,
40                }))
41            }
42        }
43    }
44}
45
46// Ipv4Addr
47impl Prompt for Ipv4Addr {
48    fn prompt() -> Option<&'static str> {
49        Some("Please enter an IPv4 address (e.g., 192.168.1.1):")
50    }
51}
52
53impl Elicitation for Ipv4Addr {
54    type Style = Ipv4AddrStyle;
55
56    #[tracing::instrument(skip(client))]
57    async fn elicit(client: &ElicitClient) -> ElicitResult<Self> {
58        tracing::debug!("Eliciting Ipv4Addr");
59
60        let ip_str = String::elicit(client).await?;
61
62        match ip_str.parse::<Ipv4Addr>() {
63            Ok(addr) => {
64                tracing::debug!(addr = %addr, "Valid IPv4 address");
65                Ok(addr)
66            }
67            Err(e) => {
68                tracing::warn!(error = ?e, input = %ip_str, "Invalid IPv4 address format");
69                Err(ElicitError::new(ElicitErrorKind::InvalidFormat {
70                    expected: "valid IPv4 address (e.g., 192.168.1.1)".to_string(),
71                    received: ip_str,
72                }))
73            }
74        }
75    }
76}
77
78// Ipv6Addr
79impl Prompt for Ipv6Addr {
80    fn prompt() -> Option<&'static str> {
81        Some("Please enter an IPv6 address (e.g., 2001:db8::1):")
82    }
83}
84
85impl Elicitation for Ipv6Addr {
86    type Style = Ipv6AddrStyle;
87
88    #[tracing::instrument(skip(client))]
89    async fn elicit(client: &ElicitClient) -> ElicitResult<Self> {
90        tracing::debug!("Eliciting Ipv6Addr");
91
92        let ip_str = String::elicit(client).await?;
93
94        match ip_str.parse::<Ipv6Addr>() {
95            Ok(addr) => {
96                tracing::debug!(addr = %addr, "Valid IPv6 address");
97                Ok(addr)
98            }
99            Err(e) => {
100                tracing::warn!(error = ?e, input = %ip_str, "Invalid IPv6 address format");
101                Err(ElicitError::new(ElicitErrorKind::InvalidFormat {
102                    expected: "valid IPv6 address (e.g., 2001:db8::1)".to_string(),
103                    received: ip_str,
104                }))
105            }
106        }
107    }
108}
109
110// SocketAddr (IpAddr + port)
111impl Prompt for SocketAddr {
112    fn prompt() -> Option<&'static str> {
113        Some("Please enter a socket address (e.g., 127.0.0.1:8080 or [::1]:8080):")
114    }
115}
116
117impl Elicitation for SocketAddr {
118    type Style = SocketAddrStyle;
119
120    #[tracing::instrument(skip(client))]
121    async fn elicit(client: &ElicitClient) -> ElicitResult<Self> {
122        tracing::debug!("Eliciting SocketAddr");
123
124        let addr_str = String::elicit(client).await?;
125
126        match addr_str.parse::<SocketAddr>() {
127            Ok(addr) => {
128                tracing::debug!(addr = %addr, "Valid socket address");
129                Ok(addr)
130            }
131            Err(e) => {
132                tracing::warn!(error = ?e, input = %addr_str, "Invalid socket address format");
133                Err(ElicitError::new(ElicitErrorKind::InvalidFormat {
134                    expected: "valid socket address (e.g., 127.0.0.1:8080)".to_string(),
135                    received: addr_str,
136                }))
137            }
138        }
139    }
140}
141
142// SocketAddrV4
143impl Prompt for SocketAddrV4 {
144    fn prompt() -> Option<&'static str> {
145        Some("Please enter an IPv4 socket address (e.g., 192.168.1.1:8080):")
146    }
147}
148
149impl Elicitation for SocketAddrV4 {
150    type Style = SocketAddrV4Style;
151
152    #[tracing::instrument(skip(client))]
153    async fn elicit(client: &ElicitClient) -> ElicitResult<Self> {
154        tracing::debug!("Eliciting SocketAddrV4");
155
156        let addr_str = String::elicit(client).await?;
157
158        match addr_str.parse::<SocketAddrV4>() {
159            Ok(addr) => {
160                tracing::debug!(addr = %addr, "Valid IPv4 socket address");
161                Ok(addr)
162            }
163            Err(e) => {
164                tracing::warn!(error = ?e, input = %addr_str, "Invalid IPv4 socket address format");
165                Err(ElicitError::new(ElicitErrorKind::InvalidFormat {
166                    expected: "valid IPv4 socket address (e.g., 192.168.1.1:8080)".to_string(),
167                    received: addr_str,
168                }))
169            }
170        }
171    }
172}
173
174// SocketAddrV6
175impl Prompt for SocketAddrV6 {
176    fn prompt() -> Option<&'static str> {
177        Some("Please enter an IPv6 socket address (e.g., [2001:db8::1]:8080):")
178    }
179}
180
181impl Elicitation for SocketAddrV6 {
182    type Style = SocketAddrV6Style;
183
184    #[tracing::instrument(skip(client))]
185    async fn elicit(client: &ElicitClient) -> ElicitResult<Self> {
186        tracing::debug!("Eliciting SocketAddrV6");
187
188        let addr_str = String::elicit(client).await?;
189
190        match addr_str.parse::<SocketAddrV6>() {
191            Ok(addr) => {
192                tracing::debug!(addr = %addr, "Valid IPv6 socket address");
193                Ok(addr)
194            }
195            Err(e) => {
196                tracing::warn!(error = ?e, input = %addr_str, "Invalid IPv6 socket address format");
197                Err(ElicitError::new(ElicitErrorKind::InvalidFormat {
198                    expected: "valid IPv6 socket address (e.g., [2001:db8::1]:8080)".to_string(),
199                    received: addr_str,
200                }))
201            }
202        }
203    }
204}