Skip to content

Commit b306a46

Browse files
oschwaldclaude
andcommitted
Improve documentation for README, Cargo.toml, and API
- Update README with inline example, features section, modern syntax - Fix Cargo.toml documentation URL to point to docs.rs - Add feature flag comments to Cargo.toml - Add module and struct documentation to decoder.rs - Expand Within iterator documentation with example - Add file-level docs and comments to example files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 736baa2 commit b306a46

File tree

6 files changed

+105
-17
lines changed

6 files changed

+105
-17
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,19 @@ readme = "README.md"
77
keywords = ["MaxMind", "GeoIP2", "GeoIP", "geolocation", "ip"]
88
categories = ["database", "network-programming"]
99
homepage = "https://github.com/oschwald/maxminddb-rust"
10-
documentation = "http://oschwald.github.io/maxminddb-rust/maxminddb/struct.Reader.html"
10+
documentation = "https://docs.rs/maxminddb"
1111
repository = "https://github.com/oschwald/maxminddb-rust"
1212
license = "ISC"
1313
include = ["/Cargo.toml", "/benches/*.rs", "/src/**/*.rs", "/README.md", "/LICENSE"]
1414
edition = "2021"
1515

1616
[features]
1717
default = []
18+
# SIMD-accelerated UTF-8 validation during string decoding
1819
simdutf8 = ["dep:simdutf8"]
20+
# Memory-mapped file access for better performance in long-running applications
1921
mmap = ["memmap2"]
22+
# Skip UTF-8 validation for maximum performance (mutually exclusive with simdutf8)
2023
unsafe-str-decode = []
2124

2225
[lib]

README.md

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,52 @@ Add this to your `Cargo.toml`:
2828

2929
```toml
3030
[dependencies]
31-
maxminddb = "0.26"
31+
maxminddb = "0.27"
3232
```
3333

34-
and this to your crate root:
34+
## Example
3535

3636
```rust
37-
extern crate maxminddb;
37+
use maxminddb::{geoip2, Reader};
38+
use std::net::IpAddr;
39+
40+
fn main() -> Result<(), Box<dyn std::error::Error>> {
41+
let reader = Reader::open_readfile("/path/to/GeoLite2-City.mmdb")?;
42+
43+
let ip: IpAddr = "89.160.20.128".parse()?;
44+
let result = reader.lookup(ip)?;
45+
46+
if let Some(city) = result.decode::<geoip2::City>()? {
47+
println!("Country: {}", city.country.iso_code.unwrap_or("N/A"));
48+
println!("City: {}", city.city.names.english.unwrap_or("N/A"));
49+
}
50+
51+
Ok(())
52+
}
3853
```
3954

40-
## API Documentation
55+
See the [examples](examples/) directory for more usage patterns.
4156

42-
The API docs are on [Docs.rs](https://docs.rs/maxminddb/latest/maxminddb/struct.Reader.html).
57+
## Features
4358

44-
## Example
59+
Optional features:
60+
61+
- **`mmap`**: Memory-mapped file access for long-running applications
62+
- **`simdutf8`**: SIMD-accelerated UTF-8 validation
63+
- **`unsafe-str-decode`**: Skip UTF-8 validation (requires trusted data)
64+
65+
Enable in `Cargo.toml`:
4566

46-
See [`examples/lookup.rs`](https://github.com/oschwald/maxminddb-rust/blob/main/examples/lookup.rs) for a basic example.
67+
```toml
68+
[dependencies]
69+
maxminddb = { version = "0.27", features = ["mmap"] }
70+
```
71+
72+
Note: `simdutf8` and `unsafe-str-decode` are mutually exclusive.
73+
74+
## Documentation
75+
76+
[API documentation on docs.rs](https://docs.rs/maxminddb)
4777

4878
## Benchmarks
4979

@@ -64,10 +94,6 @@ If [gnuplot](http://www.gnuplot.info/) is installed, Criterion.rs can generate
6494
an HTML report displaying the results of the benchmark under
6595
`target/criterion/report/index.html`.
6696

67-
Result of doing 100 random IP lookups:
68-
69-
![](/assets/pdf_small.svg)
70-
7197
## Contributing
7298

7399
Contributions welcome! Please fork the repository and open a pull request

examples/lookup.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1+
//! Basic IP lookup example.
2+
//!
3+
//! Usage: cargo run --example lookup <database.mmdb> <ip_address>
4+
15
use std::net::IpAddr;
26

37
use maxminddb::geoip2;
48

59
fn main() -> Result<(), Box<dyn std::error::Error>> {
10+
// Parse command line arguments
611
let mut args = std::env::args().skip(1);
712
let db_path = args
813
.next()
914
.ok_or("First argument must be the path to the IP database")?;
15+
16+
// Open the database file
1017
let reader = maxminddb::Reader::open_readfile(db_path)?;
1118

1219
let ip_str = args
@@ -16,15 +23,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
1623
.parse()
1724
.map_err(|e| format!("Invalid IP address '{}': {}", ip_str, e))?;
1825

26+
// Look up the IP address
1927
let result = reader.lookup(ip)?;
2028

29+
// Decode and display city data if present
2130
if let Some(city) = result.decode::<geoip2::City>()? {
2231
println!("City data for IP {}: {city:#?}", ip);
2332
} else {
2433
println!("No city data found for IP {}", ip);
2534
}
2635

27-
// Show the network (available regardless of whether data was found)
36+
// The network is always available, even when no data is found
2837
let network = result.network()?;
2938
println!("Network: {}", network);
3039
Ok(())

examples/within.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,44 @@
1+
//! Iterate over networks within a CIDR range.
2+
//!
3+
//! Usage: cargo run --example within <database.mmdb> <cidr>
4+
//!
5+
//! Example: cargo run --example within GeoLite2-City.mmdb "89.160.20.0/24"
6+
17
use ipnetwork::IpNetwork;
28
use maxminddb::{geoip2, Within};
39

410
fn main() -> Result<(), Box<dyn std::error::Error>> {
11+
// Parse command line arguments
512
let mut args = std::env::args().skip(1);
613
let db_path = args
714
.next()
815
.ok_or("First argument must be the path to the IP database")?;
16+
17+
// Open the database file
918
let reader = maxminddb::Reader::open_readfile(db_path)?;
1019

1120
let cidr_str = args.next().ok_or(
1221
"Second argument must be the IP address and mask in CIDR notation, e.g. 0.0.0.0/0 or ::/0",
1322
)?;
1423

24+
// Parse the CIDR notation
1525
let ip_net: IpNetwork = cidr_str
1626
.parse()
1727
.map_err(|e| format!("Invalid CIDR notation '{}': {}", cidr_str, e))?;
1828

29+
// Iterate over all networks within the specified range
1930
let mut n = 0;
2031
let iter: Within<_> = reader.within(ip_net, Default::default())?;
2132
for next in iter {
2233
let lookup = next?;
2334
let network = lookup.network()?;
35+
36+
// Skip networks without data
2437
let Some(info) = lookup.decode::<geoip2::City>()? else {
25-
continue; // Skip networks without data
38+
continue;
2639
};
2740

41+
// Display location hierarchy
2842
let continent = info.continent.code.unwrap_or("");
2943
let country = info.country.iso_code.unwrap_or("");
3044
let city = info.city.names.english.unwrap_or("");

src/decoder.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
//! Binary format decoder for MaxMind DB files.
2+
//!
3+
//! This module implements deserialization of the MaxMind DB binary format
4+
//! into Rust types via serde. The decoder handles all MaxMind DB data types
5+
//! including pointers, maps, arrays, and primitive types.
6+
//!
7+
//! Most users should not need to interact with this module directly.
8+
//! Use [`Reader::lookup()`](crate::Reader::lookup) for normal lookups.
9+
110
use log::debug;
211
use serde::de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor};
312
use serde::forward_to_deserialize_any;
@@ -47,6 +56,14 @@ enum Value<'a, 'de> {
4756
Array(ArrayAccess<'a, 'de>),
4857
}
4958

59+
/// Low-level decoder for MaxMind DB binary data.
60+
///
61+
/// This decoder implements serde's `Deserializer` trait to convert
62+
/// MaxMind DB binary format into Rust types. It handles pointer
63+
/// resolution, type coercion, and nested data structures.
64+
///
65+
/// Most users should use [`LookupResult::decode()`](crate::LookupResult::decode)
66+
/// instead of this type directly.
5067
#[derive(Debug)]
5168
pub struct Decoder<'de> {
5269
buf: &'de [u8],

src/within.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,28 @@ pub(crate) struct WithinNode {
8585

8686
/// Iterator over IP networks within a CIDR range.
8787
///
88-
/// This iterator yields [`LookupResult`] for each network in the database
89-
/// that falls within the specified CIDR range. Use [`LookupResult::decode()`]
90-
/// to deserialize the data for each result.
88+
/// Created by [`Reader::within()`](crate::Reader::within) or
89+
/// [`Reader::networks()`](crate::Reader::networks). Yields
90+
/// [`LookupResult`] for each network in the database that falls
91+
/// within the specified range.
92+
///
93+
/// Networks are yielded in depth-first order through the search tree.
94+
/// Use [`LookupResult::decode()`](crate::LookupResult::decode) to
95+
/// deserialize the data for each result.
96+
///
97+
/// # Example
98+
///
99+
/// ```
100+
/// use maxminddb::{Reader, WithinOptions, geoip2};
101+
///
102+
/// let reader = Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb").unwrap();
103+
/// for result in reader.within("89.160.20.0/24".parse().unwrap(), Default::default()).unwrap() {
104+
/// let lookup = result.unwrap();
105+
/// if let Some(city) = lookup.decode::<geoip2::City>().unwrap() {
106+
/// println!("{}: {:?}", lookup.network().unwrap(), city.city.names.english);
107+
/// }
108+
/// }
109+
/// ```
91110
#[derive(Debug)]
92111
pub struct Within<'de, S: AsRef<[u8]>> {
93112
pub(crate) reader: &'de Reader<S>,

0 commit comments

Comments
 (0)