maxminddb/geoip2.rs
1//! GeoIP2 and GeoLite2 database record structures
2//!
3//! This module provides strongly-typed Rust structures that correspond to the
4//! various GeoIP2 and GeoLite2 database record formats.
5//!
6//! # Record Types
7//!
8//! - [`City`] - Complete city-level geolocation data (most comprehensive)
9//! - [`Country`] - Country-level geolocation data
10//! - [`Enterprise`] - Enterprise database with additional confidence scores
11//! - [`Isp`] - Internet Service Provider information
12//! - [`AnonymousIp`] - Anonymous proxy and VPN detection
13//! - [`ConnectionType`] - Connection type classification
14//! - [`Domain`] - Domain information
15//! - [`Asn`] - Autonomous System Number data
16//! - [`DensityIncome`] - Population density and income data
17//!
18//! # Usage Examples
19//!
20//! ```rust
21//! use maxminddb::{Reader, geoip2};
22//! use std::net::IpAddr;
23//!
24//! # fn main() -> Result<(), maxminddb::MaxMindDbError> {
25//! let reader = Reader::open_readfile(
26//! "test-data/test-data/GeoIP2-City-Test.mmdb")?;
27//! let ip: IpAddr = "89.160.20.128".parse().unwrap();
28//!
29//! // City lookup - nested structs are always present (default to empty)
30//! let result = reader.lookup(ip)?;
31//! if let Some(city) = result.decode::<geoip2::City>()? {
32//! // Direct access to nested structs - no Option unwrapping needed
33//! if let Some(name) = city.city.names.english {
34//! println!("City: {}", name);
35//! }
36//! if let Some(code) = city.country.iso_code {
37//! println!("Country: {}", code);
38//! }
39//! // Subdivisions is a Vec, empty if not present
40//! for sub in &city.subdivisions {
41//! if let Some(code) = sub.iso_code {
42//! println!("Subdivision: {}", code);
43//! }
44//! }
45//! }
46//!
47//! // Country-only lookup (smaller/faster)
48//! let result = reader.lookup(ip)?;
49//! if let Some(country) = result.decode::<geoip2::Country>()? {
50//! if let Some(name) = country.country.names.english {
51//! println!("Country: {}", name);
52//! }
53//! }
54//! # Ok(())
55//! # }
56//! ```
57
58use serde::{Deserialize, Serialize};
59
60/// Localized names for geographic entities.
61///
62/// Contains name translations in the languages supported by MaxMind databases.
63/// Access names directly via fields like `names.english` or `names.german`.
64/// Each field is `Option<&str>` - `None` if not available in that language.
65///
66/// # Example
67///
68/// ```
69/// use maxminddb::{Reader, geoip2};
70/// use std::net::IpAddr;
71///
72/// let reader = Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb").unwrap();
73/// let ip: IpAddr = "89.160.20.128".parse().unwrap();
74/// let result = reader.lookup(ip).unwrap();
75///
76/// if let Some(city) = result.decode::<geoip2::City>().unwrap() {
77/// // Access names directly - Option<&str>
78/// if let Some(name) = city.city.names.english {
79/// println!("City (en): {}", name);
80/// }
81/// if let Some(name) = city.city.names.german {
82/// println!("City (de): {}", name);
83/// }
84/// }
85/// ```
86#[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq, Eq)]
87pub struct Names<'a> {
88 /// German name (de)
89 #[serde(
90 borrow,
91 rename = "de",
92 default,
93 skip_serializing_if = "Option::is_none"
94 )]
95 pub german: Option<&'a str>,
96 /// English name (en)
97 #[serde(rename = "en", default, skip_serializing_if = "Option::is_none")]
98 pub english: Option<&'a str>,
99 /// Spanish name (es)
100 #[serde(rename = "es", default, skip_serializing_if = "Option::is_none")]
101 pub spanish: Option<&'a str>,
102 /// French name (fr)
103 #[serde(rename = "fr", default, skip_serializing_if = "Option::is_none")]
104 pub french: Option<&'a str>,
105 /// Japanese name (ja)
106 #[serde(rename = "ja", default, skip_serializing_if = "Option::is_none")]
107 pub japanese: Option<&'a str>,
108 /// Brazilian Portuguese name (pt-BR)
109 #[serde(rename = "pt-BR", default, skip_serializing_if = "Option::is_none")]
110 pub brazilian_portuguese: Option<&'a str>,
111 /// Russian name (ru)
112 #[serde(rename = "ru", default, skip_serializing_if = "Option::is_none")]
113 pub russian: Option<&'a str>,
114 /// Simplified Chinese name (zh-CN)
115 #[serde(rename = "zh-CN", default, skip_serializing_if = "Option::is_none")]
116 pub simplified_chinese: Option<&'a str>,
117}
118
119impl Names<'_> {
120 /// Returns true if all name fields are `None`.
121 #[must_use]
122 pub fn is_empty(&self) -> bool {
123 self.german.is_none()
124 && self.english.is_none()
125 && self.spanish.is_none()
126 && self.french.is_none()
127 && self.japanese.is_none()
128 && self.brazilian_portuguese.is_none()
129 && self.russian.is_none()
130 && self.simplified_chinese.is_none()
131 }
132}
133
134/// GeoIP2/GeoLite2 Country database record.
135///
136/// Contains country-level geolocation data for an IP address. This is the
137/// simplest geolocation record type, suitable when you only need country
138/// information.
139#[derive(Deserialize, Serialize, Clone, Debug, Default)]
140pub struct Country<'a> {
141 /// Continent data for the IP address.
142 #[serde(borrow, default, skip_serializing_if = "country::Continent::is_empty")]
143 pub continent: country::Continent<'a>,
144 /// Country where MaxMind believes the IP is located.
145 #[serde(default, skip_serializing_if = "country::Country::is_empty")]
146 pub country: country::Country<'a>,
147 /// Country where the ISP has registered the IP block.
148 /// May differ from `country` (e.g., for mobile networks or VPNs).
149 #[serde(default, skip_serializing_if = "country::Country::is_empty")]
150 pub registered_country: country::Country<'a>,
151 /// Country represented by users of this IP (e.g., military base or embassy).
152 #[serde(default, skip_serializing_if = "country::RepresentedCountry::is_empty")]
153 pub represented_country: country::RepresentedCountry<'a>,
154 /// Various traits associated with the IP address.
155 #[serde(default, skip_serializing_if = "country::Traits::is_empty")]
156 pub traits: country::Traits,
157}
158
159/// GeoIP2/GeoLite2 City database record.
160///
161/// Contains city-level geolocation data including location coordinates,
162/// postal code, subdivisions (states/provinces), and country information.
163/// This is the most comprehensive free geolocation record type.
164#[derive(Deserialize, Serialize, Clone, Debug, Default)]
165pub struct City<'a> {
166 /// City data for the IP address.
167 #[serde(borrow, default, skip_serializing_if = "city::City::is_empty")]
168 pub city: city::City<'a>,
169 /// Continent data for the IP address.
170 #[serde(default, skip_serializing_if = "city::Continent::is_empty")]
171 pub continent: city::Continent<'a>,
172 /// Country where MaxMind believes the IP is located.
173 #[serde(default, skip_serializing_if = "city::Country::is_empty")]
174 pub country: city::Country<'a>,
175 /// Location data including coordinates and time zone.
176 #[serde(default, skip_serializing_if = "city::Location::is_empty")]
177 pub location: city::Location<'a>,
178 /// Postal code data for the IP address.
179 #[serde(default, skip_serializing_if = "city::Postal::is_empty")]
180 pub postal: city::Postal<'a>,
181 /// Country where the ISP has registered the IP block.
182 #[serde(default, skip_serializing_if = "city::Country::is_empty")]
183 pub registered_country: city::Country<'a>,
184 /// Country represented by users of this IP (e.g., military base or embassy).
185 #[serde(default, skip_serializing_if = "city::RepresentedCountry::is_empty")]
186 pub represented_country: city::RepresentedCountry<'a>,
187 /// Subdivisions (states, provinces, etc.) ordered from largest to smallest.
188 /// For example, Oxford, UK would have England first, then Oxfordshire.
189 #[serde(default, skip_serializing_if = "Vec::is_empty")]
190 pub subdivisions: Vec<city::Subdivision<'a>>,
191 /// Various traits associated with the IP address.
192 #[serde(default, skip_serializing_if = "city::Traits::is_empty")]
193 pub traits: city::Traits,
194}
195
196/// GeoIP2 Enterprise database record.
197///
198/// Contains all City data plus additional confidence scores and traits.
199/// Enterprise records include confidence values (0-100) indicating MaxMind's
200/// certainty about the accuracy of each field.
201#[derive(Deserialize, Serialize, Clone, Debug, Default)]
202pub struct Enterprise<'a> {
203 /// City data with confidence score.
204 #[serde(borrow, default, skip_serializing_if = "enterprise::City::is_empty")]
205 pub city: enterprise::City<'a>,
206 /// Continent data for the IP address.
207 #[serde(default, skip_serializing_if = "enterprise::Continent::is_empty")]
208 pub continent: enterprise::Continent<'a>,
209 /// Country data with confidence score.
210 #[serde(default, skip_serializing_if = "enterprise::Country::is_empty")]
211 pub country: enterprise::Country<'a>,
212 /// Location data including coordinates and time zone.
213 #[serde(default, skip_serializing_if = "enterprise::Location::is_empty")]
214 pub location: enterprise::Location<'a>,
215 /// Postal code data with confidence score.
216 #[serde(default, skip_serializing_if = "enterprise::Postal::is_empty")]
217 pub postal: enterprise::Postal<'a>,
218 /// Country where the ISP has registered the IP block.
219 #[serde(default, skip_serializing_if = "enterprise::Country::is_empty")]
220 pub registered_country: enterprise::Country<'a>,
221 /// Country represented by users of this IP (e.g., military base or embassy).
222 #[serde(
223 default,
224 skip_serializing_if = "enterprise::RepresentedCountry::is_empty"
225 )]
226 pub represented_country: enterprise::RepresentedCountry<'a>,
227 /// Subdivisions with confidence scores, ordered from largest to smallest.
228 #[serde(default, skip_serializing_if = "Vec::is_empty")]
229 pub subdivisions: Vec<enterprise::Subdivision<'a>>,
230 /// Extended traits including ISP, organization, and connection information.
231 #[serde(default, skip_serializing_if = "enterprise::Traits::is_empty")]
232 pub traits: enterprise::Traits<'a>,
233}
234
235/// GeoIP2 ISP database record.
236///
237/// Contains Internet Service Provider and organization information for an IP.
238#[derive(Deserialize, Serialize, Clone, Debug)]
239pub struct Isp<'a> {
240 /// The autonomous system number (ASN) for the IP address.
241 #[serde(skip_serializing_if = "Option::is_none")]
242 pub autonomous_system_number: Option<u32>,
243 /// The organization associated with the registered ASN.
244 #[serde(skip_serializing_if = "Option::is_none")]
245 pub autonomous_system_organization: Option<&'a str>,
246 /// The name of the ISP associated with the IP address.
247 #[serde(skip_serializing_if = "Option::is_none")]
248 pub isp: Option<&'a str>,
249 /// The mobile country code (MCC) associated with the IP.
250 /// See <https://en.wikipedia.org/wiki/Mobile_country_code>.
251 #[serde(skip_serializing_if = "Option::is_none")]
252 pub mobile_country_code: Option<&'a str>,
253 /// The mobile network code (MNC) associated with the IP.
254 /// See <https://en.wikipedia.org/wiki/Mobile_network_code>.
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub mobile_network_code: Option<&'a str>,
257 /// The name of the organization associated with the IP address.
258 #[serde(skip_serializing_if = "Option::is_none")]
259 pub organization: Option<&'a str>,
260}
261
262/// GeoIP2 Connection-Type database record.
263///
264/// Contains the connection type for an IP address.
265#[derive(Deserialize, Serialize, Clone, Debug)]
266pub struct ConnectionType<'a> {
267 /// The connection type. Possible values include "Dialup", "Cable/DSL",
268 /// "Corporate", "Cellular", and "Satellite". Additional values may be
269 /// added in the future.
270 #[serde(skip_serializing_if = "Option::is_none")]
271 pub connection_type: Option<&'a str>,
272}
273
274/// GeoIP2 Anonymous IP database record.
275///
276/// Contains information about whether an IP address is associated with
277/// anonymous or proxy services.
278#[derive(Deserialize, Serialize, Clone, Debug)]
279pub struct AnonymousIp {
280 /// True if the IP belongs to any sort of anonymous network.
281 #[serde(skip_serializing_if = "Option::is_none")]
282 pub is_anonymous: Option<bool>,
283 /// True if the IP is registered to an anonymous VPN provider.
284 /// Note: If a VPN provider does not register subnets under names associated
285 /// with them, we will likely only flag their IP ranges using `is_hosting_provider`.
286 #[serde(skip_serializing_if = "Option::is_none")]
287 pub is_anonymous_vpn: Option<bool>,
288 /// True if the IP belongs to a hosting or VPN provider.
289 #[serde(skip_serializing_if = "Option::is_none")]
290 pub is_hosting_provider: Option<bool>,
291 /// True if the IP belongs to a public proxy.
292 #[serde(skip_serializing_if = "Option::is_none")]
293 pub is_public_proxy: Option<bool>,
294 /// True if the IP is on a suspected anonymizing network and belongs to
295 /// a residential ISP.
296 #[serde(skip_serializing_if = "Option::is_none")]
297 pub is_residential_proxy: Option<bool>,
298 /// True if the IP is a Tor exit node.
299 #[serde(skip_serializing_if = "Option::is_none")]
300 pub is_tor_exit_node: Option<bool>,
301}
302
303/// GeoIP2 DensityIncome database record.
304///
305/// Contains population density and income data for an IP address location.
306#[derive(Deserialize, Serialize, Clone, Debug)]
307pub struct DensityIncome {
308 /// The average income in US dollars associated with the IP address.
309 #[serde(skip_serializing_if = "Option::is_none")]
310 pub average_income: Option<u32>,
311 /// The estimated number of people per square kilometer.
312 #[serde(skip_serializing_if = "Option::is_none")]
313 pub population_density: Option<u32>,
314}
315
316/// GeoIP2 Domain database record.
317///
318/// Contains the second-level domain associated with an IP address.
319#[derive(Deserialize, Serialize, Clone, Debug)]
320pub struct Domain<'a> {
321 /// The second-level domain associated with the IP address
322 /// (e.g., "example.com").
323 #[serde(skip_serializing_if = "Option::is_none")]
324 pub domain: Option<&'a str>,
325}
326
327/// GeoLite2 ASN database record.
328///
329/// Contains Autonomous System Number (ASN) data for an IP address.
330#[derive(Deserialize, Serialize, Clone, Debug)]
331pub struct Asn<'a> {
332 /// The autonomous system number for the IP address.
333 #[serde(skip_serializing_if = "Option::is_none")]
334 pub autonomous_system_number: Option<u32>,
335 /// The organization associated with the registered ASN.
336 #[serde(skip_serializing_if = "Option::is_none")]
337 pub autonomous_system_organization: Option<&'a str>,
338}
339
340/// Country/City database model structs.
341///
342/// These structs are used by both [`super::Country`] and [`super::City`] records.
343pub mod country {
344 use super::Names;
345 use serde::{Deserialize, Serialize};
346
347 /// Continent data for an IP address.
348 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
349 pub struct Continent<'a> {
350 /// Two-character continent code (e.g., "NA" for North America, "EU" for Europe).
351 #[serde(default, skip_serializing_if = "Option::is_none")]
352 pub code: Option<&'a str>,
353 /// GeoNames ID for the continent.
354 #[serde(default, skip_serializing_if = "Option::is_none")]
355 pub geoname_id: Option<u32>,
356 /// Localized continent names.
357 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
358 pub names: Names<'a>,
359 }
360
361 impl Continent<'_> {
362 /// Returns true if all fields are empty/None.
363 #[must_use]
364 pub fn is_empty(&self) -> bool {
365 *self == Self::default()
366 }
367 }
368
369 /// Country data for an IP address.
370 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
371 pub struct Country<'a> {
372 /// GeoNames ID for the country.
373 #[serde(default, skip_serializing_if = "Option::is_none")]
374 pub geoname_id: Option<u32>,
375 /// True if the country is a member state of the European Union.
376 #[serde(default, skip_serializing_if = "Option::is_none")]
377 pub is_in_european_union: Option<bool>,
378 /// Two-character ISO 3166-1 alpha-2 country code.
379 /// See <https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2>.
380 #[serde(default, skip_serializing_if = "Option::is_none")]
381 pub iso_code: Option<&'a str>,
382 /// Localized country names.
383 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
384 pub names: Names<'a>,
385 }
386
387 impl Country<'_> {
388 /// Returns true if all fields are empty/None.
389 #[must_use]
390 pub fn is_empty(&self) -> bool {
391 *self == Self::default()
392 }
393 }
394
395 /// Represented country data.
396 ///
397 /// The represented country is the country represented by something like a
398 /// military base or embassy.
399 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
400 pub struct RepresentedCountry<'a> {
401 /// GeoNames ID for the represented country.
402 #[serde(default, skip_serializing_if = "Option::is_none")]
403 pub geoname_id: Option<u32>,
404 /// True if the represented country is a member state of the European Union.
405 #[serde(default, skip_serializing_if = "Option::is_none")]
406 pub is_in_european_union: Option<bool>,
407 /// Two-character ISO 3166-1 alpha-2 country code.
408 /// See <https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2>.
409 #[serde(default, skip_serializing_if = "Option::is_none")]
410 pub iso_code: Option<&'a str>,
411 /// Localized country names.
412 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
413 pub names: Names<'a>,
414 /// Type of entity representing the country (e.g., "military").
415 #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
416 pub representation_type: Option<&'a str>,
417 }
418
419 impl RepresentedCountry<'_> {
420 /// Returns true if all fields are empty/None.
421 #[must_use]
422 pub fn is_empty(&self) -> bool {
423 *self == Self::default()
424 }
425 }
426
427 /// Traits data for Country/City records.
428 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
429 pub struct Traits {
430 /// True if the IP belongs to an anycast network.
431 /// See <https://en.wikipedia.org/wiki/Anycast>.
432 #[serde(default, skip_serializing_if = "Option::is_none")]
433 pub is_anycast: Option<bool>,
434 }
435
436 impl Traits {
437 /// Returns true if all fields are None.
438 #[must_use]
439 pub fn is_empty(&self) -> bool {
440 *self == Self::default()
441 }
442 }
443}
444
445/// City database model structs.
446///
447/// City-specific structs. Country-level structs are re-exported from [`super::country`].
448pub mod city {
449 use super::Names;
450 use serde::{Deserialize, Serialize};
451
452 pub use super::country::{Continent, Country, RepresentedCountry, Traits};
453
454 /// City data for an IP address.
455 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
456 pub struct City<'a> {
457 /// GeoNames ID for the city.
458 #[serde(default, skip_serializing_if = "Option::is_none")]
459 pub geoname_id: Option<u32>,
460 /// Localized city names.
461 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
462 pub names: Names<'a>,
463 }
464
465 impl City<'_> {
466 /// Returns true if all fields are empty/None.
467 #[must_use]
468 pub fn is_empty(&self) -> bool {
469 *self == Self::default()
470 }
471 }
472
473 /// Location data for an IP address.
474 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
475 pub struct Location<'a> {
476 /// Approximate accuracy radius in kilometers around the coordinates.
477 /// This is the radius where we have a 67% confidence that the device
478 /// using the IP address resides within.
479 #[serde(default, skip_serializing_if = "Option::is_none")]
480 pub accuracy_radius: Option<u16>,
481 /// Approximate latitude of the location. This value is not precise and
482 /// should not be used to identify a particular address or household.
483 #[serde(default, skip_serializing_if = "Option::is_none")]
484 pub latitude: Option<f64>,
485 /// Approximate longitude of the location. This value is not precise and
486 /// should not be used to identify a particular address or household.
487 #[serde(default, skip_serializing_if = "Option::is_none")]
488 pub longitude: Option<f64>,
489 /// Metro code for the location, used for targeting advertisements.
490 ///
491 /// **Deprecated:** Metro codes are no longer maintained and should not be used.
492 #[serde(default, skip_serializing_if = "Option::is_none")]
493 pub metro_code: Option<u16>,
494 /// Time zone associated with the location, as specified by the
495 /// IANA Time Zone Database (e.g., "America/New_York").
496 #[serde(default, skip_serializing_if = "Option::is_none")]
497 pub time_zone: Option<&'a str>,
498 }
499
500 impl Location<'_> {
501 /// Returns true if all fields are None.
502 #[must_use]
503 pub fn is_empty(&self) -> bool {
504 *self == Self::default()
505 }
506 }
507
508 /// Postal data for an IP address.
509 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
510 pub struct Postal<'a> {
511 /// Postal code for the location. Not available for all countries.
512 /// In some countries, this will only contain part of the postal code.
513 #[serde(default, skip_serializing_if = "Option::is_none")]
514 pub code: Option<&'a str>,
515 }
516
517 impl Postal<'_> {
518 /// Returns true if all fields are None.
519 #[must_use]
520 pub fn is_empty(&self) -> bool {
521 *self == Self::default()
522 }
523 }
524
525 /// Subdivision (state, province, etc.) data for an IP address.
526 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
527 pub struct Subdivision<'a> {
528 /// GeoNames ID for the subdivision.
529 #[serde(default, skip_serializing_if = "Option::is_none")]
530 pub geoname_id: Option<u32>,
531 /// ISO 3166-2 subdivision code (up to 3 characters).
532 /// See <https://en.wikipedia.org/wiki/ISO_3166-2>.
533 #[serde(default, skip_serializing_if = "Option::is_none")]
534 pub iso_code: Option<&'a str>,
535 /// Localized subdivision names.
536 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
537 pub names: Names<'a>,
538 }
539
540 impl Subdivision<'_> {
541 /// Returns true if all fields are empty/None.
542 #[must_use]
543 pub fn is_empty(&self) -> bool {
544 *self == Self::default()
545 }
546 }
547}
548
549/// Enterprise database model structs.
550///
551/// Enterprise-specific structs with confidence scores. Some structs are
552/// re-exported from [`super::country`].
553pub mod enterprise {
554 use super::Names;
555 use serde::{Deserialize, Serialize};
556
557 pub use super::country::{Continent, RepresentedCountry};
558
559 /// City data with confidence score.
560 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
561 pub struct City<'a> {
562 /// Confidence score (0-100) indicating MaxMind's certainty that the
563 /// city is correct.
564 #[serde(default, skip_serializing_if = "Option::is_none")]
565 pub confidence: Option<u8>,
566 /// GeoNames ID for the city.
567 #[serde(default, skip_serializing_if = "Option::is_none")]
568 pub geoname_id: Option<u32>,
569 /// Localized city names.
570 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
571 pub names: Names<'a>,
572 }
573
574 impl City<'_> {
575 /// Returns true if all fields are empty/None.
576 #[must_use]
577 pub fn is_empty(&self) -> bool {
578 *self == Self::default()
579 }
580 }
581
582 /// Country data with confidence score.
583 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
584 pub struct Country<'a> {
585 /// Confidence score (0-100) indicating MaxMind's certainty that the
586 /// country is correct.
587 #[serde(default, skip_serializing_if = "Option::is_none")]
588 pub confidence: Option<u8>,
589 /// GeoNames ID for the country.
590 #[serde(default, skip_serializing_if = "Option::is_none")]
591 pub geoname_id: Option<u32>,
592 /// True if the country is a member state of the European Union.
593 #[serde(default, skip_serializing_if = "Option::is_none")]
594 pub is_in_european_union: Option<bool>,
595 /// Two-character ISO 3166-1 alpha-2 country code.
596 /// See <https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2>.
597 #[serde(default, skip_serializing_if = "Option::is_none")]
598 pub iso_code: Option<&'a str>,
599 /// Localized country names.
600 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
601 pub names: Names<'a>,
602 }
603
604 impl Country<'_> {
605 /// Returns true if all fields are empty/None.
606 #[must_use]
607 pub fn is_empty(&self) -> bool {
608 *self == Self::default()
609 }
610 }
611
612 /// Location data for an IP address.
613 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
614 pub struct Location<'a> {
615 /// Approximate accuracy radius in kilometers around the coordinates.
616 /// This is the radius where we have a 67% confidence that the device
617 /// using the IP address resides within.
618 #[serde(default, skip_serializing_if = "Option::is_none")]
619 pub accuracy_radius: Option<u16>,
620 /// Approximate latitude of the location. This value is not precise and
621 /// should not be used to identify a particular address or household.
622 #[serde(default, skip_serializing_if = "Option::is_none")]
623 pub latitude: Option<f64>,
624 /// Approximate longitude of the location. This value is not precise and
625 /// should not be used to identify a particular address or household.
626 #[serde(default, skip_serializing_if = "Option::is_none")]
627 pub longitude: Option<f64>,
628 /// Metro code for the location, used for targeting advertisements.
629 ///
630 /// **Deprecated:** Metro codes are no longer maintained and should not be used.
631 #[serde(default, skip_serializing_if = "Option::is_none")]
632 pub metro_code: Option<u16>,
633 /// Time zone associated with the location, as specified by the
634 /// IANA Time Zone Database (e.g., "America/New_York").
635 #[serde(default, skip_serializing_if = "Option::is_none")]
636 pub time_zone: Option<&'a str>,
637 }
638
639 impl Location<'_> {
640 /// Returns true if all fields are None.
641 #[must_use]
642 pub fn is_empty(&self) -> bool {
643 *self == Self::default()
644 }
645 }
646
647 /// Postal data with confidence score.
648 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
649 pub struct Postal<'a> {
650 /// Postal code for the location. Not available for all countries.
651 /// In some countries, this will only contain part of the postal code.
652 #[serde(default, skip_serializing_if = "Option::is_none")]
653 pub code: Option<&'a str>,
654 /// Confidence score (0-100) indicating MaxMind's certainty that the
655 /// postal code is correct.
656 #[serde(default, skip_serializing_if = "Option::is_none")]
657 pub confidence: Option<u8>,
658 }
659
660 impl Postal<'_> {
661 /// Returns true if all fields are None.
662 #[must_use]
663 pub fn is_empty(&self) -> bool {
664 *self == Self::default()
665 }
666 }
667
668 /// Subdivision data with confidence score.
669 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
670 pub struct Subdivision<'a> {
671 /// Confidence score (0-100) indicating MaxMind's certainty that the
672 /// subdivision is correct.
673 #[serde(default, skip_serializing_if = "Option::is_none")]
674 pub confidence: Option<u8>,
675 /// GeoNames ID for the subdivision.
676 #[serde(default, skip_serializing_if = "Option::is_none")]
677 pub geoname_id: Option<u32>,
678 /// ISO 3166-2 subdivision code (up to 3 characters).
679 /// See <https://en.wikipedia.org/wiki/ISO_3166-2>.
680 #[serde(default, skip_serializing_if = "Option::is_none")]
681 pub iso_code: Option<&'a str>,
682 /// Localized subdivision names.
683 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
684 pub names: Names<'a>,
685 }
686
687 impl Subdivision<'_> {
688 /// Returns true if all fields are empty/None.
689 #[must_use]
690 pub fn is_empty(&self) -> bool {
691 *self == Self::default()
692 }
693 }
694
695 /// Extended traits data for Enterprise records.
696 ///
697 /// Contains ISP, organization, connection type, and anonymity information.
698 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
699 pub struct Traits<'a> {
700 /// The autonomous system number (ASN) for the IP address.
701 #[serde(default, skip_serializing_if = "Option::is_none")]
702 pub autonomous_system_number: Option<u32>,
703 /// The organization associated with the registered ASN.
704 #[serde(default, skip_serializing_if = "Option::is_none")]
705 pub autonomous_system_organization: Option<&'a str>,
706 /// The connection type. Possible values include "Dialup", "Cable/DSL",
707 /// "Corporate", "Cellular", and "Satellite".
708 #[serde(default, skip_serializing_if = "Option::is_none")]
709 pub connection_type: Option<&'a str>,
710 /// The second-level domain associated with the IP address
711 /// (e.g., "example.com").
712 #[serde(default, skip_serializing_if = "Option::is_none")]
713 pub domain: Option<&'a str>,
714 /// True if the IP belongs to any sort of anonymous network.
715 #[serde(default, skip_serializing_if = "Option::is_none")]
716 pub is_anonymous: Option<bool>,
717 /// True if the IP is registered to an anonymous VPN provider.
718 #[serde(default, skip_serializing_if = "Option::is_none")]
719 pub is_anonymous_vpn: Option<bool>,
720 /// True if the IP belongs to an anycast network.
721 /// See <https://en.wikipedia.org/wiki/Anycast>.
722 #[serde(default, skip_serializing_if = "Option::is_none")]
723 pub is_anycast: Option<bool>,
724 /// True if the IP belongs to a hosting or VPN provider.
725 #[serde(default, skip_serializing_if = "Option::is_none")]
726 pub is_hosting_provider: Option<bool>,
727 /// The name of the ISP associated with the IP address.
728 #[serde(default, skip_serializing_if = "Option::is_none")]
729 pub isp: Option<&'a str>,
730 /// True if the IP belongs to a public proxy.
731 #[serde(default, skip_serializing_if = "Option::is_none")]
732 pub is_public_proxy: Option<bool>,
733 /// True if the IP is on a suspected anonymizing network and belongs to
734 /// a residential ISP.
735 #[serde(default, skip_serializing_if = "Option::is_none")]
736 pub is_residential_proxy: Option<bool>,
737 /// True if the IP is a Tor exit node.
738 #[serde(default, skip_serializing_if = "Option::is_none")]
739 pub is_tor_exit_node: Option<bool>,
740 /// The mobile country code (MCC) associated with the IP.
741 /// See <https://en.wikipedia.org/wiki/Mobile_country_code>.
742 #[serde(default, skip_serializing_if = "Option::is_none")]
743 pub mobile_country_code: Option<&'a str>,
744 /// The mobile network code (MNC) associated with the IP.
745 /// See <https://en.wikipedia.org/wiki/Mobile_network_code>.
746 #[serde(default, skip_serializing_if = "Option::is_none")]
747 pub mobile_network_code: Option<&'a str>,
748 /// The name of the organization associated with the IP address.
749 #[serde(default, skip_serializing_if = "Option::is_none")]
750 pub organization: Option<&'a str>,
751 /// The user type associated with the IP address. Possible values include
752 /// "business", "cafe", "cellular", "college", "government", "hosting",
753 /// "library", "military", "residential", "router", "school",
754 /// "search_engine_spider", and "traveler".
755 #[serde(default, skip_serializing_if = "Option::is_none")]
756 pub user_type: Option<&'a str>,
757 }
758
759 impl Traits<'_> {
760 /// Returns true if all fields are None.
761 #[must_use]
762 pub fn is_empty(&self) -> bool {
763 *self == Self::default()
764 }
765 }
766}