Skip to main content

net_mel/
ip.rs

1use melodium_core::{executive::*, *};
2use melodium_macro::{check, mel_data, mel_function, mel_treatment};
3use std::str::FromStr;
4use std::sync::Arc;
5
6/// IP data.
7///
8/// `Ip` data type contains a valid IP v4 or v6.
9///
10/// ℹ️ _Valid IP_ means the data contained makes sense as IP, not that it is reacheable.
11#[mel_data(traits(ToString TryToString Display))]
12#[derive(Debug, Clone, Serialize)]
13pub struct Ip(pub std::net::IpAddr);
14
15impl ToString for Ip {
16    fn to_string(&self) -> string {
17        self.0.to_string()
18    }
19}
20
21impl TryToString for Ip {
22    fn try_to_string(&self) -> Option<string> {
23        Some(self.0.to_string())
24    }
25}
26
27impl Display for Ip {
28    fn display(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
29        write!(f, "{}", &self.0)
30    }
31}
32
33/// IPv4 data.
34///
35/// `Ipv4` data type contains a valid IP v4.
36///
37/// ℹ️ _Valid IP v4_ means the data contained makes sense as IP, not that it is reacheable.
38#[mel_data(traits(ToString TryToString Display))]
39#[derive(Debug, Clone, Serialize)]
40pub struct Ipv4(pub std::net::Ipv4Addr);
41
42impl ToString for Ipv4 {
43    fn to_string(&self) -> string {
44        self.0.to_string()
45    }
46}
47
48impl TryToString for Ipv4 {
49    fn try_to_string(&self) -> Option<string> {
50        Some(self.0.to_string())
51    }
52}
53
54impl Display for Ipv4 {
55    fn display(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
56        write!(f, "{}", &self.0)
57    }
58}
59
60/// Wrap an `Ipv4` into a generic `Ip`.
61#[mel_function]
62pub fn from_ipv4(ipv4: Ipv4) -> Ip {
63    Ip(std::net::IpAddr::V4(ipv4.0))
64}
65
66/// Wrap an `Ipv6` into a generic `Ip`.
67#[mel_function]
68pub fn from_ipv6(ipv6: Ipv6) -> Ip {
69    Ip(std::net::IpAddr::V6(ipv6.0))
70}
71
72/// Convert a stream of `Ipv4` addresses into generic `Ip` values.
73///
74/// ```mermaid
75/// graph LR
76///     T("fromIpv4()")
77///     A["〈🟦〉 … 〈🟨〉"] -->|ipv4| T
78///     T -->|ip| B["〈🟦〉 … 〈🟨〉"]
79///
80///     style A fill:#ffffff,stroke:#ffffff
81///     style B fill:#ffffff,stroke:#ffffff
82/// ```
83#[mel_treatment(
84    input ipv4 Stream<Ipv4>
85    output ip Stream<Ip>
86)]
87pub async fn from_ipv4() {
88    while let Ok(ips) = ipv4
89        .recv_many()
90        .await
91        .map(|values| Into::<VecDeque<Value>>::into(values))
92    {
93        check!(
94            ip.send_many(TransmissionValue::Other(
95                ips.into_iter()
96                    .map(|ip| Value::Data(Arc::new(Ip(std::net::IpAddr::V4(
97                        GetData::<Arc<dyn Data>>::try_data(ip)
98                            .unwrap()
99                            .downcast_arc::<Ipv4>()
100                            .unwrap()
101                            .0
102                    )))))
103                    .collect()
104            ))
105            .await
106        )
107    }
108}
109
110/// Convert a stream of `Ipv6` addresses into generic `Ip` values.
111///
112/// ```mermaid
113/// graph LR
114///     T("fromIpv6()")
115///     A["〈🟦〉 … 〈🟨〉"] -->|ipv6| T
116///     T -->|ip| B["〈🟦〉 … 〈🟨〉"]
117///
118///     style A fill:#ffffff,stroke:#ffffff
119///     style B fill:#ffffff,stroke:#ffffff
120/// ```
121#[mel_treatment(
122    input ipv6 Stream<Ipv6>
123    output ip Stream<Ip>
124)]
125pub async fn from_ipv6() {
126    while let Ok(ips) = ipv6
127        .recv_many()
128        .await
129        .map(|values| Into::<VecDeque<Value>>::into(values))
130    {
131        check!(
132            ip.send_many(TransmissionValue::Other(
133                ips.into_iter()
134                    .map(|ip| Value::Data(Arc::new(Ip(std::net::IpAddr::V6(
135                        GetData::<Arc<dyn Data>>::try_data(ip)
136                            .unwrap()
137                            .downcast_arc::<Ipv6>()
138                            .unwrap()
139                            .0
140                    )))))
141                    .collect()
142            ))
143            .await
144        )
145    }
146}
147
148/// Extract the `Ipv4` address from a generic `Ip`, or `none` if it is an IPv6 address.
149#[mel_function]
150pub fn as_ipv4(ip: Ip) -> Option<Ipv4> {
151    if let std::net::IpAddr::V4(ip) = ip.0 {
152        Some(Ipv4(ip))
153    } else {
154        None
155    }
156}
157
158/// Extract the `Ipv6` address from a generic `Ip`, or `none` if it is an IPv4 address.
159#[mel_function]
160pub fn as_ipv6(ip: Ip) -> Option<Ipv6> {
161    if let std::net::IpAddr::V6(ip) = ip.0 {
162        Some(Ipv6(ip))
163    } else {
164        None
165    }
166}
167
168/// Extract the `Ipv4` address from each generic `Ip` in the stream.
169///
170/// Emits `none` for each element that is an IPv6 address.
171///
172/// ```mermaid
173/// graph LR
174///     T("asIpv4()")
175///     A["〈🟦〉 … 〈🟨〉"] -->|ip| T
176///     T -->|ipv4| B["〈🟦〉 … 〈none〉"]
177///
178///     style A fill:#ffffff,stroke:#ffffff
179///     style B fill:#ffffff,stroke:#ffffff
180/// ```
181#[mel_treatment(
182    input ip Stream<Ip>
183    output ipv4 Stream<Option<Ipv4>>
184)]
185pub async fn as_ipv4() {
186    while let Ok(ips) = ip
187        .recv_many()
188        .await
189        .map(|values| Into::<VecDeque<Value>>::into(values))
190    {
191        check!(
192            ipv4.send_many(TransmissionValue::Other(
193                ips.into_iter()
194                    .map(|ip| Value::Option(
195                        match GetData::<Arc<dyn Data>>::try_data(ip)
196                            .unwrap()
197                            .downcast_arc::<Ip>()
198                            .unwrap()
199                            .0
200                        {
201                            std::net::IpAddr::V4(ip) =>
202                                Some(Box::new(Value::Data(Arc::new(Ipv4(ip))))),
203                            std::net::IpAddr::V6(_) => None,
204                        }
205                    ))
206                    .collect()
207            ))
208            .await
209        )
210    }
211}
212
213/// Extract the `Ipv6` address from each generic `Ip` in the stream.
214///
215/// Emits `none` for each element that is an IPv4 address.
216///
217/// ```mermaid
218/// graph LR
219///     T("asIpv6()")
220///     A["〈🟦〉 … 〈🟨〉"] -->|ip| T
221///     T -->|ipv6| B["〈🟦〉 … 〈none〉"]
222///
223///     style A fill:#ffffff,stroke:#ffffff
224///     style B fill:#ffffff,stroke:#ffffff
225/// ```
226#[mel_treatment(
227    input ip Stream<Ip>
228    output ipv6 Stream<Option<Ipv6>>
229)]
230pub async fn as_ipv6() {
231    while let Ok(ips) = ip
232        .recv_many()
233        .await
234        .map(|values| Into::<VecDeque<Value>>::into(values))
235    {
236        check!(
237            ipv6.send_many(TransmissionValue::Other(
238                ips.into_iter()
239                    .map(|ip| Value::Option(
240                        match GetData::<Arc<dyn Data>>::try_data(ip)
241                            .unwrap()
242                            .downcast_arc::<Ip>()
243                            .unwrap()
244                            .0
245                        {
246                            std::net::IpAddr::V4(_) => None,
247                            std::net::IpAddr::V6(ip) =>
248                                Some(Box::new(Value::Data(Arc::new(Ipv6(ip))))),
249                        }
250                    ))
251                    .collect()
252            ))
253            .await
254        )
255    }
256}
257
258/// Return `true` if `ip` is an IPv4 address.
259#[mel_function]
260pub fn is_ipv4(ip: Ip) -> bool {
261    ip.0.is_ipv4()
262}
263
264/// Return `true` if `ip` is an IPv6 address.
265#[mel_function]
266pub fn is_ipv6(ip: Ip) -> bool {
267    ip.0.is_ipv6()
268}
269
270/// Emit `true` for each `Ip` in the stream that is an IPv4 address, `false` otherwise.
271///
272/// ```mermaid
273/// graph LR
274///     T("isIpv4()")
275///     A["〈🟦〉 … 〈🟨〉"] -->|ip| T
276///     T -->|ipv4| B["true … false"]
277///
278///     style A fill:#ffffff,stroke:#ffffff
279///     style B fill:#ffffff,stroke:#ffffff
280/// ```
281#[mel_treatment(
282    input ip Stream<Ip>
283    output ipv4 Stream<bool>
284)]
285pub async fn is_ipv4() {
286    while let Ok(ips) = ip
287        .recv_many()
288        .await
289        .map(|values| Into::<VecDeque<Value>>::into(values))
290    {
291        check!(
292            ipv4.send_many(TransmissionValue::Bool(
293                ips.into_iter()
294                    .map(|ip| GetData::<Arc<dyn Data>>::try_data(ip)
295                        .unwrap()
296                        .downcast_arc::<Ip>()
297                        .unwrap()
298                        .0
299                        .is_ipv4())
300                    .collect()
301            ))
302            .await
303        )
304    }
305}
306
307/// Emit `true` for each `Ip` in the stream that is an IPv6 address, `false` otherwise.
308///
309/// ```mermaid
310/// graph LR
311///     T("isIpv6()")
312///     A["〈🟦〉 … 〈🟨〉"] -->|ip| T
313///     T -->|ipv6| B["true … false"]
314///
315///     style A fill:#ffffff,stroke:#ffffff
316///     style B fill:#ffffff,stroke:#ffffff
317/// ```
318#[mel_treatment(
319    input ip Stream<Ip>
320    output ipv6 Stream<bool>
321)]
322pub async fn is_ipv6() {
323    while let Ok(ips) = ip
324        .recv_many()
325        .await
326        .map(|values| Into::<VecDeque<Value>>::into(values))
327    {
328        check!(
329            ipv6.send_many(TransmissionValue::Bool(
330                ips.into_iter()
331                    .map(|ip| GetData::<Arc<dyn Data>>::try_data(ip)
332                        .unwrap()
333                        .downcast_arc::<Ip>()
334                        .unwrap()
335                        .0
336                        .is_ipv6())
337                    .collect()
338            ))
339            .await
340        )
341    }
342}
343
344/// Build an `Ipv4` address from its four octets `a.b.c.d`.
345#[mel_function]
346pub fn ipv4(a: u8, b: u8, c: u8, d: u8) -> Ipv4 {
347    Ipv4(std::net::Ipv4Addr::new(a, b, c, d))
348}
349
350/// Parse `text` into an `Ipv4` address, returning `none` if `text` is not a valid IPv4 address.
351#[mel_function]
352pub fn to_ipv4(text: string) -> Option<Ipv4> {
353    std::net::Ipv4Addr::from_str(&text).ok().map(|ip| Ipv4(ip))
354}
355
356/// Parse each string in the stream into an `Ipv4` address.
357///
358/// Emits `none` for each element that is not a valid IPv4 address.
359///
360/// ```mermaid
361/// graph LR
362///     T("toIpv4()")
363///     A["🟦 … 🟨"] -->|text| T
364///     T -->|ipv4| B["〈🟦〉 … 〈none〉"]
365///
366///     style A fill:#ffffff,stroke:#ffffff
367///     style B fill:#ffffff,stroke:#ffffff
368/// ```
369#[mel_treatment(
370    input text Stream<string>
371    output ipv4 Stream<Option<Ipv4>>
372)]
373pub async fn to_ipv4() {
374    while let Ok(text) = text
375        .recv_many()
376        .await
377        .map(|values| TryInto::<Vec<string>>::try_into(values).unwrap())
378    {
379        check!(
380            ipv4.send_many(TransmissionValue::Other(
381                text.iter()
382                    .map(|t| Value::Option(
383                        std::net::Ipv4Addr::from_str(t)
384                            .ok()
385                            .map(|ip| Box::new(Value::Data(Arc::new(Ipv4(ip)))))
386                    ))
387                    .collect()
388            ))
389            .await
390        )
391    }
392}
393
394/// Return the IPv4 loopback address `127.0.0.1`.
395#[mel_function]
396pub fn localhost_ipv4() -> Ipv4 {
397    Ipv4(std::net::Ipv4Addr::LOCALHOST)
398}
399
400/// Return the IPv4 unspecified address `0.0.0.0`, typically used to bind to all interfaces.
401#[mel_function]
402pub fn unspecified_ipv4() -> Ipv4 {
403    Ipv4(std::net::Ipv4Addr::UNSPECIFIED)
404}
405
406/// IPv6 data.
407///
408/// `Ipv6` data type contains a valid IP v6.
409///
410/// ℹ️ _Valid IP v6_ means the data contained makes sense as IP, not that it is reacheable.
411#[mel_data(traits(ToString TryToString Display))]
412#[derive(Debug, Clone, Serialize)]
413pub struct Ipv6(pub std::net::Ipv6Addr);
414
415impl ToString for Ipv6 {
416    fn to_string(&self) -> string {
417        self.0.to_string()
418    }
419}
420
421impl TryToString for Ipv6 {
422    fn try_to_string(&self) -> Option<string> {
423        Some(self.0.to_string())
424    }
425}
426
427impl Display for Ipv6 {
428    fn display(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
429        write!(f, "{}", &self.0)
430    }
431}
432
433/// Build an `Ipv6` address from its eight 16-bit segments `a:b:c:d:e:f:g:h`.
434#[mel_function]
435pub fn ipv6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6 {
436    Ipv6(std::net::Ipv6Addr::new(a, b, c, d, e, f, g, h))
437}
438
439/// Parse `text` into an `Ipv6` address, returning `none` if `text` is not a valid IPv6 address.
440#[mel_function]
441pub fn to_ipv6(text: string) -> Option<Ipv6> {
442    std::net::Ipv6Addr::from_str(&text).ok().map(|ip| Ipv6(ip))
443}
444
445/// Parse each string in the stream into an `Ipv6` address.
446///
447/// Emits `none` for each element that is not a valid IPv6 address.
448///
449/// ```mermaid
450/// graph LR
451///     T("toIpv6()")
452///     A["🟦 … 🟨"] -->|text| T
453///     T -->|ipv6| B["〈🟦〉 … 〈none〉"]
454///
455///     style A fill:#ffffff,stroke:#ffffff
456///     style B fill:#ffffff,stroke:#ffffff
457/// ```
458#[mel_treatment(
459    input text Stream<string>
460    output ipv6 Stream<Option<Ipv6>>
461)]
462pub async fn to_ipv6() {
463    while let Ok(text) = text
464        .recv_many()
465        .await
466        .map(|values| TryInto::<Vec<string>>::try_into(values).unwrap())
467    {
468        check!(
469            ipv6.send_many(TransmissionValue::Other(
470                text.iter()
471                    .map(|t| Value::Option(
472                        std::net::Ipv6Addr::from_str(t)
473                            .ok()
474                            .map(|ip| Box::new(Value::Data(Arc::new(Ipv6(ip)))))
475                    ))
476                    .collect()
477            ))
478            .await
479        )
480    }
481}
482
483/// Return the IPv6 loopback address `::1`.
484#[mel_function]
485pub fn localhost_ipv6() -> Ipv6 {
486    Ipv6(std::net::Ipv6Addr::LOCALHOST)
487}
488
489/// Return the IPv6 unspecified address `::`, typically used to bind to all interfaces.
490#[mel_function]
491pub fn unspecified_ipv6() -> Ipv6 {
492    Ipv6(std::net::Ipv6Addr::UNSPECIFIED)
493}