11#![ 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+ //! ```
255
356use std:: cmp:: Ordering ;
457use std:: collections:: BTreeMap ;
@@ -200,7 +253,19 @@ impl<'de, T: Deserialize<'de>, S: AsRef<[u8]>> Iterator for Within<'de, T, S> {
200253 }
201254}
202255
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)
204269#[ derive( Debug ) ]
205270pub struct Reader < S : AsRef < [ u8 ] > > {
206271 buf : S ,
@@ -234,7 +299,8 @@ impl Reader<Vec<u8>> {
234299 /// # Example
235300 ///
236301 /// ```
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();
238304 /// ```
239305 pub fn open_readfile < P : AsRef < Path > > ( database : P ) -> Result < Reader < Vec < u8 > > , MaxMindDbError > {
240306 let buf: Vec < u8 > = fs:: read ( & database) ?; // IO error converted via #[from]
@@ -275,24 +341,53 @@ impl<'de, S: AsRef<[u8]>> Reader<S> {
275341 /// Lookup the socket address in the opened MaxMind DB.
276342 /// Returns `Ok(None)` if the address is not found in the database.
277343 ///
278- /// Example:
344+ /// # Examples
279345 ///
346+ /// Basic city lookup:
280347 /// ```
281348 /// # use maxminddb::geoip2;
282349 /// # use std::net::IpAddr;
283350 /// # use std::str::FromStr;
284351 /// # 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")?;
286354 ///
287355 /// 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),
292368 /// }
293369 /// # Ok(())
294370 /// # }
295371 /// ```
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+ /// ```
296391 pub fn lookup < T > ( & ' de self , address : IpAddr ) -> Result < Option < T > , MaxMindDbError >
297392 where
298393 T : Deserialize < ' de > ,
@@ -314,7 +409,8 @@ impl<'de, S: AsRef<[u8]>> Reader<S> {
314409 /// # use std::net::IpAddr;
315410 /// # use std::str::FromStr;
316411 /// # 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")?;
318414 ///
319415 /// let ip: IpAddr = "89.160.20.128".parse().unwrap(); // Known IP
320416 /// let ip_unknown: IpAddr = "10.0.0.1".parse().unwrap(); // Unknown IP
@@ -359,19 +455,50 @@ impl<'de, S: AsRef<[u8]>> Reader<S> {
359455
360456 /// Iterate over blocks of IP networks in the opened MaxMind DB
361457 ///
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
363463 ///
464+ /// Iterate over all IPv4 networks:
364465 /// ```
365466 /// use ipnetwork::IpNetwork;
366467 /// use maxminddb::{geoip2, Within};
367468 ///
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();
369490 ///
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),
375502 /// }
376503 /// ```
377504 pub fn within < T > ( & ' de self , cidr : IpNetwork ) -> Result < Within < ' de , T , S > , MaxMindDbError >
0 commit comments