1use core::ops::Deref;
4use std::net::IpAddr;
5
6use country_code::CountryCode as CountryCodeInner;
7
8#[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#[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
166pub(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 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 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#[derive(Debug, Clone, Copy)]
267pub enum RecordField {
268 CountryCodeAndName,
269 RegionName,
270 CityName,
271 Isp,
272 Domain,
273 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 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}