vrl/stdlib/
ip_ntoa.rs

1use crate::compiler::prelude::*;
2use std::{convert::TryInto, net::Ipv4Addr};
3
4fn ip_ntoa(value: Value) -> Resolved {
5    let i: u32 = value
6        .try_integer()?
7        .try_into()
8        .map_err(|_| String::from("cannot convert to bytes: integer does not fit in u32"))?;
9
10    Ok(Ipv4Addr::from(i).to_string().into())
11}
12
13#[derive(Clone, Copy, Debug)]
14pub struct IpNtoa;
15
16impl Function for IpNtoa {
17    fn identifier(&self) -> &'static str {
18        "ip_ntoa"
19    }
20
21    fn usage(&self) -> &'static str {
22        indoc! {"
23            Converts numeric representation of IPv4 address in network-order bytes
24            to numbers-and-dots notation.
25
26            This behavior mimics [inet_ntoa](https://linux.die.net/man/3/inet_ntoa).
27        "}
28    }
29
30    fn category(&self) -> &'static str {
31        Category::Ip.as_ref()
32    }
33
34    fn internal_failure_reasons(&self) -> &'static [&'static str] {
35        &["`value` cannot fit in an unsigned 32-bit integer."]
36    }
37
38    fn return_kind(&self) -> u16 {
39        kind::BYTES
40    }
41
42    fn parameters(&self) -> &'static [Parameter] {
43        const PARAMETERS: &[Parameter] = &[Parameter::required(
44            "value",
45            kind::INTEGER,
46            "The integer representation of an IPv4 address.",
47        )];
48        PARAMETERS
49    }
50
51    fn examples(&self) -> &'static [Example] {
52        &[example! {
53            title: "Integer to IPv4",
54            source: "ip_ntoa!(16909060)",
55            result: Ok("1.2.3.4"),
56        }]
57    }
58
59    fn compile(
60        &self,
61        _state: &state::TypeState,
62        _ctx: &mut FunctionCompileContext,
63        arguments: ArgumentList,
64    ) -> Compiled {
65        let value = arguments.required("value");
66
67        Ok(IpNtoaFn { value }.as_expr())
68    }
69}
70
71#[derive(Debug, Clone)]
72struct IpNtoaFn {
73    value: Box<dyn Expression>,
74}
75
76impl FunctionExpression for IpNtoaFn {
77    fn resolve(&self, ctx: &mut Context) -> Resolved {
78        let value = self.value.resolve(ctx)?;
79        ip_ntoa(value)
80    }
81
82    fn type_def(&self, _: &state::TypeState) -> TypeDef {
83        TypeDef::bytes().fallible()
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90    use crate::value;
91
92    test_function![
93        ip_ntoa => IpNtoa;
94
95        invalid {
96            args: func_args![value: i64::from(u32::MAX) + 1],
97            want: Err("cannot convert to bytes: integer does not fit in u32"),
98            tdef: TypeDef::bytes().fallible(),
99        }
100
101        valid {
102            args: func_args![value: 16_909_060],
103            want: Ok(value!("1.2.3.4")),
104            tdef: TypeDef::bytes().fallible(),
105        }
106    ];
107}