maxmind-geoip2 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +13 -10
- data/lib/maxmind/geoip2/client.rb +18 -9
- data/lib/maxmind/geoip2/errors.rb +17 -8
- data/lib/maxmind/geoip2/model/city.rb +4 -5
- data/lib/maxmind/geoip2/model/connection_type.rb +3 -2
- data/lib/maxmind/geoip2/model/enterprise.rb +2 -3
- data/lib/maxmind/geoip2/model/insights.rb +3 -5
- data/lib/maxmind/geoip2/model/isp.rb +16 -0
- data/lib/maxmind/geoip2/reader.rb +1 -2
- data/lib/maxmind/geoip2/record/traits.rb +58 -26
- data/lib/maxmind/geoip2/version.rb +8 -0
- data/maxmind-geoip2.gemspec +8 -2
- data/test/data/MaxMind-DB-spec.md +15 -11
- data/test/data/cmd/write-test-data/main.go +68 -0
- data/test/data/go.mod +13 -0
- data/test/data/go.sum +16 -0
- data/test/data/perltidyrc +6 -0
- data/test/data/pkg/writer/decoder.go +178 -0
- data/test/data/pkg/writer/geoip2.go +182 -0
- data/test/data/pkg/writer/ip.go +39 -0
- data/test/data/pkg/writer/maxmind.go +245 -0
- data/test/data/pkg/writer/nestedstructures.go +73 -0
- data/test/data/pkg/writer/writer.go +58 -0
- data/test/data/source-data/GeoIP2-City-Test.json +402 -48
- data/test/data/source-data/GeoIP2-Connection-Type-Test.json +35 -10
- data/test/data/source-data/GeoIP2-Country-Test.json +145 -58
- data/test/data/source-data/GeoIP2-Domain-Test.json +5 -0
- data/test/data/source-data/GeoIP2-Enterprise-Test.json +408 -4
- data/test/data/source-data/GeoIP2-ISP-Test.json +10 -0
- data/test/data/source-data/GeoIP2-Precision-Enterprise-Sandbox-Test.json +296 -0
- data/test/data/source-data/GeoIP2-Precision-Enterprise-Test.json +473 -6
- data/test/data/source-data/GeoIP2-Static-IP-Score-Test.json +15 -0
- data/test/data/source-data/GeoIP2-User-Count-Test.json +18 -0
- data/test/data/source-data/GeoLite2-ASN-Test.json +4091 -8
- data/test/data/source-data/GeoLite2-City-Test.json +12972 -0
- data/test/data/source-data/GeoLite2-Country-Test.json +11372 -0
- data/test/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb +0 -0
- data/test/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb +0 -0
- data/test/data/test-data/GeoIP2-City-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Connection-Type-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Country-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-DensityIncome-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Domain-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Enterprise-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-ISP-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-User-Count-Test.mmdb +0 -0
- data/test/data/test-data/GeoLite2-ASN-Test.mmdb +0 -0
- data/test/data/test-data/GeoLite2-City-Test.mmdb +0 -0
- data/test/data/test-data/GeoLite2-Country-Test.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-string-value-entries.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-decoder.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv4-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv4-28.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv4-32.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv6-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv6-28.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv6-32.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-mixed-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-mixed-28.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-mixed-32.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-nested.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-pointer-decoder.mmdb +0 -0
- data/test/data/test-data/README.md +30 -14
- data/test/test_client.rb +18 -2
- data/test/test_reader.rb +37 -3
- metadata +21 -8
- data/test/data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
- data/test/data/source-data/README +0 -15
- data/test/data/test-data/write-test-data.pl +0 -691
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
package writer
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"net/netip"
|
|
6
|
+
|
|
7
|
+
"github.com/maxmind/mmdbwriter"
|
|
8
|
+
"github.com/maxmind/mmdbwriter/mmdbtype"
|
|
9
|
+
"go4.org/netipx"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// WriteIPv4TestDB writes mmdb files for an ip range between 1.1.1.1 and 1.1.1.32
|
|
13
|
+
// with various record sizes.
|
|
14
|
+
func (w *Writer) WriteIPv4TestDB() error {
|
|
15
|
+
ipRange, err := parseIPRange("1.1.1.1", "1.1.1.32")
|
|
16
|
+
if err != nil {
|
|
17
|
+
return fmt.Errorf("parsing ip range: %w", err)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
for _, recordSize := range []int{24, 28, 32} {
|
|
21
|
+
err := w.writeMaxMindTestDB(
|
|
22
|
+
recordSize,
|
|
23
|
+
[]netipx.IPRange{ipRange},
|
|
24
|
+
"ipv4",
|
|
25
|
+
)
|
|
26
|
+
if err != nil {
|
|
27
|
+
return fmt.Errorf("writing test database: %w", err)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return nil
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// WriteIPv6TestDB writes mmdb files for an ip range between ::1:ffff:ffff and ::2:0000:0059
|
|
35
|
+
// with various record sizes.
|
|
36
|
+
func (w *Writer) WriteIPv6TestDB() error {
|
|
37
|
+
ipRange, err := parseIPRange("::1:ffff:ffff", "::2:0000:0059")
|
|
38
|
+
if err != nil {
|
|
39
|
+
return fmt.Errorf("parsing ip range: %w", err)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for _, recordSize := range []int{24, 28, 32} {
|
|
43
|
+
err := w.writeMaxMindTestDB(
|
|
44
|
+
recordSize,
|
|
45
|
+
[]netipx.IPRange{ipRange},
|
|
46
|
+
"ipv6",
|
|
47
|
+
)
|
|
48
|
+
if err != nil {
|
|
49
|
+
return fmt.Errorf("writing test database: %w", err)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return nil
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// WriteMixedIPTestDB writes mmdb files for a mixed ip version range between ::1:ffff:ffff and ::2:0000:0059
|
|
57
|
+
// with various record sizes.
|
|
58
|
+
func (w *Writer) WriteMixedIPTestDB() error {
|
|
59
|
+
ipv6Range, err := parseIPRange("::1:ffff:ffff", "::2:0000:0059")
|
|
60
|
+
if err != nil {
|
|
61
|
+
return fmt.Errorf("parsing ip range: %w", err)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
ipv4Range, err := parseIPRange("1.1.1.1", "1.1.1.32")
|
|
65
|
+
if err != nil {
|
|
66
|
+
return fmt.Errorf("parsing ip range: %w", err)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for _, recordSize := range []int{24, 28, 32} {
|
|
70
|
+
err := w.writeMaxMindTestDB(
|
|
71
|
+
recordSize,
|
|
72
|
+
[]netipx.IPRange{ipv6Range, ipv4Range},
|
|
73
|
+
"mixed",
|
|
74
|
+
)
|
|
75
|
+
if err != nil {
|
|
76
|
+
return fmt.Errorf("writing test database: %w", err)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return nil
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// writeMaxMindTestDB writes test mmdb files.
|
|
84
|
+
func (w *Writer) writeMaxMindTestDB(
|
|
85
|
+
recordSize int,
|
|
86
|
+
ipRange []netipx.IPRange,
|
|
87
|
+
ipVersionName string,
|
|
88
|
+
) error {
|
|
89
|
+
ipVersion := 6
|
|
90
|
+
if ipRange[0].From().Is4() {
|
|
91
|
+
ipVersion = 4
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
metadata := map[string]string{}
|
|
95
|
+
metadata["en"] = "Test Database"
|
|
96
|
+
metadata["zh"] = "Test Database Chinese"
|
|
97
|
+
|
|
98
|
+
dbWriter, err := mmdbwriter.New(
|
|
99
|
+
mmdbwriter.Options{
|
|
100
|
+
DatabaseType: "Test",
|
|
101
|
+
Description: metadata,
|
|
102
|
+
DisableIPv4Aliasing: ipVersion == 4,
|
|
103
|
+
IPVersion: ipVersion,
|
|
104
|
+
Languages: []string{"en", "zh"},
|
|
105
|
+
RecordSize: recordSize,
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
if err != nil {
|
|
109
|
+
return fmt.Errorf("creating mmdbwriter: %w", err)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for _, ir := range ipRange {
|
|
113
|
+
for _, prefix := range ir.Prefixes() {
|
|
114
|
+
ipString := prefix.Addr().String()
|
|
115
|
+
if ipVersion == 6 && prefix.Addr().Is4() {
|
|
116
|
+
ipString = "::" + ipString
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
err := dbWriter.Insert(
|
|
120
|
+
netipx.PrefixIPNet(prefix),
|
|
121
|
+
mmdbtype.Map{
|
|
122
|
+
"ip": mmdbtype.String(ipString),
|
|
123
|
+
},
|
|
124
|
+
)
|
|
125
|
+
if err != nil {
|
|
126
|
+
return fmt.Errorf("inserting ip: %w", err)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fileName := fmt.Sprintf("MaxMind-DB-test-%s-%d.mmdb", ipVersionName, recordSize)
|
|
132
|
+
if err := w.write(dbWriter, fileName); err != nil {
|
|
133
|
+
return fmt.Errorf("writing database: %w", err)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return nil
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// WriteNoIPv4TestDB writes an mmdb file with no ipv4 records.
|
|
140
|
+
func (w *Writer) WriteNoIPv4TestDB() error {
|
|
141
|
+
dbWriter, err := mmdbwriter.New(
|
|
142
|
+
mmdbwriter.Options{
|
|
143
|
+
DatabaseType: "MaxMind DB No IPv4 Search Tree",
|
|
144
|
+
Description: map[string]string{
|
|
145
|
+
"en": "MaxMind DB No IPv4 Search Tree",
|
|
146
|
+
},
|
|
147
|
+
DisableIPv4Aliasing: true,
|
|
148
|
+
IncludeReservedNetworks: true,
|
|
149
|
+
IPVersion: 6,
|
|
150
|
+
Languages: []string{"en"},
|
|
151
|
+
RecordSize: 24,
|
|
152
|
+
},
|
|
153
|
+
)
|
|
154
|
+
if err != nil {
|
|
155
|
+
return fmt.Errorf("creating mmdbwriter: %w", err)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
addr, err := netip.ParsePrefix("::/64")
|
|
159
|
+
if err != nil {
|
|
160
|
+
return fmt.Errorf("parsing ip: %w", err)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
err = dbWriter.Insert(
|
|
164
|
+
netipx.PrefixIPNet(addr),
|
|
165
|
+
mmdbtype.String(addr.String()),
|
|
166
|
+
)
|
|
167
|
+
if err != nil {
|
|
168
|
+
return fmt.Errorf("inserting ip: %w", err)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if err := w.write(dbWriter, "MaxMind-DB-no-ipv4-search-tree.mmdb"); err != nil {
|
|
172
|
+
return fmt.Errorf("writing database: %w", err)
|
|
173
|
+
}
|
|
174
|
+
return nil
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// WriteNoMapTestDB writes an mmdb file where each record points to
|
|
178
|
+
// a string value.
|
|
179
|
+
func (w *Writer) WriteNoMapTestDB() error {
|
|
180
|
+
dbWriter, err := mmdbwriter.New(
|
|
181
|
+
mmdbwriter.Options{
|
|
182
|
+
DatabaseType: "MaxMind DB String Value Entries",
|
|
183
|
+
Description: map[string]string{
|
|
184
|
+
"en": "MaxMind DB String Value Entries (no maps or arrays as values)",
|
|
185
|
+
},
|
|
186
|
+
IPVersion: 4,
|
|
187
|
+
Languages: []string{"en"},
|
|
188
|
+
RecordSize: 24,
|
|
189
|
+
},
|
|
190
|
+
)
|
|
191
|
+
if err != nil {
|
|
192
|
+
return fmt.Errorf("creating mmdbwriter: %w", err)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
ipRange, err := parseIPRange("1.1.1.1", "1.1.1.32")
|
|
196
|
+
if err != nil {
|
|
197
|
+
return fmt.Errorf("parsing ip range: %w", err)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
for _, prefix := range ipRange.Prefixes() {
|
|
201
|
+
err := dbWriter.Insert(
|
|
202
|
+
netipx.PrefixIPNet(prefix),
|
|
203
|
+
mmdbtype.String(prefix.String()),
|
|
204
|
+
)
|
|
205
|
+
if err != nil {
|
|
206
|
+
return fmt.Errorf("inserting ip: %w", err)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if err := w.write(dbWriter, "MaxMind-DB-string-value-entries.mmdb"); err != nil {
|
|
211
|
+
return fmt.Errorf("writing database: %w", err)
|
|
212
|
+
}
|
|
213
|
+
return nil
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// WriteMetadataPointersTestDB writes an mmdb file with metadata pointers allowed.
|
|
217
|
+
func (w *Writer) WriteMetadataPointersTestDB() error {
|
|
218
|
+
repeatedString := "Lots of pointers in metadata"
|
|
219
|
+
dbWriter, err := mmdbwriter.New(
|
|
220
|
+
mmdbwriter.Options{
|
|
221
|
+
DatabaseType: repeatedString,
|
|
222
|
+
Description: map[string]string{
|
|
223
|
+
"en": repeatedString,
|
|
224
|
+
"es": repeatedString,
|
|
225
|
+
"zh": repeatedString,
|
|
226
|
+
},
|
|
227
|
+
DisableIPv4Aliasing: true,
|
|
228
|
+
IPVersion: 6,
|
|
229
|
+
Languages: []string{"en", "es", "zh"},
|
|
230
|
+
RecordSize: 24,
|
|
231
|
+
},
|
|
232
|
+
)
|
|
233
|
+
if err != nil {
|
|
234
|
+
return fmt.Errorf("creating mmdbwriter: %w", err)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if err := populateAllNetworks(dbWriter); err != nil {
|
|
238
|
+
return fmt.Errorf("inserting all networks: %w", err)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if err := w.write(dbWriter, "MaxMind-DB-test-metadata-pointers.mmdb"); err != nil {
|
|
242
|
+
return fmt.Errorf("writing database: %w", err)
|
|
243
|
+
}
|
|
244
|
+
return nil
|
|
245
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
package writer
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"net/netip"
|
|
6
|
+
|
|
7
|
+
"github.com/maxmind/mmdbwriter"
|
|
8
|
+
"github.com/maxmind/mmdbwriter/mmdbtype"
|
|
9
|
+
"go4.org/netipx"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// WriteDeeplyNestedStructuresTestDB writes an mmdb file with deeply nested record value types.
|
|
13
|
+
func (w *Writer) WriteDeeplyNestedStructuresTestDB() error {
|
|
14
|
+
dbWriter, err := mmdbwriter.New(
|
|
15
|
+
mmdbwriter.Options{
|
|
16
|
+
DatabaseType: "MaxMind DB Nested Data Structures",
|
|
17
|
+
Description: map[string]string{
|
|
18
|
+
"en": "MaxMind DB Nested Data Structures Test database - contains deeply nested map/array structures",
|
|
19
|
+
},
|
|
20
|
+
DisableIPv4Aliasing: false,
|
|
21
|
+
IncludeReservedNetworks: true,
|
|
22
|
+
IPVersion: 6,
|
|
23
|
+
Languages: []string{"en"},
|
|
24
|
+
RecordSize: 24,
|
|
25
|
+
},
|
|
26
|
+
)
|
|
27
|
+
if err != nil {
|
|
28
|
+
return fmt.Errorf("creating mmdbwriter: %w", err)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
addrs, err := parseIPSlice(ipSample)
|
|
32
|
+
if err != nil {
|
|
33
|
+
return fmt.Errorf("parsing ip addresses: %w", err)
|
|
34
|
+
}
|
|
35
|
+
if err := insertNestedStructure(dbWriter, addrs); err != nil {
|
|
36
|
+
return fmt.Errorf("inserting all types records: %w", err)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if err := w.write(dbWriter, "MaxMind-DB-test-nested.mmdb"); err != nil {
|
|
40
|
+
return fmt.Errorf("writing database: %w", err)
|
|
41
|
+
}
|
|
42
|
+
return nil
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// insertNestedStructure inserts records with deeply nested structures.
|
|
46
|
+
func insertNestedStructure(w *mmdbwriter.Tree, ipAddresses []netip.Prefix) error {
|
|
47
|
+
nestedStruct := mmdbtype.Map{
|
|
48
|
+
"map1": mmdbtype.Map{
|
|
49
|
+
"map2": mmdbtype.Map{
|
|
50
|
+
"array": mmdbtype.Slice{
|
|
51
|
+
mmdbtype.Map{
|
|
52
|
+
"map3": mmdbtype.Map{
|
|
53
|
+
"a": mmdbtype.Uint32(1),
|
|
54
|
+
"b": mmdbtype.Uint32(2),
|
|
55
|
+
"c": mmdbtype.Uint32(3),
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for _, addr := range ipAddresses {
|
|
64
|
+
err := w.Insert(
|
|
65
|
+
netipx.PrefixIPNet(addr),
|
|
66
|
+
nestedStruct,
|
|
67
|
+
)
|
|
68
|
+
if err != nil {
|
|
69
|
+
return fmt.Errorf("inserting ip: %w", err)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return nil
|
|
73
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Package writer defines database writers responsible
|
|
2
|
+
// for generating test mmdb files.
|
|
3
|
+
package writer
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"fmt"
|
|
7
|
+
"os"
|
|
8
|
+
"path/filepath"
|
|
9
|
+
|
|
10
|
+
"github.com/maxmind/mmdbwriter"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
var ipSample = []string{
|
|
14
|
+
"::1.1.1.0/120",
|
|
15
|
+
"::2.2.0.0/112",
|
|
16
|
+
"::3.0.0.0/104",
|
|
17
|
+
"::4.5.6.7/128",
|
|
18
|
+
"abcd::/64",
|
|
19
|
+
"1000::1234:0000/112",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Writer is responsible for writing test mmdb databases
|
|
23
|
+
// based on the provided data sources.
|
|
24
|
+
type Writer struct {
|
|
25
|
+
source string
|
|
26
|
+
target string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// New initializes a new test database writer struct.
|
|
30
|
+
func New(source, target string) (*Writer, error) {
|
|
31
|
+
s := filepath.Clean(source)
|
|
32
|
+
if _, err := os.Stat(s); os.IsNotExist(err) {
|
|
33
|
+
return nil, fmt.Errorf("source directory does not exist: %w", err)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
t := filepath.Clean(target)
|
|
37
|
+
if err := os.MkdirAll(t, os.ModePerm); err != nil {
|
|
38
|
+
return nil, fmt.Errorf("creating target directory: %w", err)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return &Writer{
|
|
42
|
+
source: s,
|
|
43
|
+
target: t,
|
|
44
|
+
}, nil
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func (w *Writer) write(dbWriter *mmdbwriter.Tree, fileName string) error {
|
|
48
|
+
outputFile, err := os.Create(filepath.Clean(filepath.Join(w.target, fileName)))
|
|
49
|
+
if err != nil {
|
|
50
|
+
return fmt.Errorf("creating mmdb file: %w", err)
|
|
51
|
+
}
|
|
52
|
+
defer outputFile.Close()
|
|
53
|
+
|
|
54
|
+
if _, err := dbWriter.WriteTo(outputFile); err != nil {
|
|
55
|
+
return fmt.Errorf("writing mmdb file: %w", err)
|
|
56
|
+
}
|
|
57
|
+
return nil
|
|
58
|
+
}
|