Skip to content

Commit 32fa95b

Browse files
authored
Merge pull request #132 from oschwald/greg/beta-2
Prepare for beta 2
2 parents 83fcdbf + 933f2ba commit 32fa95b

File tree

6 files changed

+751
-630
lines changed

6 files changed

+751
-630
lines changed

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
# 2.0.0-beta.2
2+
3+
* **BREAKING CHANGE**: Replaced `IsZero()` methods with `HasData()` methods on all
4+
result structs (including Names). The new methods provide clearer semantics:
5+
`HasData()` returns `true` when GeoIP data is found and `false` when no data is
6+
available. Unlike `IsZero()`, `HasData()` excludes Network and IPAddress fields
7+
from validation, allowing users to access network topology information even when
8+
no GeoIP data is found. The Network and IPAddress fields are now always
9+
populated for all lookups, regardless of whether GeoIP data is available.
10+
* **BREAKING CHANGE**: Replaced all anonymous nested structs with named types to
11+
improve struct initialization ergonomics. All result structs (Enterprise, City,
12+
Country) now use named types like `EnterpriseCityRecord`, `CityTraits`,
13+
`CountryRecord`, etc. This makes it much easier to initialize structs in user
14+
code while maintaining the same JSON serialization behavior.
15+
* **BREAKING CHANGE**: Changed `Location.Latitude` and `Location.Longitude` from
16+
`float64` to `*float64` to properly distinguish between missing coordinates and
17+
the valid location (0, 0). Missing coordinates are now represented as `nil`
18+
and are omitted from JSON output, while valid zero coordinates are preserved.
19+
This fixes the ambiguity where (0, 0) was incorrectly treated as "no data".
20+
Added `Location.HasCoordinates()` method for safe coordinate access. Reported
21+
by Nick Bruun. GitHub #5.
22+
123
# 2.0.0-beta.1 - 2025-06-22
224

325
* **BREAKING CHANGE**: Updated to use `maxminddb-golang/v2` which provides

README.md

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ This library is built using
1111
data for the database record is decoded using this library. Version 2.0
1212
provides significant performance improvements with 56% fewer allocations and
1313
34% less memory usage compared to v1. Version 2.0 also adds `Network` and
14-
`IPAddress` fields to all result structs, and includes an `IsZero()` method to
14+
`IPAddress` fields to all result structs, and includes a `HasData()` method to
1515
easily check if data was found. If you only need several fields, you may get
1616
superior performance by using maxminddb's `Lookup` directly with a result
1717
struct that only contains the required fields. (See
@@ -32,7 +32,7 @@ Version 2.0 includes several major improvements:
3232
- **Modern API**: Uses `netip.Addr` instead of `net.IP` for better performance
3333
- **Network Information**: All result structs now include `Network` and
3434
`IPAddress` fields
35-
- **Data Validation**: New `IsZero()` method to easily check if data was found
35+
- **Data Validation**: New `HasData()` method to easily check if data was found
3636
- **Structured Names**: Replaced `map[string]string` with typed `Names` struct
3737
for better performance
3838
- **Go 1.24 Support**: Uses `omitzero` JSON tags to match MaxMind database
@@ -71,7 +71,7 @@ func main() {
7171
if err != nil {
7272
log.Fatal(err)
7373
}
74-
if record.IsZero() {
74+
if !record.HasData() {
7575
fmt.Println("No data found for this IP")
7676
return
7777
}
@@ -82,7 +82,9 @@ func main() {
8282
fmt.Printf("Russian country name: %v\n", record.Country.Names.Russian)
8383
fmt.Printf("ISO country code: %v\n", record.Country.ISOCode)
8484
fmt.Printf("Time zone: %v\n", record.Location.TimeZone)
85-
fmt.Printf("Coordinates: %v, %v\n", record.Location.Latitude, record.Location.Longitude)
85+
if record.Location.HasCoordinates() {
86+
fmt.Printf("Coordinates: %v, %v\n", *record.Location.Latitude, *record.Location.Longitude)
87+
}
8688
// Output:
8789
// Portuguese (BR) city name: Londres
8890
// English subdivision name: England
@@ -151,7 +153,7 @@ func main() {
151153
log.Fatal(err)
152154
}
153155

154-
if record.IsZero() {
156+
if !record.HasData() {
155157
fmt.Println("No data found for this IP")
156158
return
157159
}
@@ -161,7 +163,9 @@ func main() {
161163
fmt.Printf("Country: %v (%v)\n", record.Country.Names.English, record.Country.ISOCode)
162164
fmt.Printf("Continent: %v (%v)\n", record.Continent.Names.English, record.Continent.Code)
163165
fmt.Printf("Postal Code: %v\n", record.Postal.Code)
164-
fmt.Printf("Location: %v, %v\n", record.Location.Latitude, record.Location.Longitude)
166+
if record.Location.HasCoordinates() {
167+
fmt.Printf("Location: %v, %v\n", *record.Location.Latitude, *record.Location.Longitude)
168+
}
165169
fmt.Printf("Time Zone: %v\n", record.Location.TimeZone)
166170
fmt.Printf("Network: %v\n", record.Traits.Network)
167171
fmt.Printf("IP Address: %v\n", record.Traits.IPAddress)
@@ -200,7 +204,7 @@ func main() {
200204
log.Fatal(err)
201205
}
202206

203-
if record.IsZero() {
207+
if !record.HasData() {
204208
fmt.Println("No data found for this IP")
205209
return
206210
}
@@ -251,7 +255,7 @@ func main() {
251255
log.Fatal(err)
252256
}
253257

254-
if record.IsZero() {
258+
if !record.HasData() {
255259
fmt.Println("No data found for this IP")
256260
return
257261
}
@@ -296,7 +300,7 @@ func main() {
296300
log.Fatal(err)
297301
}
298302

299-
if record.IsZero() {
303+
if !record.HasData() {
300304
fmt.Println("No data found for this IP")
301305
return
302306
}
@@ -345,15 +349,17 @@ func main() {
345349
log.Fatal(err)
346350
}
347351

348-
if record.IsZero() {
352+
if !record.HasData() {
349353
fmt.Println("No data found for this IP")
350354
return
351355
}
352356

353357
// Basic location information
354358
fmt.Printf("City: %v\n", record.City.Names.English)
355359
fmt.Printf("Country: %v (%v)\n", record.Country.Names.English, record.Country.ISOCode)
356-
fmt.Printf("Location: %v, %v\n", record.Location.Latitude, record.Location.Longitude)
360+
if record.Location.HasCoordinates() {
361+
fmt.Printf("Location: %v, %v\n", *record.Location.Latitude, *record.Location.Longitude)
362+
}
357363

358364
// Enterprise-specific fields
359365
fmt.Printf("ISP: %v\n", record.Traits.ISP)
@@ -410,7 +416,7 @@ func main() {
410416
log.Fatal(err)
411417
}
412418

413-
if record.IsZero() {
419+
if !record.HasData() {
414420
fmt.Println("No data found for this IP")
415421
return
416422
}
@@ -464,7 +470,7 @@ func main() {
464470
log.Fatal(err)
465471
}
466472

467-
if record.IsZero() {
473+
if !record.HasData() {
468474
fmt.Println("No data found for this IP")
469475
return
470476
}
@@ -507,7 +513,7 @@ func main() {
507513
log.Fatal(err)
508514
}
509515

510-
if record.IsZero() {
516+
if !record.HasData() {
511517
fmt.Println("No data found for this IP")
512518
return
513519
}
@@ -551,7 +557,7 @@ func main() {
551557
}
552558

553559
// Always check if data was found
554-
if record.IsZero() {
560+
if !record.HasData() {
555561
fmt.Println("No data found for this IP address")
556562
return
557563
}
@@ -627,7 +633,7 @@ fmt.Println(string(jsonData))
627633
- **IP Type**: Use `netip.Addr` instead of `net.IP`
628634
- **Field Names**: `IsoCode``ISOCode`
629635
- **Names Access**: Use struct fields instead of map access
630-
- **Data Validation**: Use `IsZero()` method to check for data availability
636+
- **Data Validation**: Use `HasData()` method to check for data availability
631637

632638
### Migration Example
633639

@@ -647,7 +653,7 @@ if err != nil {
647653
// handle error
648654
}
649655
record, err := db.City(ip)
650-
if record.IsZero() {
656+
if !record.HasData() {
651657
// handle no data found
652658
}
653659
cityName := record.City.Names.English
@@ -659,7 +665,7 @@ cityName := record.City.Names.English
659665

660666
**Database not found**: Ensure the .mmdb file path is correct and readable.
661667

662-
**No data returned**: Check if `IsZero()` returns true - the IP may not be in
668+
**No data returned**: Check if `HasData()` returns false - the IP may not be in
663669
the database or may be a private/reserved IP.
664670

665671
**Performance issues**: Ensure you're reusing the database instance rather than

example_test.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ func Example() {
2828
fmt.Printf("Russian country name: %v\n", record.Country.Names.Russian)
2929
fmt.Printf("ISO country code: %v\n", record.Country.ISOCode)
3030
fmt.Printf("Time zone: %v\n", record.Location.TimeZone)
31-
fmt.Printf("Coordinates: %v, %v\n", record.Location.Latitude, record.Location.Longitude)
31+
if record.Location.HasCoordinates() {
32+
fmt.Printf("Coordinates: %v, %v\n", *record.Location.Latitude, *record.Location.Longitude)
33+
} else {
34+
fmt.Println("Coordinates: unavailable")
35+
}
3236
// Output:
3337
// Portuguese (BR) city name: Londres
3438
// English subdivision name: England
@@ -55,7 +59,7 @@ func ExampleReader_City() {
5559
log.Panic(err)
5660
}
5761

58-
if record.IsZero() {
62+
if !record.HasData() {
5963
fmt.Println("No data found for this IP")
6064
return
6165
}
@@ -86,7 +90,7 @@ func ExampleReader_Country() {
8690
log.Panic(err)
8791
}
8892

89-
if record.IsZero() {
93+
if !record.HasData() {
9094
fmt.Println("No data found for this IP")
9195
return
9296
}
@@ -115,7 +119,7 @@ func ExampleReader_ASN() {
115119
log.Panic(err)
116120
}
117121

118-
if record.IsZero() {
122+
if !record.HasData() {
119123
fmt.Println("No data found for this IP")
120124
return
121125
}
@@ -144,7 +148,7 @@ func ExampleReader_AnonymousIP() {
144148
log.Panic(err)
145149
}
146150

147-
if record.IsZero() {
151+
if !record.HasData() {
148152
fmt.Println("No data found for this IP")
149153
return
150154
}
@@ -175,7 +179,7 @@ func ExampleReader_Enterprise() {
175179
log.Panic(err)
176180
}
177181

178-
if record.IsZero() {
182+
if !record.HasData() {
179183
fmt.Println("No data found for this IP")
180184
return
181185
}
@@ -208,7 +212,7 @@ func ExampleReader_ISP() {
208212
log.Panic(err)
209213
}
210214

211-
if record.IsZero() {
215+
if !record.HasData() {
212216
fmt.Println("No data found for this IP")
213217
return
214218
}
@@ -239,7 +243,7 @@ func ExampleReader_Domain() {
239243
log.Panic(err)
240244
}
241245

242-
if record.IsZero() {
246+
if !record.HasData() {
243247
fmt.Println("No data found for this IP")
244248
return
245249
}
@@ -266,7 +270,7 @@ func ExampleReader_ConnectionType() {
266270
log.Panic(err)
267271
}
268272

269-
if record.IsZero() {
273+
if !record.HasData() {
270274
fmt.Println("No data found for this IP")
271275
return
272276
}

0 commit comments

Comments
 (0)