Skip to content

Add generated MaxMind DB decoders - #221

Merged
oschwald merged 4 commits into
mainfrom
greg/generator-improvement-plan-7
Aug 8, 2026
Merged

Add generated MaxMind DB decoders#221
oschwald merged 4 commits into
mainfrom
greg/generator-improvement-plan-7

Conversation

@oschwald

@oschwald oschwald commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • add the optional maxminddb-gen module tool, which discovers exported structs from an input Go source file and writes deterministic _maxminddb.go decoders
  • add the pool-free cursor API used by generated decoders while preserving reflection decoding compatibility
  • harden generated and reflection decoding parity for malformed input and nested unmarshaling
  • document generation and add focused correctness, parity, fuzz, and performance coverage

Validation

  • go generate ./... twice with identical output
  • go test ./...
  • go test -race ./...
  • GOARCH=386 go test ./...
  • GOOS=windows go test -exec=true ./...
  • go vet ./...
  • golangci-lint fmt
  • golangci-lint run
  • focused cursor behavioral-parity fuzzing

Summary by CodeRabbit

  • New Features
    • Added optional maxminddb-gen workflow (with go:generate and reproducible setup) to generate reflection-free unmarshaling for application-owned structs.
    • Introduced public cursor-based decoding APIs and cursor-based custom unmarshaling with proven-successor traversal.
    • Added cursor/map/slice traversal helpers and exported UnexpectedKindError.
  • Bug Fixes
    • Improved cursor successor validation, nested unmarshaling precedence, and safer generated-output replacement/migration rules.
    • Corrected pointer-to-pointer decoding validation and key kind/type error routing.
  • Documentation
    • Expanded README and added maxminddb-gen/README.md with usage, output naming, and verification steps.
  • Tests
    • Added extensive cursor and generator test coverage, plus end-to-end generated fixture validation and benchmarks.

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The change adds successor-validated cursor decoding, cursor-first custom unmarshaling, optimized decoder paths, and maxminddb-gen for reflection-free methods on application-owned structs. It also adds generated fixtures, typed error helpers, validation, documentation, and tool configuration.

Changes

Cursor decoding and decoder integration

Layer / File(s) Summary
Cursor traversal and public contracts
internal/decoder/cursor.go, mmdbdata/*.go, internal/decoder/cursor_test.go
Adds successor-validated scalar, map, slice, skip, and custom-unmarshal cursor APIs with structural validation and traversal tests.
Decoder integration and validation
internal/decoder/*.go, internal/decoder/*_test.go
Reflection decoding prefers cursor unmarshaling; pooling, string/key fast paths, structured kind errors, rollback checks, and benchmarks are added.

Generator workflow

Layer / File(s) Summary
Generator CLI and target analysis
maxminddb-gen/main.go, maxminddb-gen/generate.go, maxminddb-gen/rename_*.go
Discovers exported structs, validates tags and supported type graphs, preserves build constraints, detects generated ownership, and atomically replaces owned output.
Generated decoder output and validation
maxminddb-gen/generate.go, maxminddb-gen/generate_test.go, maxminddb-gen/testdata/basic/*
Generates cursor and decoder methods for supported fields and validates deterministic output, runtime parity, malformed input, reuse, and output safety.
Public contracts and documentation
mmdbdata/*.go, README.md, CHANGELOG.md, maxminddb-gen/README.md, go.mod
Documents cursor and generator usage, output behavior, validation rules, typed errors, and reproducible tool setup.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Application
  participant maxminddbGen
  participant GeneratedDecoder
  participant Cursor
  Application->>maxminddbGen: Analyze source structs and generate methods
  maxminddbGen->>GeneratedDecoder: Emit cursor and decoder methods
  Application->>GeneratedDecoder: Decode an MMDB record
  GeneratedDecoder->>Cursor: Traverse maps, slices, and scalar values
  Cursor-->>GeneratedDecoder: Return validated successor cursors
  GeneratedDecoder-->>Application: Populate the destination struct
Loading

Possibly related PRs

Poem

A rabbit hops where cursors glide,
Through maps and slices side by side.
Generated methods bloom with care,
While errors point to places fair.
Hop, hop—decoding’s swift! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding generated MaxMind DB decoders.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch greg/generator-improvement-plan-7

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/decoder/decoder.go`:
- Around line 418-432: Update UnexpectedKindError.Error() to format Actual and
Expected using Kind’s String()-based representation instead of numeric
formatting. Preserve the existing message structure so errors include readable
names such as “unexpected kind String, expected Map.”

In `@maxminddb-gen/generate.go`:
- Around line 706-723: Update the *types.Slice branch in the generator’s
byte-slice fast path to require an unnamed built-in byte element type, or
convert each []byte element to the target named element type before appending.
Preserve the existing optimized append behavior for exact []byte fields while
ensuring named byte slices such as []Octet generate compilable code.

In `@mmdbdata/error.go`:
- Around line 1-25: Add focused unit tests for the public helpers
NewUnmarshalTypeError and NormalizeUnmarshalError in a dedicated error test
file. Verify the generated error category and type details, normalization of an
UnexpectedKindError including wrapped errors, and unchanged propagation of
unrelated errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7ea6884e-b553-4de0-8ae2-09da9d87cb1d

📥 Commits

Reviewing files that changed from the base of the PR and between 91542f2 and 5afd411.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (34)
  • CHANGELOG.md
  • README.md
  • go.mod
  • internal/decoder/cursor.go
  • internal/decoder/cursor_external_test.go
  • internal/decoder/cursor_parity_test.go
  • internal/decoder/cursor_test.go
  • internal/decoder/data_decoder.go
  • internal/decoder/data_decoder_test.go
  • internal/decoder/decoder.go
  • internal/decoder/error_context.go
  • internal/decoder/nested_unmarshaler_test.go
  • internal/decoder/performance_test.go
  • internal/decoder/reflection.go
  • internal/decoder/testdata/fuzz/FuzzCursorBehavioralParity/19981bffc2abbaf1
  • internal/decoder/testdata/fuzz/FuzzCursorBehavioralParity/56ce1d00fcf93ca2
  • internal/decoder/testdata/fuzz/FuzzCursorBehavioralParity/5cfddfe87fbd8efb
  • internal/decoder/testdata/fuzz/FuzzCursorBehavioralParity/830e6b1f358378c7
  • internal/decoder/testdata/fuzz/FuzzCursorBehavioralParity/a529f2bd56fdeb2e
  • internal/decoder/testdata/fuzz/FuzzCursorBehavioralParity/a56fb6b698176e1d
  • maxminddb-gen/README.md
  • maxminddb-gen/generate.go
  • maxminddb-gen/generate_test.go
  • maxminddb-gen/main.go
  • maxminddb-gen/rename_unix.go
  • maxminddb-gen/rename_windows.go
  • maxminddb-gen/testdata/basic/go.mod
  • maxminddb-gen/testdata/basic/model.go
  • maxminddb-gen/testdata/basic/model_maxminddb.go
  • maxminddb-gen/testdata/basic/model_test.go
  • mmdbdata/doc.go
  • mmdbdata/error.go
  • mmdbdata/interface.go
  • mmdbdata/type.go

Comment thread internal/decoder/decoder.go Outdated
Comment thread maxminddb-gen/generate.go Outdated
Comment thread mmdbdata/error.go
@oschwald
oschwald force-pushed the greg/generator-improvement-plan-7 branch from beb1a36 to 0c906da Compare July 13, 2026 02:27

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/decoder/performance_test.go`:
- Around line 150-178: Fix the unreachable slice fast path in Cursor.Slice by
reworking its kind detection to inspect the extended-kind byte before comparing
against KindSlice, or remove the dead branch if no valid fast path remains.
Ensure slice values no longer incorrectly fall through resolveCtrlData solely
because Kind(ctrlByte>>5) cannot represent KindSlice, and update
BenchmarkCursorOpenSmallContainers to exercise the intended path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b5524336-f092-4d46-856e-fbf4ca4776d9

📥 Commits

Reviewing files that changed from the base of the PR and between beb1a36 and 0c906da.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (28)
  • CHANGELOG.md
  • README.md
  • go.mod
  • internal/decoder/cursor.go
  • internal/decoder/cursor_external_test.go
  • internal/decoder/cursor_test.go
  • internal/decoder/data_decoder.go
  • internal/decoder/data_decoder_test.go
  • internal/decoder/decoder.go
  • internal/decoder/error_context.go
  • internal/decoder/nested_unmarshaler_test.go
  • internal/decoder/performance_test.go
  • internal/decoder/reflection.go
  • maxminddb-gen/README.md
  • maxminddb-gen/generate.go
  • maxminddb-gen/generate_test.go
  • maxminddb-gen/main.go
  • maxminddb-gen/rename_unix.go
  • maxminddb-gen/rename_windows.go
  • maxminddb-gen/testdata/basic/go.mod
  • maxminddb-gen/testdata/basic/model.go
  • maxminddb-gen/testdata/basic/model_maxminddb.go
  • maxminddb-gen/testdata/basic/model_test.go
  • mmdbdata/doc.go
  • mmdbdata/error.go
  • mmdbdata/error_test.go
  • mmdbdata/interface.go
  • mmdbdata/type.go

Comment thread internal/decoder/performance_test.go
@oschwald
oschwald force-pushed the greg/generator-improvement-plan-7 branch from 0c906da to c05e169 Compare July 13, 2026 02:57

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/decoder/data_decoder.go`:
- Around line 317-421: Replace the duplicated slow-path pointer resolution in
DataDecoder.decodeStringValue and decodeKey with DataDecoder.resolveCtrlData.
Use the helper’s resolved kind, size, data offset, and successor offset while
preserving existing pointer-to-pointer detection, error propagation, and type
validation behavior; leave the compact fast paths unchanged.

In `@README.md`:
- Around line 115-138: Update the go.mod example code fence in the README to use
go.mod or plain-text syntax instead of Go syntax, and remove the misleading //
go.mod marker from inside the block while preserving the tool directive content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2bd146cc-fa90-4339-8199-c7a413b88da9

📥 Commits

Reviewing files that changed from the base of the PR and between 0c906da and c05e169.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (28)
  • CHANGELOG.md
  • README.md
  • go.mod
  • internal/decoder/cursor.go
  • internal/decoder/cursor_external_test.go
  • internal/decoder/cursor_test.go
  • internal/decoder/data_decoder.go
  • internal/decoder/data_decoder_test.go
  • internal/decoder/decoder.go
  • internal/decoder/error_context.go
  • internal/decoder/nested_unmarshaler_test.go
  • internal/decoder/performance_test.go
  • internal/decoder/reflection.go
  • maxminddb-gen/README.md
  • maxminddb-gen/generate.go
  • maxminddb-gen/generate_test.go
  • maxminddb-gen/main.go
  • maxminddb-gen/rename_unix.go
  • maxminddb-gen/rename_windows.go
  • maxminddb-gen/testdata/basic/go.mod
  • maxminddb-gen/testdata/basic/model.go
  • maxminddb-gen/testdata/basic/model_maxminddb.go
  • maxminddb-gen/testdata/basic/model_test.go
  • mmdbdata/doc.go
  • mmdbdata/error.go
  • mmdbdata/error_test.go
  • mmdbdata/interface.go
  • mmdbdata/type.go

Comment thread internal/decoder/data_decoder.go
Comment thread README.md
@oschwald
oschwald force-pushed the greg/generator-improvement-plan-7 branch from c05e169 to 17f99d7 Compare July 13, 2026 13:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/decoder/reflection.go`:
- Around line 93-99: Update the top-level CursorUnmarshaler branch in the
reflection decoding switch to route the returned successor through
Cursor.UnmarshalCursor, matching nested decoding validation. Ensure foreign,
zero, or otherwise invalid successors are rejected while preserving the existing
error propagation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6384b84a-40f3-47ab-b628-df0ce6cc24e6

📥 Commits

Reviewing files that changed from the base of the PR and between c05e169 and 17f99d7.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (28)
  • CHANGELOG.md
  • README.md
  • go.mod
  • internal/decoder/cursor.go
  • internal/decoder/cursor_external_test.go
  • internal/decoder/cursor_test.go
  • internal/decoder/data_decoder.go
  • internal/decoder/data_decoder_test.go
  • internal/decoder/decoder.go
  • internal/decoder/error_context.go
  • internal/decoder/nested_unmarshaler_test.go
  • internal/decoder/performance_test.go
  • internal/decoder/reflection.go
  • maxminddb-gen/README.md
  • maxminddb-gen/generate.go
  • maxminddb-gen/generate_test.go
  • maxminddb-gen/main.go
  • maxminddb-gen/rename_unix.go
  • maxminddb-gen/rename_windows.go
  • maxminddb-gen/testdata/basic/go.mod
  • maxminddb-gen/testdata/basic/model.go
  • maxminddb-gen/testdata/basic/model_maxminddb.go
  • maxminddb-gen/testdata/basic/model_test.go
  • mmdbdata/doc.go
  • mmdbdata/error.go
  • mmdbdata/error_test.go
  • mmdbdata/interface.go
  • mmdbdata/type.go

Comment thread internal/decoder/reflection.go
@oschwald
oschwald force-pushed the greg/generator-improvement-plan-7 branch from 17f99d7 to 9af1db6 Compare August 1, 2026 16:32
@oschwald
oschwald force-pushed the greg/generator-improvement-plan-7 branch 2 times, most recently from 867c6b9 to 9db25f0 Compare August 1, 2026 19:22
@@ -15,6 +15,10 @@ func (d *Decoder) wrapError(err error) error {
// wrapErrorAtOffset wraps an error with context at a specific offset.
// Used when the error occurs at a different offset than the decoder's current position.
func (*Decoder) wrapErrorAtOffset(err error, offset uint) error {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like we could eliminate this method entirely and just inline the new function.

Comment thread README.md Outdated

Import `github.com/oschwald/maxminddb-golang/v2/mmdbdata` for the decoder type.
For application-owned structs, `maxminddb-gen` can generate an
`UnmarshalMaxMindDB` method that avoids reflection. The generator is versioned

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is referring to the wrong method. Re-review everything in the README.md and CHANGELOG.md

@oschwald
oschwald force-pushed the greg/generator-improvement-plan-7 branch 2 times, most recently from 0c1b73c to aaf55f3 Compare August 7, 2026 03:02
Add the cursor API and source-file generator needed to decode application models without reflection. Preserve reflection behavior, deterministic output, and safe regeneration.
Reject uninitialized public container cursors, extend reflection fallback and cross-platform generator coverage, and clarify cursor contracts.
@oschwald
oschwald force-pushed the greg/generator-improvement-plan-7 branch from aaf55f3 to 57b8ec9 Compare August 8, 2026 18:52
@oschwald
oschwald merged commit 6d08cba into main Aug 8, 2026
20 checks passed
@oschwald
oschwald deleted the greg/generator-improvement-plan-7 branch August 8, 2026 20:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant