Skip to content

Commit 14e7872

Browse files
oschwaldclaude
andcommitted
Refactor GeoIP2 structs for ergonomic access
Replace BTreeMap-based names with typed Names struct using Option<&str> fields for each language. Make nested struct fields non-optional with Default, while keeping leaf values as Option<T> to preserve semantics. This eliminates nested Option unwrapping: - Old: city.city.as_ref().and_then(|c| c.names.english) - New: city.city.names.english Add is_empty() methods using *self == Self::default() pattern so they don't need updating when fields are added. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 8707834 commit 14e7872

File tree

6 files changed

+373
-181
lines changed

6 files changed

+373
-181
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,22 @@
6868
added for user input errors (e.g., looking up an IPv6 address in an IPv4-only
6969
database). Previously this returned `InvalidDatabase`, which incorrectly
7070
suggested the database was corrupted.
71+
- **BREAKING CHANGE:** The `names` fields in GeoIP2 structs now use a `Names`
72+
struct instead of `BTreeMap<&str, &str>`. This improves performance (no map
73+
allocation) and ergonomics. Each language field is `Option<&str>`:
74+
- Old: `city.names.as_ref().and_then(|n| n.get("en"))`
75+
- New: `city.city.names.english`
76+
- Supported languages: `german`, `english`, `spanish`, `french`, `japanese`,
77+
`brazilian_portuguese`, `russian`, `simplified_chinese`
78+
- **BREAKING CHANGE:** Nested struct fields in GeoIP2 record types (`City`,
79+
`Country`, `Enterprise`) are now non-optional with `Default`. This simplifies
80+
access patterns by removing nested Option unwrapping:
81+
- Old: `city.city.as_ref().and_then(|c| c.names.english)`
82+
- New: `city.city.names.english`
83+
- Old: `city.subdivisions.as_ref().map(|v| v.iter())`
84+
- New: `city.subdivisions.iter()` (empty Vec if not present)
85+
- Leaf values (strings, numbers, bools) remain `Option<T>` to preserve
86+
the distinction between "not present" and "present but empty"
7187
- Error messages now include byte offsets when available, making it easier to
7288
debug malformed databases. The `#[non_exhaustive]` attribute is added to
7389
`MaxMindDbError` to allow future additions without breaking changes.

examples/within.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
2525
continue; // Skip networks without data
2626
};
2727

28-
let continent = info.continent.and_then(|c| c.code).unwrap_or("");
29-
let country = info.country.and_then(|c| c.iso_code).unwrap_or("");
30-
let city = match info.city.and_then(|c| c.names) {
31-
Some(names) => names.get("en").copied().unwrap_or(""),
32-
None => "",
33-
};
28+
let continent = info.continent.code.unwrap_or("");
29+
let country = info.country.iso_code.unwrap_or("");
30+
let city = info.city.names.english.unwrap_or("");
3431
if !city.is_empty() {
3532
println!("{} {}-{}-{}", network, continent, country, city);
3633
} else if !country.is_empty() {

0 commit comments

Comments
 (0)