-
Notifications
You must be signed in to change notification settings - Fork 54
Rework API and modernize code #100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Owner
oschwald
commented
Nov 28, 2025
- Inline byte manipulation in read_node
- Add inline hints to lookup path functions
- Add LookupResult API with lazy decoding support
- Match Go reader network() behavior for IPv4/IPv6
- Add comprehensive test coverage matching Go reader
- Add iteration options matching Go's Networks/NetworksWithin
- Add verify() method for database integrity validation
- Add serde size hints for efficient collection pre-allocation
- Refactor: Use type constants throughout decoder
- Return false for is_human_readable() since MMDB is binary
- Implement deserialize_ignored_any for efficient value skipping
- Implement deserialize_enum for string-to-enum deserialization
- Add test for serde(flatten) attribute support
- Add changelog entry for serde deserializer improvements
- Add recursion depth limit matching libmaxminddb
- Include offset in decoder error messages
- Restructure error types with structured fields
- Modernize Rust idioms in codebase
- Reorganize code into modern module structure
Directly perform byte-to-usize conversion in read_node instead of calling the separate to_usize function. This makes the byte layout for each record size explicit and removes the now-unused to_usize function from the library. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add #[inline] hints to start_node and resolve_data_pointer functions which are called during the lookup hot path. While the compiler likely inlines these already, explicit hints make the optimization intent clear. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
BREAKING CHANGE: The lookup() method now returns LookupResult instead of Option<T>. Data is only deserialized when decode() is called. Migration: - Old: reader.lookup::<City>(ip)? returns Option<City> - New: reader.lookup(ip)?.decode::<City>()? returns City - Check if found: reader.lookup(ip)?.found() Other breaking changes: - lookup_prefix() removed - use result.network() instead - Within iterator now yields LookupResult instead of WithinItem<T> New features: - LookupResult with found(), network(), offset(), decode(), decode_path(), and decoder() methods - PathElement enum for navigating nested structures with Python-style negative indexing - Low-level Decoder API (Kind, MapReader, ArrayReader) for FFI bindings and custom deserialization 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- IPv4 lookups return IPv4 networks with correct prefix length - IPv6 lookups preserve IPv6 form (including IPv4-mapped addresses) - Handle no-ipv4-search-tree databases by tracking ipv4_start_bit_depth - Add comprehensive test_lookup_network covering 11 edge cases 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add IPv6 in IPv4-only database validation (returns error like Go) - Add missing network test case for 0:0:0:0:ffff:ffff:ffff:ffff - Add comprehensive decode_path tests (array indexing, negative index) - Test nested path navigation (map.mapX.arrayX[1]) - Test non-existent key handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add WithinOptions struct with builder methods to control network iteration behavior: - include_aliased_networks(): Include IPv4 via IPv6 aliases - include_networks_without_data(): Include networks with no data - skip_empty_values(): Skip empty maps/arrays Add networks() convenience method for iterating all networks. Update within() to take options as second parameter. Breaking change: within(cidr) -> within(cidr, options) 🤖 Generated with [Claude Code](https://claude.ai/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive database verification matching Go's Verify() method: - Metadata validation (format version, required fields, value constraints) - Search tree traversal with cycle detection - Data section separator verification (16 zero bytes) - Data record validation at each pointed-to offset - Type-specific size validation for floats, integers, and booleans 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Implement size_hint() for SeqAccess and MapAccess traits in the decoder. This allows Vec, HashMap, and other collections to pre-allocate the correct capacity when deserializing, avoiding reallocations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Replace magic numbers with named constants for all MaxMind DB type codes. This improves readability and maintainability. Constants added: - TYPE_EXTENDED (0), TYPE_POINTER (1), TYPE_STRING (2) - TYPE_DOUBLE (3), TYPE_BYTES (4), TYPE_UINT16 (5) - TYPE_UINT32 (6), TYPE_MAP (7), TYPE_INT32 (8) - TYPE_UINT64 (9), TYPE_UINT128 (10), TYPE_ARRAY (11) - TYPE_BOOL (14), TYPE_FLOAT (15) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The serde Deserializer trait's is_human_readable() method defaults to true, but MMDB is a binary format. This affects how some types like Duration and IpAddr choose their serialization format. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
When deserializing into serde::de::IgnoredAny, we can now skip values efficiently without fully decoding them. This reuses the existing skip_value() method which advances the decoder offset based on the control byte without parsing strings, validating UTF-8, etc. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Users can now deserialize string values from MMDB into Rust enums using
serde's rename attribute:
#[derive(Deserialize)]
enum ConnType {
#[serde(rename = "Cable/DSL")]
CableDsl,
}
This adds EnumAccessor implementing EnumAccess and VariantAccess traits,
supporting unit, newtype, tuple, and struct variants.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Real-world GeoIP2/GeoLite2 databases don't contain u128 values, so #[serde(flatten)] with HashMap<String, IgnoredAny> works without issues. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add a maximum data structure depth of 512 when decoding, matching the limit used in libmaxminddb and the Go reader. This prevents stack overflow when decoding malformed databases with excessively nested maps, arrays, or pointer chains. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Decoding errors now include the current offset in the data section, making it easier to debug malformed or corrupt databases. For example: "Invalid database: unknown data type: 13 at offset 1234" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Change InvalidDatabase and Decoding variants to use structured fields
(message, offset, path) instead of a single String
- Add helper constructors: invalid_database(), invalid_database_at(),
decoding(), decoding_at(), decoding_at_path()
- Add offset() method to Decoder for exposing current position
- Add #[non_exhaustive] attribute to MaxMindDbError for future compat
- Update all error creation sites to use new constructors with offsets
- Follow Rust idiom: lowercase error messages without trailing punctuation
This is a breaking change - pattern matching code must be updated from
InvalidDatabase(msg) to InvalidDatabase { message, .. }.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Use then_some() for Option transformation in LookupResult::offset() - Replace FromStr::from_str() with .parse() throughout tests - Simplify test error handling: if let Err + unwrap -> expect() - Remove unused FromStr import - Use _ instead of unused variable in match guards 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Move from non-standard src/maxminddb/lib.rs to standard src/lib.rs layout. Split the monolithic lib.rs into focused modules: - error.rs: MaxMindDbError and helper functions - metadata.rs: Metadata struct - reader.rs: Reader struct and all implementations - within.rs: Within iterator, WithinOptions, and IpInt helper - result.rs: LookupResult and PathElement (already separate) - decoder.rs: Binary format decoder (already separate) - geoip2.rs: GeoIP2 data structures (already separate) The lib.rs now serves as the crate root with re-exports, following modern Rust conventions. No public API changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Fields should only be modified through builder methods, not directly. This ensures consistent option construction via the builder pattern. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Replace usize::MAX sentinel with Option<usize> for clearer semantics. This is an internal change that doesn't affect the public API. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Looking up an IPv6 address in an IPv4-only database is a user input error, not a database corruption issue. Adding a separate error variant makes this distinction clear and allows callers to handle these cases appropriately. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Use separate Index(usize) and IndexFromEnd(usize) variants instead of a signed integer. This is more idiomatic Rust and makes the intent clearer at the call site. - Index(0) is the first element - IndexFromEnd(0) is the last element 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The name has_data() more clearly describes what the method checks: whether the database contains data for this IP address. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add PartialEq and Eq to Metadata and WithinOptions for easier comparison and use in collections. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Instead of returning Err when the IP isn't found, decode() now returns Ok(None). This makes the "not found" case consistent with decode_path() and eliminates the need to check has_data() before decoding. - Ok(Some(T)) - found and decoded successfully - Ok(None) - IP not found in database - Err(e) - decoding error 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The macro converts string literals to Key elements, non-negative integers to Index elements, and negative integers to IndexFromEnd elements (e.g., -1 becomes the last element). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
When decode_path fails during navigation, the error now includes the path traversed up to the point of failure. This helps users understand where in a nested structure the error occurred. For example, decode_path(&["city", "names", 0]) failing at the index will show "path: /city/names/0" in the error message. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The derived Debug would print the entire database buffer (potentially gigabytes of binary data). The manual implementation shows only the metadata, which contains the useful information about the database. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
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>
Adds a convenience method to convert the build_epoch Unix timestamp to a SystemTime, matching the Go v2 library's BuildTime() method. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
These fields have been removed from MaxMind databases. Users should use the dedicated Anonymous IP database for anonymity detection. This matches the Go v2 library which also removed these fields. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive documentation for all GeoIP2 structs and fields: - Document all top-level record types (Country, City, Enterprise, etc.) - Add field descriptions with semantic meaning - Include links to Wikipedia for ISO codes and standards - Mark deprecated fields (metro_code) - Document confidence scores for Enterprise fields - Describe possible values for enum-like fields (connection_type, user_type) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- 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>
Decoder is not exported from the crate, so pub visibility was misleading. Changed to pub(crate) for clarity. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Reorganize changelog with categorized sections - Add UPGRADING.md with migration guide and code examples - Bump version to 0.27.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The function wraps memmap2's MmapOptions::map(), which is unsafe because undefined behavior can occur if the underlying file is modified while mapped. By marking open_mmap as unsafe, callers must acknowledge this safety requirement. Fixes #86 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.