Skip to main content

bpi_rs/clientinfo/
params.rs

1use std::net::IpAddr;
2
3use crate::{BpiError, BpiResult};
4
5/// Parameters for `/ip_service/v1/ip_service/get_ip_addr`.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub struct ClientInfoIpParams {
8    ip: Option<IpAddr>,
9}
10
11impl ClientInfoIpParams {
12    /// Creates parameters that query the caller's current IP address.
13    pub fn new() -> Self {
14        Self::default()
15    }
16
17    /// Creates parameters for a validated IP address.
18    pub fn for_ip(ip: IpAddr) -> Self {
19        Self { ip: Some(ip) }
20    }
21
22    /// Parses and sets an IPv4 or IPv6 address.
23    pub fn with_ip_str(mut self, ip: &str) -> BpiResult<Self> {
24        self.ip = Some(parse_ip(ip)?);
25        Ok(self)
26    }
27
28    pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
29        self.ip
30            .map(|ip| vec![("ip", ip.to_string())])
31            .unwrap_or_default()
32    }
33}
34
35fn parse_ip(ip: &str) -> BpiResult<IpAddr> {
36    ip.trim().parse().map_err(|_| {
37        BpiError::invalid_parameter("ip", "value must be a valid IPv4 or IPv6 address")
38    })
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn client_info_ip_params_serializes_empty_defaults() {
47        let params = ClientInfoIpParams::new();
48
49        assert!(params.query_pairs().is_empty());
50    }
51
52    #[test]
53    fn client_info_ip_params_serializes_ipv4() -> BpiResult<()> {
54        let params = ClientInfoIpParams::new().with_ip_str("8.8.8.8")?;
55
56        assert_eq!(params.query_pairs(), vec![("ip", "8.8.8.8".to_string())]);
57        Ok(())
58    }
59
60    #[test]
61    fn client_info_ip_params_serializes_ipv6() -> BpiResult<()> {
62        let params = ClientInfoIpParams::new().with_ip_str("2001:4860:4860::8888")?;
63
64        assert_eq!(
65            params.query_pairs(),
66            vec![("ip", "2001:4860:4860::8888".to_string())]
67        );
68        Ok(())
69    }
70
71    #[test]
72    fn client_info_ip_params_rejects_invalid_ip() {
73        let err = ClientInfoIpParams::new()
74            .with_ip_str("not-an-ip")
75            .unwrap_err();
76
77        assert!(matches!(
78            err,
79            BpiError::InvalidParameter { field: "ip", .. }
80        ));
81    }
82}