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}