1
1
#![ deny( trivial_casts, trivial_numeric_casts, unused_import_braces) ]
2
+ //! # MaxMind DB Reader
3
+ //!
4
+ //! This library reads the MaxMind DB format, including the GeoIP2 and GeoLite2 databases.
5
+ //!
6
+ //! ## Features
7
+ //!
8
+ //! This crate provides several optional features for performance and functionality:
9
+ //!
10
+ //! - **`mmap`** (default: disabled): Enable memory-mapped file access for
11
+ //! better performance in long-running applications
12
+ //! - **`simdutf8`** (default: disabled): Use SIMD instructions for faster
13
+ //! UTF-8 validation during string decoding
14
+ //! - **`unsafe-str-decode`** (default: disabled): Skip UTF-8 validation
15
+ //! entirely for maximum performance (~20% faster lookups)
16
+ //!
17
+ //! **Note**: `simdutf8` and `unsafe-str-decode` are mutually exclusive.
18
+ //!
19
+ //! ## Database Compatibility
20
+ //!
21
+ //! This library supports all MaxMind DB format databases:
22
+ //! - **GeoIP2** databases (City, Country, Enterprise, ISP, etc.)
23
+ //! - **GeoLite2** databases (free versions)
24
+ //! - Custom MaxMind DB format databases
25
+ //!
26
+ //! ## Thread Safety
27
+ //!
28
+ //! The `Reader` is `Send` and `Sync`, making it safe to share across threads.
29
+ //! This makes it ideal for web servers and other concurrent applications.
30
+ //!
31
+ //! ## Quick Start
32
+ //!
33
+ //! ```rust
34
+ //! use maxminddb::{Reader, geoip2};
35
+ //! use std::net::IpAddr;
36
+ //!
37
+ //! fn main() -> Result<(), Box<dyn std::error::Error>> {
38
+ //! // Open database file
39
+ //! # let reader = Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb")?;
40
+ //! # /*
41
+ //! let reader = Reader::open_readfile("/path/to/GeoIP2-City.mmdb")?;
42
+ //! # */
43
+ //!
44
+ //! // Look up an IP address
45
+ //! let ip: IpAddr = "89.160.20.128".parse()?;
46
+ //! if let Some(city) = reader.lookup::<geoip2::City>(ip)? {
47
+ //! if let Some(country) = city.country {
48
+ //! println!("Country: {}", country.iso_code.unwrap_or("Unknown"));
49
+ //! }
50
+ //! }
51
+ //!
52
+ //! Ok(())
53
+ //! }
54
+ //! ```
2
55
3
56
use std:: cmp:: Ordering ;
4
57
use std:: collections:: BTreeMap ;
@@ -200,7 +253,19 @@ impl<'de, T: Deserialize<'de>, S: AsRef<[u8]>> Iterator for Within<'de, T, S> {
200
253
}
201
254
}
202
255
203
- /// A reader for the MaxMind DB format. The lifetime `'data` is tied to the lifetime of the underlying buffer holding the contents of the database file.
256
+ /// A reader for the MaxMind DB format. The lifetime `'data` is tied to the
257
+ /// lifetime of the underlying buffer holding the contents of the database file.
258
+ ///
259
+ /// The `Reader` supports both file-based and memory-mapped access to MaxMind
260
+ /// DB files, including GeoIP2 and GeoLite2 databases.
261
+ ///
262
+ /// # Features
263
+ ///
264
+ /// - **`mmap`**: Enable memory-mapped file access for better performance
265
+ /// - **`simdutf8`**: Use SIMD-accelerated UTF-8 validation (faster string
266
+ /// decoding)
267
+ /// - **`unsafe-str-decode`**: Skip UTF-8 validation entirely (unsafe, but
268
+ /// ~20% faster)
204
269
#[ derive( Debug ) ]
205
270
pub struct Reader < S : AsRef < [ u8 ] > > {
206
271
buf : S ,
@@ -234,7 +299,8 @@ impl Reader<Vec<u8>> {
234
299
/// # Example
235
300
///
236
301
/// ```
237
- /// let reader = maxminddb::Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb").unwrap();
302
+ /// let reader = maxminddb::Reader::open_readfile(
303
+ /// "test-data/test-data/GeoIP2-City-Test.mmdb").unwrap();
238
304
/// ```
239
305
pub fn open_readfile < P : AsRef < Path > > ( database : P ) -> Result < Reader < Vec < u8 > > , MaxMindDbError > {
240
306
let buf: Vec < u8 > = fs:: read ( & database) ?; // IO error converted via #[from]
@@ -275,24 +341,53 @@ impl<'de, S: AsRef<[u8]>> Reader<S> {
275
341
/// Lookup the socket address in the opened MaxMind DB.
276
342
/// Returns `Ok(None)` if the address is not found in the database.
277
343
///
278
- /// Example:
344
+ /// # Examples
279
345
///
346
+ /// Basic city lookup:
280
347
/// ```
281
348
/// # use maxminddb::geoip2;
282
349
/// # use std::net::IpAddr;
283
350
/// # use std::str::FromStr;
284
351
/// # fn main() -> Result<(), maxminddb::MaxMindDbError> {
285
- /// let reader = maxminddb::Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb")?;
352
+ /// let reader = maxminddb::Reader::open_readfile(
353
+ /// "test-data/test-data/GeoIP2-City-Test.mmdb")?;
286
354
///
287
355
/// let ip: IpAddr = FromStr::from_str("89.160.20.128").unwrap();
288
- /// if let Some(city) = reader.lookup::<geoip2::City>(ip)? {
289
- /// println!("{:?}", city);
290
- /// } else {
291
- /// println!("Address not found");
356
+ /// match reader.lookup::<geoip2::City>(ip)? {
357
+ /// Some(city) => {
358
+ /// if let Some(city_names) = city.city.and_then(|c| c.names) {
359
+ /// if let Some(name) = city_names.get("en") {
360
+ /// println!("City: {}", name);
361
+ /// }
362
+ /// }
363
+ /// if let Some(country) = city.country.and_then(|c| c.iso_code) {
364
+ /// println!("Country: {}", country);
365
+ /// }
366
+ /// }
367
+ /// None => println!("No data found for IP {}", ip),
292
368
/// }
293
369
/// # Ok(())
294
370
/// # }
295
371
/// ```
372
+ ///
373
+ /// Lookup with different record types:
374
+ /// ```
375
+ /// # use maxminddb::geoip2;
376
+ /// # use std::net::IpAddr;
377
+ /// # fn main() -> Result<(), maxminddb::MaxMindDbError> {
378
+ /// let reader = maxminddb::Reader::open_readfile(
379
+ /// "test-data/test-data/GeoIP2-City-Test.mmdb")?;
380
+ /// let ip: IpAddr = "89.160.20.128".parse().unwrap();
381
+ ///
382
+ /// // Different record types for the same IP
383
+ /// let city: Option<geoip2::City> = reader.lookup(ip)?;
384
+ /// let country: Option<geoip2::Country> = reader.lookup(ip)?;
385
+ ///
386
+ /// println!("City data available: {}", city.is_some());
387
+ /// println!("Country data available: {}", country.is_some());
388
+ /// # Ok(())
389
+ /// # }
390
+ /// ```
296
391
pub fn lookup < T > ( & ' de self , address : IpAddr ) -> Result < Option < T > , MaxMindDbError >
297
392
where
298
393
T : Deserialize < ' de > ,
@@ -314,7 +409,8 @@ impl<'de, S: AsRef<[u8]>> Reader<S> {
314
409
/// # use std::net::IpAddr;
315
410
/// # use std::str::FromStr;
316
411
/// # fn main() -> Result<(), maxminddb::MaxMindDbError> {
317
- /// let reader = maxminddb::Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb")?;
412
+ /// let reader = maxminddb::Reader::open_readfile(
413
+ /// "test-data/test-data/GeoIP2-City-Test.mmdb")?;
318
414
///
319
415
/// let ip: IpAddr = "89.160.20.128".parse().unwrap(); // Known IP
320
416
/// let ip_unknown: IpAddr = "10.0.0.1".parse().unwrap(); // Unknown IP
@@ -359,19 +455,50 @@ impl<'de, S: AsRef<[u8]>> Reader<S> {
359
455
360
456
/// Iterate over blocks of IP networks in the opened MaxMind DB
361
457
///
362
- /// Example:
458
+ /// This method returns an iterator that yields all IP network blocks that
459
+ /// fall within the specified CIDR range and have associated data in the
460
+ /// database.
461
+ ///
462
+ /// # Examples
363
463
///
464
+ /// Iterate over all IPv4 networks:
364
465
/// ```
365
466
/// use ipnetwork::IpNetwork;
366
467
/// use maxminddb::{geoip2, Within};
367
468
///
368
- /// let reader = maxminddb::Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb").unwrap();
469
+ /// let reader = maxminddb::Reader::open_readfile(
470
+ /// "test-data/test-data/GeoIP2-City-Test.mmdb").unwrap();
471
+ ///
472
+ /// let ipv4_all = IpNetwork::V4("0.0.0.0/0".parse().unwrap());
473
+ /// let mut count = 0;
474
+ /// for result in reader.within::<geoip2::City>(ipv4_all).unwrap() {
475
+ /// let item = result.unwrap();
476
+ /// let city_name = item.info.city.as_ref().and_then(|c| c.names.as_ref()).and_then(|n| n.get("en"));
477
+ /// println!("Network: {}, City: {:?}", item.ip_net, city_name);
478
+ /// count += 1;
479
+ /// if count >= 10 { break; } // Limit output for example
480
+ /// }
481
+ /// ```
482
+ ///
483
+ /// Search within a specific subnet:
484
+ /// ```
485
+ /// use ipnetwork::IpNetwork;
486
+ /// use maxminddb::geoip2;
487
+ ///
488
+ /// let reader = maxminddb::Reader::open_readfile(
489
+ /// "test-data/test-data/GeoIP2-City-Test.mmdb").unwrap();
369
490
///
370
- /// let ip_net = IpNetwork::V6("::/0".parse().unwrap());
371
- /// let mut iter: Within<geoip2::City, _> = reader.within(ip_net).unwrap();
372
- /// while let Some(next) = iter.next() {
373
- /// let item = next.unwrap();
374
- /// println!("ip_net={}, city={:?}", item.ip_net, item.info);
491
+ /// let subnet = IpNetwork::V4("192.168.0.0/16".parse().unwrap());
492
+ /// match reader.within::<geoip2::City>(subnet) {
493
+ /// Ok(iter) => {
494
+ /// for result in iter {
495
+ /// match result {
496
+ /// Ok(item) => println!("Found: {}", item.ip_net),
497
+ /// Err(e) => eprintln!("Error processing item: {}", e),
498
+ /// }
499
+ /// }
500
+ /// }
501
+ /// Err(e) => eprintln!("Failed to create iterator: {}", e),
375
502
/// }
376
503
/// ```
377
504
pub fn within < T > ( & ' de self , cidr : IpNetwork ) -> Result < Within < ' de , T , S > , MaxMindDbError >
0 commit comments