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 (most common)
30//! if let Some(city) = reader.lookup::<geoip2::City>(ip)? {
31//!     if let Some(city_names) = city.city.and_then(|c| c.names) {
32//!         if let Some(city_name) = city_names.get("en") {
33//!             println!("City: {}", city_name);
34//!         }
35//!     }
36//!     if let Some(country_code) = city.country.and_then(|c| c.iso_code) {
37//!         println!("Country: {}", country_code);
38//!     }
39//! }
40//!
41//! // Country-only lookup (smaller/faster)
42//! if let Some(country) = reader.lookup::<geoip2::Country>(ip)? {
43//!     if let Some(country_names) = country.country.and_then(|c| c.names) {
44//!         if let Some(country_name) = country_names.get("en") {
45//!             println!("Country: {}", country_name);
46//!         }
47//!     }
48//! }
49//! # Ok(())
50//! # }
51//! ```
52
53use serde::{Deserialize, Serialize};
54
55/// GeoIP2 Country record
56#[derive(Deserialize, Serialize, Clone, Debug)]
57pub struct Country<'a> {
58    #[serde(borrow)]
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub continent: Option<country::Continent<'a>>,
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub country: Option<country::Country<'a>>,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub registered_country: Option<country::Country<'a>>,
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub represented_country: Option<country::RepresentedCountry<'a>>,
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub traits: Option<country::Traits>,
69}
70
71/// GeoIP2 City record
72#[derive(Deserialize, Serialize, Clone, Debug)]
73pub struct City<'a> {
74    #[serde(borrow)]
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub city: Option<city::City<'a>>,
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub continent: Option<city::Continent<'a>>,
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub country: Option<city::Country<'a>>,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub location: Option<city::Location<'a>>,
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub postal: Option<city::Postal<'a>>,
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub registered_country: Option<city::Country<'a>>,
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub represented_country: Option<city::RepresentedCountry<'a>>,
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub subdivisions: Option<Vec<city::Subdivision<'a>>>,
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub traits: Option<city::Traits>,
93}
94
95/// GeoIP2 Enterprise record
96#[derive(Deserialize, Serialize, Clone, Debug)]
97pub struct Enterprise<'a> {
98    #[serde(borrow)]
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub city: Option<enterprise::City<'a>>,
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub continent: Option<enterprise::Continent<'a>>,
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub country: Option<enterprise::Country<'a>>,
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub location: Option<enterprise::Location<'a>>,
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub postal: Option<enterprise::Postal<'a>>,
109    #[serde(skip_serializing_if = "Option::is_none")]
110    pub registered_country: Option<enterprise::Country<'a>>,
111    #[serde(skip_serializing_if = "Option::is_none")]
112    pub represented_country: Option<enterprise::RepresentedCountry<'a>>,
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub subdivisions: Option<Vec<enterprise::Subdivision<'a>>>,
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub traits: Option<enterprise::Traits<'a>>,
117}
118
119/// GeoIP2 ISP record
120#[derive(Deserialize, Serialize, Clone, Debug)]
121pub struct Isp<'a> {
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub autonomous_system_number: Option<u32>,
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub autonomous_system_organization: Option<&'a str>,
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub isp: Option<&'a str>,
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub mobile_country_code: Option<&'a str>,
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub mobile_network_code: Option<&'a str>,
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub organization: Option<&'a str>,
134}
135
136/// GeoIP2 Connection-Type record
137#[derive(Deserialize, Serialize, Clone, Debug)]
138pub struct ConnectionType<'a> {
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub connection_type: Option<&'a str>,
141}
142
143/// GeoIP2 Anonymous Ip record
144#[derive(Deserialize, Serialize, Clone, Debug)]
145pub struct AnonymousIp {
146    #[serde(skip_serializing_if = "Option::is_none")]
147    pub is_anonymous: Option<bool>,
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub is_anonymous_vpn: Option<bool>,
150    #[serde(skip_serializing_if = "Option::is_none")]
151    pub is_hosting_provider: Option<bool>,
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub is_public_proxy: Option<bool>,
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub is_residential_proxy: Option<bool>,
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub is_tor_exit_node: Option<bool>,
158}
159
160/// GeoIP2 DensityIncome record
161#[derive(Deserialize, Serialize, Clone, Debug)]
162pub struct DensityIncome {
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub average_income: Option<u32>,
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub population_density: Option<u32>,
167}
168
169/// GeoIP2 Domain record
170#[derive(Deserialize, Serialize, Clone, Debug)]
171pub struct Domain<'a> {
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub domain: Option<&'a str>,
174}
175
176/// GeoIP2 Asn record
177#[derive(Deserialize, Serialize, Clone, Debug)]
178pub struct Asn<'a> {
179    #[serde(skip_serializing_if = "Option::is_none")]
180    pub autonomous_system_number: Option<u32>,
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub autonomous_system_organization: Option<&'a str>,
183}
184
185/// Country model structs
186pub mod country {
187    use serde::{Deserialize, Serialize};
188    use std::collections::BTreeMap;
189
190    #[derive(Deserialize, Serialize, Clone, Debug)]
191    pub struct Continent<'a> {
192        #[serde(skip_serializing_if = "Option::is_none")]
193        pub code: Option<&'a str>,
194        #[serde(skip_serializing_if = "Option::is_none")]
195        pub geoname_id: Option<u32>,
196        #[serde(skip_serializing_if = "Option::is_none")]
197        pub names: Option<BTreeMap<&'a str, &'a str>>,
198    }
199
200    #[derive(Deserialize, Serialize, Clone, Debug)]
201    pub struct Country<'a> {
202        #[serde(skip_serializing_if = "Option::is_none")]
203        pub geoname_id: Option<u32>,
204        #[serde(skip_serializing_if = "Option::is_none")]
205        pub is_in_european_union: Option<bool>,
206        #[serde(skip_serializing_if = "Option::is_none")]
207        pub iso_code: Option<&'a str>,
208        #[serde(skip_serializing_if = "Option::is_none")]
209        pub names: Option<BTreeMap<&'a str, &'a str>>,
210    }
211
212    #[derive(Deserialize, Serialize, Clone, Debug)]
213    pub struct RepresentedCountry<'a> {
214        #[serde(skip_serializing_if = "Option::is_none")]
215        pub geoname_id: Option<u32>,
216        #[serde(skip_serializing_if = "Option::is_none")]
217        pub is_in_european_union: Option<bool>,
218        #[serde(skip_serializing_if = "Option::is_none")]
219        pub iso_code: Option<&'a str>,
220        #[serde(skip_serializing_if = "Option::is_none")]
221        pub names: Option<BTreeMap<&'a str, &'a str>>,
222        #[serde(rename = "type")]
223        #[serde(skip_serializing_if = "Option::is_none")]
224        pub representation_type: Option<&'a str>,
225    }
226
227    #[derive(Deserialize, Serialize, Clone, Debug)]
228    pub struct Traits {
229        #[serde(skip_serializing_if = "Option::is_none")]
230        pub is_anonymous_proxy: Option<bool>,
231        #[serde(skip_serializing_if = "Option::is_none")]
232        pub is_anycast: Option<bool>,
233        #[serde(skip_serializing_if = "Option::is_none")]
234        pub is_satellite_provider: Option<bool>,
235    }
236}
237
238/// City model structs
239pub mod city {
240    use serde::{Deserialize, Serialize};
241    use std::collections::BTreeMap;
242
243    pub use super::country::{Continent, Country, RepresentedCountry, Traits};
244
245    #[derive(Deserialize, Serialize, Clone, Debug)]
246    pub struct City<'a> {
247        #[serde(skip_serializing_if = "Option::is_none")]
248        pub geoname_id: Option<u32>,
249        #[serde(borrow)]
250        #[serde(skip_serializing_if = "Option::is_none")]
251        pub names: Option<BTreeMap<&'a str, &'a str>>,
252    }
253
254    #[derive(Deserialize, Serialize, Clone, Debug)]
255    pub struct Location<'a> {
256        #[serde(skip_serializing_if = "Option::is_none")]
257        pub accuracy_radius: Option<u16>,
258        #[serde(skip_serializing_if = "Option::is_none")]
259        pub latitude: Option<f64>,
260        #[serde(skip_serializing_if = "Option::is_none")]
261        pub longitude: Option<f64>,
262        #[serde(skip_serializing_if = "Option::is_none")]
263        pub metro_code: Option<u16>,
264        #[serde(skip_serializing_if = "Option::is_none")]
265        pub time_zone: Option<&'a str>,
266    }
267
268    #[derive(Deserialize, Serialize, Clone, Debug)]
269    pub struct Postal<'a> {
270        #[serde(skip_serializing_if = "Option::is_none")]
271        pub code: Option<&'a str>,
272    }
273
274    #[derive(Deserialize, Serialize, Clone, Debug)]
275    pub struct Subdivision<'a> {
276        #[serde(skip_serializing_if = "Option::is_none")]
277        pub geoname_id: Option<u32>,
278        #[serde(skip_serializing_if = "Option::is_none")]
279        pub iso_code: Option<&'a str>,
280        #[serde(skip_serializing_if = "Option::is_none")]
281        pub names: Option<BTreeMap<&'a str, &'a str>>,
282    }
283}
284
285/// Enterprise model structs
286pub mod enterprise {
287    use serde::{Deserialize, Serialize};
288    use std::collections::BTreeMap;
289
290    pub use super::country::{Continent, RepresentedCountry};
291
292    #[derive(Deserialize, Serialize, Clone, Debug)]
293    pub struct City<'a> {
294        #[serde(skip_serializing_if = "Option::is_none")]
295        pub confidence: Option<u8>,
296        #[serde(skip_serializing_if = "Option::is_none")]
297        pub geoname_id: Option<u32>,
298        #[serde(borrow)]
299        #[serde(skip_serializing_if = "Option::is_none")]
300        pub names: Option<BTreeMap<&'a str, &'a str>>,
301    }
302
303    #[derive(Deserialize, Serialize, Clone, Debug)]
304    pub struct Country<'a> {
305        #[serde(skip_serializing_if = "Option::is_none")]
306        pub confidence: Option<u8>,
307        #[serde(skip_serializing_if = "Option::is_none")]
308        pub geoname_id: Option<u32>,
309        #[serde(skip_serializing_if = "Option::is_none")]
310        pub is_in_european_union: Option<bool>,
311        #[serde(skip_serializing_if = "Option::is_none")]
312        pub iso_code: Option<&'a str>,
313        #[serde(skip_serializing_if = "Option::is_none")]
314        pub names: Option<BTreeMap<&'a str, &'a str>>,
315    }
316
317    #[derive(Deserialize, Serialize, Clone, Debug)]
318    pub struct Location<'a> {
319        #[serde(skip_serializing_if = "Option::is_none")]
320        pub accuracy_radius: Option<u16>,
321        #[serde(skip_serializing_if = "Option::is_none")]
322        pub latitude: Option<f64>,
323        #[serde(skip_serializing_if = "Option::is_none")]
324        pub longitude: Option<f64>,
325        #[serde(skip_serializing_if = "Option::is_none")]
326        pub metro_code: Option<u16>,
327        #[serde(skip_serializing_if = "Option::is_none")]
328        pub time_zone: Option<&'a str>,
329    }
330
331    #[derive(Deserialize, Serialize, Clone, Debug)]
332    pub struct Postal<'a> {
333        #[serde(skip_serializing_if = "Option::is_none")]
334        pub code: Option<&'a str>,
335        #[serde(skip_serializing_if = "Option::is_none")]
336        pub confidence: Option<u8>,
337    }
338
339    #[derive(Deserialize, Serialize, Clone, Debug)]
340    pub struct Subdivision<'a> {
341        #[serde(skip_serializing_if = "Option::is_none")]
342        pub confidence: Option<u8>,
343        #[serde(skip_serializing_if = "Option::is_none")]
344        pub geoname_id: Option<u32>,
345        #[serde(skip_serializing_if = "Option::is_none")]
346        pub iso_code: Option<&'a str>,
347        #[serde(skip_serializing_if = "Option::is_none")]
348        pub names: Option<BTreeMap<&'a str, &'a str>>,
349    }
350
351    #[derive(Deserialize, Serialize, Clone, Debug)]
352    pub struct Traits<'a> {
353        #[serde(skip_serializing_if = "Option::is_none")]
354        pub autonomous_system_number: Option<u32>,
355        #[serde(skip_serializing_if = "Option::is_none")]
356        pub autonomous_system_organization: Option<&'a str>,
357        #[serde(skip_serializing_if = "Option::is_none")]
358        pub connection_type: Option<&'a str>,
359        #[serde(skip_serializing_if = "Option::is_none")]
360        pub domain: Option<&'a str>,
361        #[serde(skip_serializing_if = "Option::is_none")]
362        pub is_anonymous: Option<bool>,
363        #[serde(skip_serializing_if = "Option::is_none")]
364        pub is_anonymous_proxy: Option<bool>,
365        #[serde(skip_serializing_if = "Option::is_none")]
366        pub is_anonymous_vpn: Option<bool>,
367        #[serde(skip_serializing_if = "Option::is_none")]
368        pub is_anycast: Option<bool>,
369        #[serde(skip_serializing_if = "Option::is_none")]
370        pub is_hosting_provider: Option<bool>,
371        #[serde(skip_serializing_if = "Option::is_none")]
372        pub isp: Option<&'a str>,
373        #[serde(skip_serializing_if = "Option::is_none")]
374        pub is_public_proxy: Option<bool>,
375        #[serde(skip_serializing_if = "Option::is_none")]
376        pub is_residential_proxy: Option<bool>,
377        #[serde(skip_serializing_if = "Option::is_none")]
378        pub is_satellite_provider: Option<bool>,
379        #[serde(skip_serializing_if = "Option::is_none")]
380        pub is_tor_exit_node: Option<bool>,
381        #[serde(skip_serializing_if = "Option::is_none")]
382        pub mobile_country_code: Option<&'a str>,
383        #[serde(skip_serializing_if = "Option::is_none")]
384        pub mobile_network_code: Option<&'a str>,
385        #[serde(skip_serializing_if = "Option::is_none")]
386        pub organization: Option<&'a str>,
387        #[serde(skip_serializing_if = "Option::is_none")]
388        pub user_type: Option<&'a str>,
389    }
390}