ip2location_ip2proxy/
record.rs

1//! [Ref](https://lite.ip2location.com/database/px11-ip-proxytype-country-region-city-isp-domain-usagetype-asn-lastseen-threat-residential-provider#database-fields)
2
3use std::net::IpAddr;
4
5use country_code::CountryCode;
6
7use crate::{proxy_type::ProxyType, usage_type::UsageType};
8
9//
10#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
11#[derive(Debug, Clone)]
12pub struct Record {
13    #[cfg_attr(feature = "serde", serde(deserialize_with = "ip_deserialize"))]
14    pub ip_from: IpAddr,
15    #[cfg_attr(feature = "serde", serde(deserialize_with = "ip_deserialize"))]
16    pub ip_to: IpAddr,
17    pub proxy_type: Option<ProxyType>,
18    pub country_code: CountryCode,
19    #[cfg_attr(
20        feature = "serde",
21        serde(default, deserialize_with = "option_box_str_deserialize")
22    )]
23    pub country_name: Option<Box<str>>,
24    #[cfg_attr(
25        feature = "serde",
26        serde(default, deserialize_with = "option_box_str_deserialize")
27    )]
28    pub region_name: Option<Box<str>>,
29    #[cfg_attr(
30        feature = "serde",
31        serde(default, deserialize_with = "option_box_str_deserialize")
32    )]
33    pub city_name: Option<Box<str>>,
34    #[cfg_attr(
35        feature = "serde",
36        serde(default, deserialize_with = "option_box_str_deserialize")
37    )]
38    pub isp: Option<Box<str>>,
39    #[cfg_attr(
40        feature = "serde",
41        serde(default, deserialize_with = "option_box_str_deserialize")
42    )]
43    pub domain: Option<Box<str>>,
44    pub usage_type: Option<UsageType>,
45    #[cfg_attr(
46        feature = "serde",
47        serde(default, deserialize_with = "option_usize_deserialize")
48    )]
49    pub asn: Option<usize>,
50    #[cfg_attr(
51        feature = "serde",
52        serde(default, deserialize_with = "option_box_str_deserialize")
53    )]
54    pub as_name: Option<Box<str>>,
55    #[cfg_attr(
56        feature = "serde",
57        serde(default, deserialize_with = "option_box_str_deserialize")
58    )]
59    pub last_seen: Option<Box<str>>,
60    #[cfg_attr(
61        feature = "serde",
62        serde(default, deserialize_with = "option_box_str_deserialize")
63    )]
64    pub threat: Option<Box<str>>,
65    #[cfg_attr(
66        feature = "serde",
67        serde(default, deserialize_with = "option_box_str_deserialize")
68    )]
69    pub provider: Option<Box<str>>,
70    #[cfg_attr(
71        feature = "serde",
72        serde(default, deserialize_with = "option_box_str_deserialize")
73    )]
74    pub residential: Option<Box<str>>,
75}
76
77#[cfg(feature = "serde")]
78fn ip_deserialize<'de, D>(deserializer: D) -> Result<IpAddr, D::Error>
79where
80    D: serde::Deserializer<'de>,
81{
82    use std::net::{Ipv4Addr, Ipv6Addr};
83
84    use serde::Deserialize as _;
85
86    let s = Box::<str>::deserialize(deserializer)?;
87    if let Ok(v) = s.parse::<u32>() {
88        Ok(Ipv4Addr::from(v).into())
89    } else if let Ok(v) = s.parse::<u128>() {
90        Ok(Ipv6Addr::from(v).into())
91    } else if let Ok(v) = s.parse::<Ipv4Addr>() {
92        Ok(v.into())
93    } else if let Ok(v) = s.parse::<Ipv6Addr>() {
94        Ok(v.into())
95    } else {
96        Err(serde::de::Error::custom(""))
97    }
98}
99
100#[cfg(feature = "serde")]
101fn option_box_str_deserialize<'de, D>(deserializer: D) -> Result<Option<Box<str>>, D::Error>
102where
103    D: serde::Deserializer<'de>,
104{
105    use ip2location_bin_format::content::UNKNOWN_STR;
106    use serde::Deserialize as _;
107
108    let s = Box::<str>::deserialize(deserializer)?;
109    if s == UNKNOWN_STR.into() {
110        Ok(None)
111    } else {
112        Ok(Some(s))
113    }
114}
115
116#[cfg(feature = "serde")]
117fn option_usize_deserialize<'de, D>(deserializer: D) -> Result<Option<usize>, D::Error>
118where
119    D: serde::Deserializer<'de>,
120{
121    use ip2location_bin_format::content::UNKNOWN_STR;
122    use serde::Deserialize as _;
123
124    let s = Box::<str>::deserialize(deserializer)?;
125    if s == UNKNOWN_STR.into() {
126        Ok(None)
127    } else {
128        match s.parse::<usize>() {
129            Ok(v) => Ok(Some(v)),
130            Err(err) => Err(serde::de::Error::custom(err.to_string())),
131        }
132    }
133}
134
135impl Record {
136    pub(crate) fn with_empty(ip_from: IpAddr, ip_to: IpAddr) -> Self {
137        Self {
138            ip_from,
139            ip_to,
140            proxy_type: Default::default(),
141            country_code: Default::default(),
142            country_name: Default::default(),
143            region_name: Default::default(),
144            city_name: Default::default(),
145            isp: Default::default(),
146            domain: Default::default(),
147            usage_type: Default::default(),
148            asn: Default::default(),
149            as_name: Default::default(),
150            last_seen: Default::default(),
151            threat: Default::default(),
152            residential: Default::default(),
153            provider: Default::default(),
154        }
155    }
156}
157
158//
159pub(crate) struct OptionRecord(pub(crate) Option<Record>);
160
161impl
162    TryFrom<(
163        IpAddr,
164        IpAddr,
165        ip2location_bin_format::record_field::RecordFieldContents,
166    )> for OptionRecord
167{
168    type Error = Box<str>;
169
170    fn try_from(
171        (ip_from, ip_to, record_field_contents): (
172            IpAddr,
173            IpAddr,
174            ip2location_bin_format::record_field::RecordFieldContents,
175        ),
176    ) -> Result<Self, Self::Error> {
177        use ip2location_bin_format::record_field::RecordFieldContent;
178
179        let mut record = Record::with_empty(ip_from, ip_to);
180
181        for record_field_content in record_field_contents.iter() {
182            match record_field_content {
183                RecordFieldContent::COUNTRY(_, v, v_name) => {
184                    if let Some(v) = v {
185                        record.country_code = v
186                            .parse::<CountryCode>()
187                            .map_err(|err| Box::<str>::from(err.to_string()))?;
188                    } else {
189                        return Ok(OptionRecord(None));
190                    }
191
192                    record.country_name = v_name.to_owned();
193                }
194                RecordFieldContent::REGION(_, v) => {
195                    record.region_name = v.to_owned();
196                }
197                RecordFieldContent::CITY(_, v) => {
198                    record.city_name = v.to_owned();
199                }
200                RecordFieldContent::ISP(_, v) => {
201                    record.isp = v.to_owned();
202                }
203                RecordFieldContent::DOMAIN(_, v) => {
204                    record.domain = v.to_owned();
205                }
206                //
207                RecordFieldContent::LATITUDE(_) => {
208                    return Err("Unknown field LATITUDE".into());
209                }
210                RecordFieldContent::LONGITUDE(_) => {
211                    return Err("Unknown field LONGITUDE".into());
212                }
213                RecordFieldContent::ZIPCODE(_, _) => {
214                    return Err("Unknown field ZIPCODE".into());
215                }
216                RecordFieldContent::TIMEZONE(_, _) => {
217                    return Err("Unknown field TIMEZONE".into());
218                }
219                RecordFieldContent::NETSPEED(_, _) => {
220                    return Err("Unknown field NETSPEED".into());
221                }
222                //
223                RecordFieldContent::PROXYTYPE(_, v) => {
224                    if let Some(v) = v {
225                        let v = v
226                            .parse::<ProxyType>()
227                            .map_err(|err| Box::<str>::from(err.to_string()))?;
228                        record.proxy_type = Some(v);
229                    }
230                }
231                RecordFieldContent::USAGETYPE(_, v) => {
232                    if let Some(v) = v {
233                        let v = v
234                            .parse::<UsageType>()
235                            .map_err(|err| Box::<str>::from(err.to_string()))?;
236                        record.usage_type = Some(v);
237                    }
238                }
239                RecordFieldContent::ASN(_, v) => {
240                    if let Some(v) = v {
241                        let v = v
242                            .parse::<usize>()
243                            .map_err(|err| Box::<str>::from(err.to_string()))?;
244                        record.asn = Some(v);
245                    }
246                }
247                RecordFieldContent::AS(_, v) => {
248                    record.as_name = v.to_owned();
249                }
250                RecordFieldContent::LASTSEEN(_, v) => {
251                    record.last_seen = v.to_owned();
252                }
253                RecordFieldContent::THREAT(_, v) => {
254                    record.threat = v.to_owned();
255                }
256                RecordFieldContent::RESIDENTIAL(_, v) => {
257                    record.residential = v.to_owned();
258                }
259                RecordFieldContent::PROVIDER(_, v) => {
260                    record.provider = v.to_owned();
261                }
262            }
263        }
264
265        Ok(OptionRecord(Some(record)))
266    }
267}
268
269//
270//
271//
272#[derive(Debug, Clone, Copy)]
273pub enum RecordField {
274    CountryCodeAndName,
275    RegionName,
276    CityName,
277    Isp,
278    Domain,
279    //
280    ProxyType,
281    UsageType,
282    Asn,
283    AsName,
284    LastSeen,
285    Threat,
286    Provider,
287    Residential,
288}
289
290impl From<&RecordField> for ip2location_bin_format::record_field::RecordField {
291    fn from(x: &RecordField) -> Self {
292        match x {
293            RecordField::CountryCodeAndName => Self::COUNTRY,
294            RecordField::RegionName => Self::REGION,
295            RecordField::CityName => Self::CITY,
296            RecordField::Isp => Self::ISP,
297            RecordField::Domain => Self::DOMAIN,
298            //
299            RecordField::ProxyType => Self::PROXYTYPE,
300            RecordField::UsageType => Self::USAGETYPE,
301            RecordField::Asn => Self::ASN,
302            RecordField::AsName => Self::AS,
303            RecordField::LastSeen => Self::LASTSEEN,
304            RecordField::Threat => Self::THREAT,
305            RecordField::Provider => Self::PROVIDER,
306            RecordField::Residential => Self::RESIDENTIAL,
307        }
308    }
309}