ip2location_ip2location/
record.rs

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