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}