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_as(
95                ips.into_iter()
96                    .map(|ip| Arc::new(Ip(std::net::IpAddr::V4(
97                        ip.try_data::<Arc<Ipv4>>().unwrap().0
98                    ))))
99                    .collect::<Vec<_>>()
100            )
101            .await
102        )
103    }
104}
105
106/// Convert a stream of `Ipv6` addresses into generic `Ip` values.
107///
108/// ```mermaid
109/// graph LR
110///     T("fromIpv6()")
111///     A["〈🟦〉 … 〈🟨〉"] -->|ipv6| T
112///     T -->|ip| B["〈🟦〉 … 〈🟨〉"]
113///
114///     style A fill:#ffffff,stroke:#ffffff
115///     style B fill:#ffffff,stroke:#ffffff
116/// ```
117#[mel_treatment(
118    input ipv6 Stream<Ipv6>
119    output ip Stream<Ip>
120)]
121pub async fn from_ipv6() {
122    while let Ok(ips) = ipv6
123        .recv_many()
124        .await
125        .map(|values| Into::<VecDeque<Value>>::into(values))
126    {
127        check!(
128            ip.send_many_as(
129                ips.into_iter()
130                    .map(|ip| Arc::new(Ip(std::net::IpAddr::V6(
131                        ip.try_data::<Arc<Ipv6>>().unwrap().0
132                    ))))
133                    .collect::<Vec<_>>()
134            )
135            .await
136        )
137    }
138}
139
140/// Extract the `Ipv4` address from a generic `Ip`, or `none` if it is an IPv6 address.
141#[mel_function]
142pub fn as_ipv4(ip: Ip) -> Option<Ipv4> {
143    if let std::net::IpAddr::V4(ip) = ip.0 {
144        Some(Ipv4(ip))
145    } else {
146        None
147    }
148}
149
150/// Extract the `Ipv6` address from a generic `Ip`, or `none` if it is an IPv4 address.
151#[mel_function]
152pub fn as_ipv6(ip: Ip) -> Option<Ipv6> {
153    if let std::net::IpAddr::V6(ip) = ip.0 {
154        Some(Ipv6(ip))
155    } else {
156        None
157    }
158}
159
160/// Extract the `Ipv4` address from each generic `Ip` in the stream.
161///
162/// Emits `none` for each element that is an IPv6 address.
163///
164/// ```mermaid
165/// graph LR
166///     T("asIpv4()")
167///     A["〈🟦〉 … 〈🟨〉"] -->|ip| T
168///     T -->|ipv4| B["〈🟦〉 … 〈none〉"]
169///
170///     style A fill:#ffffff,stroke:#ffffff
171///     style B fill:#ffffff,stroke:#ffffff
172/// ```
173#[mel_treatment(
174    input ip Stream<Ip>
175    output ipv4 Stream<Option<Ipv4>>
176)]
177pub async fn as_ipv4() {
178    while let Ok(ips) = ip
179        .recv_many()
180        .await
181        .map(|values| Into::<VecDeque<Value>>::into(values))
182    {
183        check!(
184            ipv4.send_many(TransmissionValue::Other(
185                ips.into_iter()
186                    .map(
187                        |ip| Value::Option(match ip.try_data::<Arc<Ip>>().unwrap().0 {
188                            std::net::IpAddr::V4(ip) =>
189                                Some(Box::new(Value::Data(Arc::new(Ipv4(ip))))),
190                            std::net::IpAddr::V6(_) => None,
191                        })
192                    )
193                    .collect()
194            ))
195            .await
196        )
197    }
198}
199
200/// Extract the `Ipv6` address from each generic `Ip` in the stream.
201///
202/// Emits `none` for each element that is an IPv4 address.
203///
204/// ```mermaid
205/// graph LR
206///     T("asIpv6()")
207///     A["〈🟦〉 … 〈🟨〉"] -->|ip| T
208///     T -->|ipv6| B["〈🟦〉 … 〈none〉"]
209///
210///     style A fill:#ffffff,stroke:#ffffff
211///     style B fill:#ffffff,stroke:#ffffff
212/// ```
213#[mel_treatment(
214    input ip Stream<Ip>
215    output ipv6 Stream<Option<Ipv6>>
216)]
217pub async fn as_ipv6() {
218    while let Ok(ips) = ip
219        .recv_many()
220        .await
221        .map(|values| Into::<VecDeque<Value>>::into(values))
222    {
223        check!(
224            ipv6.send_many(TransmissionValue::Other(
225                ips.into_iter()
226                    .map(
227                        |ip| Value::Option(match ip.try_data::<Arc<Ip>>().unwrap().0 {
228                            std::net::IpAddr::V4(_) => None,
229                            std::net::IpAddr::V6(ip) =>
230                                Some(Box::new(Value::Data(Arc::new(Ipv6(ip))))),
231                        })
232                    )
233                    .collect()
234            ))
235            .await
236        )
237    }
238}
239
240/// Return `true` if `ip` is an IPv4 address.
241#[mel_function]
242pub fn is_ipv4(ip: Ip) -> bool {
243    ip.0.is_ipv4()
244}
245
246/// Return `true` if `ip` is an IPv6 address.
247#[mel_function]
248pub fn is_ipv6(ip: Ip) -> bool {
249    ip.0.is_ipv6()
250}
251
252/// Emit `true` for each `Ip` in the stream that is an IPv4 address, `false` otherwise.
253///
254/// ```mermaid
255/// graph LR
256///     T("isIpv4()")
257///     A["〈🟦〉 … 〈🟨〉"] -->|ip| T
258///     T -->|ipv4| B["true … false"]
259///
260///     style A fill:#ffffff,stroke:#ffffff
261///     style B fill:#ffffff,stroke:#ffffff
262/// ```
263#[mel_treatment(
264    input ip Stream<Ip>
265    output ipv4 Stream<bool>
266)]
267pub async fn is_ipv4() {
268    while let Ok(ips) = ip
269        .recv_many()
270        .await
271        .map(|values| Into::<VecDeque<Value>>::into(values))
272    {
273        check!(
274            ipv4.send_many_as(
275                ips.into_iter()
276                    .map(|ip| ip.try_data::<Arc<Ip>>().unwrap().0.is_ipv4())
277                    .collect::<Vec<_>>()
278            )
279            .await
280        )
281    }
282}
283
284/// Emit `true` for each `Ip` in the stream that is an IPv6 address, `false` otherwise.
285///
286/// ```mermaid
287/// graph LR
288///     T("isIpv6()")
289///     A["〈🟦〉 … 〈🟨〉"] -->|ip| T
290///     T -->|ipv6| B["true … false"]
291///
292///     style A fill:#ffffff,stroke:#ffffff
293///     style B fill:#ffffff,stroke:#ffffff
294/// ```
295#[mel_treatment(
296    input ip Stream<Ip>
297    output ipv6 Stream<bool>
298)]
299pub async fn is_ipv6() {
300    while let Ok(ips) = ip
301        .recv_many()
302        .await
303        .map(|values| Into::<VecDeque<Value>>::into(values))
304    {
305        check!(
306            ipv6.send_many_as(
307                ips.into_iter()
308                    .map(|ip| ip.try_data::<Arc<Ip>>().unwrap().0.is_ipv6())
309                    .collect::<Vec<_>>()
310            )
311            .await
312        )
313    }
314}
315
316/// Build an `Ipv4` address from its four octets `a.b.c.d`.
317#[mel_function]
318pub fn ipv4(a: u8, b: u8, c: u8, d: u8) -> Ipv4 {
319    Ipv4(std::net::Ipv4Addr::new(a, b, c, d))
320}
321
322/// Parse `text` into an `Ipv4` address, returning `none` if `text` is not a valid IPv4 address.
323#[mel_function]
324pub fn to_ipv4(text: string) -> Option<Ipv4> {
325    std::net::Ipv4Addr::from_str(&text).ok().map(|ip| Ipv4(ip))
326}
327
328/// Parse each string in the stream into an `Ipv4` address.
329///
330/// Emits `none` for each element that is not a valid IPv4 address.
331///
332/// ```mermaid
333/// graph LR
334///     T("toIpv4()")
335///     A["🟦 … 🟨"] -->|text| T
336///     T -->|ipv4| B["〈🟦〉 … 〈none〉"]
337///
338///     style A fill:#ffffff,stroke:#ffffff
339///     style B fill:#ffffff,stroke:#ffffff
340/// ```
341#[mel_treatment(
342    input text Stream<string>
343    output ipv4 Stream<Option<Ipv4>>
344)]
345pub async fn to_ipv4() {
346    while let Ok(text) = text.recv_many_as::<string>().await {
347        check!(
348            ipv4.send_many(TransmissionValue::Other(
349                text.iter()
350                    .map(|t| Value::Option(
351                        std::net::Ipv4Addr::from_str(t)
352                            .ok()
353                            .map(|ip| Box::new(Value::Data(Arc::new(Ipv4(ip)))))
354                    ))
355                    .collect()
356            ))
357            .await
358        )
359    }
360}
361
362/// Return the IPv4 loopback address `127.0.0.1`.
363#[mel_function]
364pub fn localhost_ipv4() -> Ipv4 {
365    Ipv4(std::net::Ipv4Addr::LOCALHOST)
366}
367
368/// Return the IPv4 unspecified address `0.0.0.0`, typically used to bind to all interfaces.
369#[mel_function]
370pub fn unspecified_ipv4() -> Ipv4 {
371    Ipv4(std::net::Ipv4Addr::UNSPECIFIED)
372}
373
374/// IPv6 data.
375///
376/// `Ipv6` data type contains a valid IP v6.
377///
378/// ℹ️ _Valid IP v6_ means the data contained makes sense as IP, not that it is reacheable.
379#[mel_data(traits(ToString TryToString Display))]
380#[derive(Debug, Clone, Serialize)]
381pub struct Ipv6(pub std::net::Ipv6Addr);
382
383impl ToString for Ipv6 {
384    fn to_string(&self) -> string {
385        self.0.to_string()
386    }
387}
388
389impl TryToString for Ipv6 {
390    fn try_to_string(&self) -> Option<string> {
391        Some(self.0.to_string())
392    }
393}
394
395impl Display for Ipv6 {
396    fn display(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
397        write!(f, "{}", &self.0)
398    }
399}
400
401/// Build an `Ipv6` address from its eight 16-bit segments `a:b:c:d:e:f:g:h`.
402#[mel_function]
403pub fn ipv6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6 {
404    Ipv6(std::net::Ipv6Addr::new(a, b, c, d, e, f, g, h))
405}
406
407/// Parse `text` into an `Ipv6` address, returning `none` if `text` is not a valid IPv6 address.
408#[mel_function]
409pub fn to_ipv6(text: string) -> Option<Ipv6> {
410    std::net::Ipv6Addr::from_str(&text).ok().map(|ip| Ipv6(ip))
411}
412
413/// Parse each string in the stream into an `Ipv6` address.
414///
415/// Emits `none` for each element that is not a valid IPv6 address.
416///
417/// ```mermaid
418/// graph LR
419///     T("toIpv6()")
420///     A["🟦 … 🟨"] -->|text| T
421///     T -->|ipv6| B["〈🟦〉 … 〈none〉"]
422///
423///     style A fill:#ffffff,stroke:#ffffff
424///     style B fill:#ffffff,stroke:#ffffff
425/// ```
426#[mel_treatment(
427    input text Stream<string>
428    output ipv6 Stream<Option<Ipv6>>
429)]
430pub async fn to_ipv6() {
431    while let Ok(text) = text.recv_many_as::<string>().await {
432        check!(
433            ipv6.send_many(TransmissionValue::Other(
434                text.iter()
435                    .map(|t| Value::Option(
436                        std::net::Ipv6Addr::from_str(t)
437                            .ok()
438                            .map(|ip| Box::new(Value::Data(Arc::new(Ipv6(ip)))))
439                    ))
440                    .collect()
441            ))
442            .await
443        )
444    }
445}
446
447/// Return the IPv6 loopback address `::1`.
448#[mel_function]
449pub fn localhost_ipv6() -> Ipv6 {
450    Ipv6(std::net::Ipv6Addr::LOCALHOST)
451}
452
453/// Return the IPv6 unspecified address `::`, typically used to bind to all interfaces.
454#[mel_function]
455pub fn unspecified_ipv6() -> Ipv6 {
456    Ipv6(std::net::Ipv6Addr::UNSPECIFIED)
457}