vrl/stdlib/
ip_to_ipv6.rs

1use crate::compiler::prelude::*;
2use std::net::IpAddr;
3
4fn ip_to_ipv6(value: &Value) -> Resolved {
5    let ip: IpAddr = value
6        .try_bytes_utf8_lossy()?
7        .parse()
8        .map_err(|err| format!("unable to parse IP address: {err}"))?;
9    match ip {
10        IpAddr::V4(addr) => Ok(addr.to_ipv6_mapped().to_string().into()),
11        IpAddr::V6(addr) => Ok(addr.to_string().into()),
12    }
13}
14
15#[derive(Clone, Copy, Debug)]
16pub struct IpToIpv6;
17
18impl Function for IpToIpv6 {
19    fn identifier(&self) -> &'static str {
20        "ip_to_ipv6"
21    }
22
23    fn usage(&self) -> &'static str {
24        "Converts the `ip` to an IPv6 address."
25    }
26
27    fn category(&self) -> &'static str {
28        Category::Ip.as_ref()
29    }
30
31    fn internal_failure_reasons(&self) -> &'static [&'static str] {
32        &["`ip` is not a valid IP address."]
33    }
34
35    fn return_kind(&self) -> u16 {
36        kind::BYTES
37    }
38
39    fn return_rules(&self) -> &'static [&'static str] {
40        &[
41            "The `ip` is returned unchanged if it's already an IPv6 address.",
42            "The `ip` is converted to an IPv6 address if it's an IPv4 address.",
43        ]
44    }
45
46    fn parameters(&self) -> &'static [Parameter] {
47        const PARAMETERS: &[Parameter] = &[Parameter::required(
48            "value",
49            kind::BYTES,
50            "The IP address to convert to IPv6.",
51        )];
52        PARAMETERS
53    }
54
55    fn examples(&self) -> &'static [Example] {
56        &[example! {
57            title: "IPv4 to IPv6",
58            source: r#"ip_to_ipv6!("192.168.10.32")"#,
59            result: Ok("::ffff:192.168.10.32"),
60        }]
61    }
62
63    fn compile(
64        &self,
65        _state: &state::TypeState,
66        _ctx: &mut FunctionCompileContext,
67        arguments: ArgumentList,
68    ) -> Compiled {
69        let value = arguments.required("value");
70
71        Ok(IpToIpv6Fn { value }.as_expr())
72    }
73}
74
75#[derive(Debug, Clone)]
76struct IpToIpv6Fn {
77    value: Box<dyn Expression>,
78}
79
80impl FunctionExpression for IpToIpv6Fn {
81    fn resolve(&self, ctx: &mut Context) -> Resolved {
82        let value = self.value.resolve(ctx)?;
83        ip_to_ipv6(&value)
84    }
85
86    fn type_def(&self, _: &state::TypeState) -> TypeDef {
87        TypeDef::bytes().fallible()
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94    use crate::value;
95
96    test_function![
97        ip_to_ipv6 => IpToIpv6;
98
99        invalid {
100            args: func_args![value: "i am not an ipaddress"],
101            want: Err(
102                    "unable to parse IP address: invalid IP address syntax"),
103            tdef: TypeDef::bytes().fallible(),
104        }
105
106        valid {
107            args: func_args![value: "192.168.0.1"],
108            want: Ok(value!("::ffff:192.168.0.1")),
109            tdef: TypeDef::bytes().fallible(),
110        }
111
112        ipv6_passthrough {
113            args: func_args![value: "2404:6800:4003:c02::64"],
114            want: Ok(value!("2404:6800:4003:c02::64")),
115            tdef: TypeDef::bytes().fallible(),
116        }
117    ];
118}