Skip to content

Commit eaaa929

Browse files
committed
Add geoip2 records
1 parent 0b0cf76 commit eaaa929

File tree

5 files changed

+598
-16
lines changed

5 files changed

+598
-16
lines changed

examples/lookup.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const std = @import("std");
22
const maxminddb = @import("maxminddb");
33

4-
const db_path = "test-data/test-data/GeoLite2-City-Test.mmdb";
4+
const db_path = "test-data/test-data/GeoIP2-City-Test.mmdb";
55
// We expect a DB file not larger than 1 GB.
66
const max_db_size: usize = 1024 * 1024 * 1024;
77

@@ -14,7 +14,7 @@ pub fn main() !void {
1414
defer db.close();
1515

1616
const ip = try std.net.Address.parseIp("89.160.20.128", 0);
17-
const city = try db.lookup(maxminddb.geolite2.City, &ip);
17+
const city = try db.lookup(maxminddb.geoip2.City, &ip);
1818
defer city.deinit();
1919

2020
var it = city.country.names.?.iterator();

src/decoder.zig

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,16 @@ pub const Decoder = struct {
169169
DecodedType = opt.child;
170170
switch (@typeInfo(DecodedType)) {
171171
.Struct => {},
172-
else => return DecodeError.UnsupportedFieldType,
172+
else => {
173+
std.debug.print("expected field {any} got optional {any}\n", .{ field, DecodedType });
174+
return DecodeError.UnsupportedFieldType;
175+
},
173176
}
174177
},
175-
else => return DecodeError.UnsupportedFieldType,
178+
else => {
179+
std.debug.print("expected field {any} got {any}\n", .{ field, DecodedType });
180+
return DecodeError.UnsupportedFieldType;
181+
},
176182
}
177183

178184
// Decode Map into std.hash_map.HashMap.

src/geoip2.zig

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
const std = @import("std");
2+
3+
pub const Names = std.hash_map.StringHashMap([]const u8);
4+
5+
/// Country represents a record in the GeoIP2-Country database, for example,
6+
/// https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoIP2-Country-Test.json.
7+
///
8+
/// It can be used for geolocation at the country-level for analytics, content customization,
9+
/// or compliance use cases in territories that are not disputed.
10+
pub const Country = struct {
11+
continent: Self.Continent,
12+
country: Self.Country,
13+
registered_country: Self.Country,
14+
represented_country: Self.RepresentedCountry,
15+
traits: Self.Traits,
16+
17+
_arena: std.heap.ArenaAllocator,
18+
19+
const Self = @This();
20+
pub const Continent = struct {
21+
code: []const u8 = "",
22+
geoname_id: u32 = 0,
23+
names: ?Names = null,
24+
};
25+
pub const Country = struct {
26+
geoname_id: u32 = 0,
27+
is_in_european_union: bool = false,
28+
iso_code: []const u8 = "",
29+
names: ?Names = null,
30+
};
31+
pub const RepresentedCountry = struct {
32+
geoname_id: u32 = 0,
33+
is_in_european_union: bool = false,
34+
iso_code: []const u8 = "",
35+
names: ?Names = null,
36+
type: []const u8 = "",
37+
};
38+
pub const Traits = struct {
39+
is_anycast: bool = false,
40+
};
41+
42+
pub fn init(allocator: std.mem.Allocator) Self {
43+
const arena = std.heap.ArenaAllocator.init(allocator);
44+
45+
return .{
46+
.continent = .{},
47+
.country = .{},
48+
.registered_country = .{},
49+
.represented_country = .{},
50+
.traits = .{},
51+
52+
._arena = arena,
53+
};
54+
}
55+
56+
pub fn deinit(self: *const Self) void {
57+
self._arena.deinit();
58+
}
59+
};
60+
61+
/// City represents a record in the GeoIP2-City database, for example,
62+
/// https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoIP2-City-Test.json.
63+
///
64+
/// It can be used for geolocation down to the city or postal code for analytics and content customization.
65+
pub const City = struct {
66+
city: Self.City,
67+
continent: Country.Continent,
68+
country: Country.Country,
69+
location: Self.Location,
70+
postal: Self.Postal,
71+
registered_country: Country.Country,
72+
represented_country: Country.RepresentedCountry,
73+
subdivisions: ?std.ArrayList(Self.Subdivision) = null,
74+
traits: Country.Traits,
75+
76+
_arena: std.heap.ArenaAllocator,
77+
78+
const Self = @This();
79+
pub const City = struct {
80+
geoname_id: u32 = 0,
81+
names: ?Names = null,
82+
};
83+
pub const Location = struct {
84+
accuracy_radius: u16 = 0,
85+
latitude: f64 = 0,
86+
longitude: f64 = 0,
87+
metro_code: u16 = 0,
88+
time_zone: []const u8 = "",
89+
};
90+
pub const Postal = struct {
91+
code: []const u8 = "",
92+
};
93+
pub const Subdivision = struct {
94+
geoname_id: u32 = 0,
95+
iso_code: []const u8 = "",
96+
names: ?Names = null,
97+
};
98+
99+
pub fn init(allocator: std.mem.Allocator) Self {
100+
const arena = std.heap.ArenaAllocator.init(allocator);
101+
102+
return .{
103+
.city = .{},
104+
.continent = .{},
105+
.country = .{},
106+
.location = .{},
107+
.postal = .{},
108+
.registered_country = .{},
109+
.represented_country = .{},
110+
.traits = .{},
111+
112+
._arena = arena,
113+
};
114+
}
115+
116+
pub fn deinit(self: *const Self) void {
117+
self._arena.deinit();
118+
}
119+
};
120+
121+
/// Enterprise represents a record in the GeoIP2-Enterprise database, for example,
122+
/// https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoIP2-Enterprise-Test.json.
123+
/// Determine geolocation data such as country, region, state, city, ZIP/postal code,
124+
/// and additional intelligence such as confidence factors, ISP, domain, and connection type.
125+
pub const Enterprise = struct {
126+
city: Self.City,
127+
continent: Self.Continent,
128+
country: Self.Country,
129+
location: Self.Location,
130+
postal: Self.Postal,
131+
registered_country: Self.Country,
132+
represented_country: Self.RepresentedCountry,
133+
subdivisions: ?std.ArrayList(Self.Subdivision) = null,
134+
traits: Self.Traits,
135+
136+
_arena: std.heap.ArenaAllocator,
137+
138+
const Self = @This();
139+
pub const City = struct {
140+
confidence: u16 = 0,
141+
geoname_id: u32 = 0,
142+
names: ?Names = null,
143+
};
144+
pub const Continent = struct {
145+
code: []const u8 = "",
146+
geoname_id: u32 = 0,
147+
names: ?Names = null,
148+
};
149+
pub const Country = struct {
150+
confidence: u16 = 0,
151+
geoname_id: u32 = 0,
152+
is_in_european_union: bool = false,
153+
iso_code: []const u8 = "",
154+
names: ?Names = null,
155+
};
156+
pub const Location = struct {
157+
accuracy_radius: u16 = 0,
158+
latitude: f64 = 0,
159+
longitude: f64 = 0,
160+
metro_code: u16 = 0,
161+
time_zone: []const u8 = "",
162+
};
163+
pub const Postal = struct {
164+
code: []const u8 = "",
165+
confidence: u16 = 0,
166+
};
167+
pub const RepresentedCountry = struct {
168+
geoname_id: u32 = 0,
169+
is_in_european_union: bool = false,
170+
iso_code: []const u8 = "",
171+
names: ?Names = null,
172+
type: []const u8 = "",
173+
};
174+
pub const Subdivision = struct {
175+
confidence: u16 = 0,
176+
geoname_id: u32 = 0,
177+
iso_code: []const u8 = "",
178+
names: ?Names = null,
179+
};
180+
pub const Traits = struct {
181+
autonomous_system_number: u32 = 0,
182+
autonomous_system_organization: []const u8 = "",
183+
connection_type: []const u8 = "",
184+
domain: []const u8 = "",
185+
is_anonymous: bool = false,
186+
is_anonymous_vpn: bool = false,
187+
is_anycast: bool = false,
188+
is_hosting_provider: bool = false,
189+
is_legitimate_proxy: bool = false,
190+
isp: []const u8 = "",
191+
is_public_proxy: bool = false,
192+
is_residential_proxy: bool = false,
193+
is_tor_exit_node: bool = false,
194+
mobile_country_code: []const u8 = "",
195+
mobile_network_code: []const u8 = "",
196+
organization: []const u8 = "",
197+
static_ip_score: f64 = 0,
198+
user_type: []const u8 = "",
199+
};
200+
201+
pub fn init(allocator: std.mem.Allocator) Self {
202+
const arena = std.heap.ArenaAllocator.init(allocator);
203+
204+
return .{
205+
.city = .{},
206+
.continent = .{},
207+
.country = .{},
208+
.location = .{},
209+
.postal = .{},
210+
.registered_country = .{},
211+
.represented_country = .{},
212+
.traits = .{},
213+
214+
._arena = arena,
215+
};
216+
}
217+
218+
pub fn deinit(self: *const Self) void {
219+
self._arena.deinit();
220+
}
221+
};
222+
223+
/// ISP represents a record in the GeoIP2-ISP database, for example,
224+
/// https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoIP2-ISP-Test.json.
225+
/// Determine the Internet Service Provider, organization name, and autonomous system organization
226+
/// and number associated with an IP address.
227+
pub const ISP = struct {
228+
autonomous_system_number: u32 = 0,
229+
autonomous_system_organization: []const u8 = "",
230+
isp: []const u8 = "",
231+
mobile_country_code: []const u8 = "",
232+
mobile_network_code: []const u8 = "",
233+
organization: []const u8 = "",
234+
};
235+
236+
/// ConnectionType represents a record in the GeoIP2-Connection-Type database, for example,
237+
/// https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoIP2-Connection-Type-Test.json.
238+
/// Determine the connection type of your visitors based on their IP address.
239+
/// The database identifies cellular, cable/DSL, and corporate connection speeds.
240+
pub const ConnectionType = struct {
241+
connection_type: []const u8 = "",
242+
};
243+
244+
/// AnonymousIP represents a record in the GeoIP2-Anonymous-IP database, for example,
245+
/// https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoIP2-Anonymous-IP-Test.json.
246+
/// It helps protect your business by identifying proxy, VPN, hosting, and other anonymous IP addresses.
247+
pub const AnonymousIP = struct {
248+
is_anonymous: bool = false,
249+
is_anonymous_vpn: bool = false,
250+
is_hosting_provider: bool = false,
251+
is_public_proxy: bool = false,
252+
is_residential_proxy: bool = false,
253+
is_tor_exit_node: bool = false,
254+
};
255+
256+
/// DensityIncome represents a record in the GeoIP2-DensityIncome database, for example,
257+
/// https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoIP2-DensityIncome-Test.json.
258+
pub const DensityIncome = struct {
259+
average_income: u32 = 0,
260+
population_density: u32 = 0,
261+
};
262+
263+
/// Domain represents a record in the GeoIP2-Domain database, for example,
264+
/// https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoIP2-Domain-Test.json.
265+
/// Look up the second level domain names associated with IPv4 and IPv6 addresses.
266+
pub const Domain = struct {
267+
domain: []const u8 = "",
268+
};

src/geolite2.zig

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const std = @import("std");
22

3-
pub const StringMap = std.hash_map.StringHashMap([]const u8);
3+
pub const Names = std.hash_map.StringHashMap([]const u8);
44

55
/// Country represents a record in the GeoLite2-Country database, for example,
66
/// https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoLite2-Country-Test.json.
@@ -18,19 +18,19 @@ pub const Country = struct {
1818
pub const Continent = struct {
1919
code: []const u8 = "",
2020
geoname_id: u32 = 0,
21-
names: ?StringMap = null,
21+
names: ?Names = null,
2222
};
2323
pub const Country = struct {
2424
geoname_id: u32 = 0,
2525
is_in_european_union: bool = false,
2626
iso_code: []const u8 = "",
27-
names: ?StringMap = null,
27+
names: ?Names = null,
2828
};
2929
pub const RepresentedCountry = struct {
3030
geoname_id: u32 = 0,
3131
is_in_european_union: bool = false,
3232
iso_code: []const u8 = "",
33-
names: ?StringMap = null,
33+
names: ?Names = null,
3434
type: []const u8 = "",
3535
};
3636

@@ -70,7 +70,7 @@ pub const City = struct {
7070
const Self = @This();
7171
pub const City = struct {
7272
geoname_id: u32 = 0,
73-
names: ?StringMap = null,
73+
names: ?Names = null,
7474
};
7575
pub const Location = struct {
7676
accuracy_radius: u16 = 0,
@@ -85,7 +85,7 @@ pub const City = struct {
8585
pub const Subdivision = struct {
8686
geoname_id: u32 = 0,
8787
iso_code: []const u8 = "",
88-
names: ?StringMap = null,
88+
names: ?Names = null,
8989
};
9090

9191
pub fn init(allocator: std.mem.Allocator) Self {

0 commit comments

Comments
 (0)