maxmind-db-rust 0.1.4 → 0.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 +6 -1
- data/ext/maxmind_db_rust/Cargo.toml +1 -6
- data/ext/maxmind_db_rust/src/lib.rs +223 -41
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 662443bf974af98c823cd6eb43f5c898c7d7167329fc996dfd5df947bef78913
|
|
4
|
+
data.tar.gz: 0e69e3734ec1d65f26c2a0e6e9aaa8e6ca7d1560812351e81470681db2d89d69
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c417f7b5bc40b5af80e8976afecf0fa557d2e6b9879ab1c115163cefbe6f5f3db58e71a2af271a961d4fbdf7dd39eecb5eb184356ae3ea800f1de90ff45edc0f
|
|
7
|
+
data.tar.gz: ec18124df44c2991265cf043bd63e6bc512564b11baca819c9b6eedf21bbacf4ba24d0864a2202a3f23a14d4782ba32e3da30df9fc9f5e8b829a7a37134e5af4
|
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [0.2.0] - 2025-11-28
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Upgraded to the `0.27.0` release of the `maxminddb` crate.
|
|
13
|
+
- Expanded String interning.
|
|
9
14
|
|
|
10
15
|
## [0.1.4] - 2025-11-16
|
|
11
16
|
|
|
@@ -15,11 +15,6 @@ crate-type = ["cdylib"]
|
|
|
15
15
|
arc-swap = "1.7"
|
|
16
16
|
ipnetwork = "0.21"
|
|
17
17
|
magnus = "0.8"
|
|
18
|
-
maxminddb = { version = "0.
|
|
18
|
+
maxminddb = { version = "0.27", features = ["unsafe-str-decode"] }
|
|
19
19
|
memmap2 = "0.9"
|
|
20
20
|
serde = "1.0"
|
|
21
|
-
|
|
22
|
-
[profile.release]
|
|
23
|
-
lto = "thin"
|
|
24
|
-
codegen-units = 1
|
|
25
|
-
opt-level = 3
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
// Ruby validates UTF-8 when we construct `RString`s, so skipping the redundant check in
|
|
3
3
|
// the decoder is safe and avoids re-validating every string record twice.
|
|
4
4
|
use ::maxminddb as maxminddb_crate;
|
|
5
|
-
use arc_swap::ArcSwapOption;
|
|
5
|
+
use arc_swap::{ArcSwapOption, Guard};
|
|
6
6
|
use ipnetwork::IpNetwork;
|
|
7
7
|
use magnus::{
|
|
8
8
|
error::Error, prelude::*, scan_args::get_kwargs, scan_args::scan_args, value::Lazy,
|
|
9
9
|
ExceptionClass, IntoValue, RArray, RClass, RHash, RModule, RString, Symbol, Value,
|
|
10
10
|
};
|
|
11
|
-
use maxminddb_crate::{MaxMindDbError, Reader as MaxMindReader, Within
|
|
11
|
+
use maxminddb_crate::{MaxMindDbError, Reader as MaxMindReader, Within};
|
|
12
12
|
use memmap2::Mmap;
|
|
13
13
|
use serde::de::{self, Deserialize, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor};
|
|
14
14
|
use std::{
|
|
@@ -42,10 +42,100 @@ macro_rules! define_interned_keys {
|
|
|
42
42
|
)*
|
|
43
43
|
|
|
44
44
|
fn interned_key(ruby: &magnus::Ruby, key: &str) -> Option<Value> {
|
|
45
|
-
match key {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
match key.len() {
|
|
46
|
+
2 => match key.as_bytes() {
|
|
47
|
+
b"en" => Some(ruby.get_inner(&$crate::EN_KEY).as_value()),
|
|
48
|
+
b"es" => Some(ruby.get_inner(&$crate::ES_KEY).as_value()),
|
|
49
|
+
b"fr" => Some(ruby.get_inner(&$crate::FR_KEY).as_value()),
|
|
50
|
+
b"ja" => Some(ruby.get_inner(&$crate::JA_KEY).as_value()),
|
|
51
|
+
b"ru" => Some(ruby.get_inner(&$crate::RU_KEY).as_value()),
|
|
52
|
+
b"AF" => Some(ruby.get_inner(&$crate::AF_KEY).as_value()),
|
|
53
|
+
b"AN" => Some(ruby.get_inner(&$crate::AN_KEY).as_value()),
|
|
54
|
+
b"AS" => Some(ruby.get_inner(&$crate::AS_KEY).as_value()),
|
|
55
|
+
b"EU" => Some(ruby.get_inner(&$crate::EU_KEY).as_value()),
|
|
56
|
+
b"NA" => Some(ruby.get_inner(&$crate::NA_KEY).as_value()),
|
|
57
|
+
b"OC" => Some(ruby.get_inner(&$crate::OC_KEY).as_value()),
|
|
58
|
+
b"SA" => Some(ruby.get_inner(&$crate::SA_KEY).as_value()),
|
|
59
|
+
b"US" => Some(ruby.get_inner(&$crate::US_VAL).as_value()),
|
|
60
|
+
b"CN" => Some(ruby.get_inner(&$crate::CN_VAL).as_value()),
|
|
61
|
+
b"JP" => Some(ruby.get_inner(&$crate::JP_VAL).as_value()),
|
|
62
|
+
b"DE" => Some(ruby.get_inner(&$crate::DE_VAL).as_value()),
|
|
63
|
+
b"IN" => Some(ruby.get_inner(&$crate::IN_VAL).as_value()),
|
|
64
|
+
b"GB" => Some(ruby.get_inner(&$crate::GB_VAL).as_value()),
|
|
65
|
+
b"FR" => Some(ruby.get_inner(&$crate::FR_VAL).as_value()),
|
|
66
|
+
b"BR" => Some(ruby.get_inner(&$crate::BR_VAL).as_value()),
|
|
67
|
+
b"IT" => Some(ruby.get_inner(&$crate::IT_VAL).as_value()),
|
|
68
|
+
b"CA" => Some(ruby.get_inner(&$crate::CA_VAL).as_value()),
|
|
69
|
+
b"RU" => Some(ruby.get_inner(&$crate::RU_VAL).as_value()),
|
|
70
|
+
b"KR" => Some(ruby.get_inner(&$crate::KR_VAL).as_value()),
|
|
71
|
+
b"AU" => Some(ruby.get_inner(&$crate::AU_VAL).as_value()),
|
|
72
|
+
b"ES" => Some(ruby.get_inner(&$crate::ES_VAL).as_value()),
|
|
73
|
+
b"MX" => Some(ruby.get_inner(&$crate::MX_VAL).as_value()),
|
|
74
|
+
b"ID" => Some(ruby.get_inner(&$crate::ID_VAL).as_value()),
|
|
75
|
+
b"TR" => Some(ruby.get_inner(&$crate::TR_VAL).as_value()),
|
|
76
|
+
_ => None,
|
|
77
|
+
},
|
|
78
|
+
4 => match key.as_bytes() {
|
|
79
|
+
b"city" => Some(ruby.get_inner(&$crate::CITY_KEY).as_value()),
|
|
80
|
+
b"code" => Some(ruby.get_inner(&$crate::CODE_KEY).as_value()),
|
|
81
|
+
_ => None,
|
|
82
|
+
},
|
|
83
|
+
5 => match key.as_bytes() {
|
|
84
|
+
b"names" => Some(ruby.get_inner(&$crate::NAMES_KEY).as_value()),
|
|
85
|
+
b"pt-BR" => Some(ruby.get_inner(&$crate::PT_BR_KEY).as_value()),
|
|
86
|
+
b"zh-CN" => Some(ruby.get_inner(&$crate::ZH_CN_KEY).as_value()),
|
|
87
|
+
_ => None,
|
|
88
|
+
},
|
|
89
|
+
6 => match key.as_bytes() {
|
|
90
|
+
b"postal" => Some(ruby.get_inner(&$crate::POSTAL_KEY).as_value()),
|
|
91
|
+
b"traits" => Some(ruby.get_inner(&$crate::TRAITS_KEY).as_value()),
|
|
92
|
+
_ => None,
|
|
93
|
+
},
|
|
94
|
+
7 => match key.as_bytes() {
|
|
95
|
+
b"country" => Some(ruby.get_inner(&$crate::COUNTRY_KEY).as_value()),
|
|
96
|
+
b"network" => Some(ruby.get_inner(&$crate::NETWORK_KEY).as_value()),
|
|
97
|
+
_ => None,
|
|
98
|
+
},
|
|
99
|
+
8 => match key.as_bytes() {
|
|
100
|
+
b"location" => Some(ruby.get_inner(&$crate::LOCATION_KEY).as_value()),
|
|
101
|
+
b"iso_code" => Some(ruby.get_inner(&$crate::ISO_CODE_KEY).as_value()),
|
|
102
|
+
b"latitude" => Some(ruby.get_inner(&$crate::LATITUDE_KEY).as_value()),
|
|
103
|
+
_ => None,
|
|
104
|
+
},
|
|
105
|
+
9 => match key.as_bytes() {
|
|
106
|
+
b"continent" => Some(ruby.get_inner(&$crate::CONTINENT_KEY).as_value()),
|
|
107
|
+
b"longitude" => Some(ruby.get_inner(&$crate::LONGITUDE_KEY).as_value()),
|
|
108
|
+
b"time_zone" => Some(ruby.get_inner(&$crate::TIME_ZONE_KEY).as_value()),
|
|
109
|
+
_ => None,
|
|
110
|
+
},
|
|
111
|
+
10 => match key.as_bytes() {
|
|
112
|
+
b"geoname_id" => Some(ruby.get_inner(&$crate::GEONAME_ID_KEY).as_value()),
|
|
113
|
+
b"metro_code" => Some(ruby.get_inner(&$crate::METRO_CODE_KEY).as_value()),
|
|
114
|
+
b"confidence" => Some(ruby.get_inner(&$crate::CONFIDENCE_KEY).as_value()),
|
|
115
|
+
_ => None,
|
|
116
|
+
},
|
|
117
|
+
12 => match key.as_bytes() {
|
|
118
|
+
b"subdivisions" => Some(ruby.get_inner(&$crate::SUBDIVISIONS_KEY).as_value()),
|
|
119
|
+
_ => None,
|
|
120
|
+
},
|
|
121
|
+
15 => match key.as_bytes() {
|
|
122
|
+
b"accuracy_radius" => Some(ruby.get_inner(&$crate::ACCURACY_RADIUS_KEY).as_value()),
|
|
123
|
+
_ => None,
|
|
124
|
+
},
|
|
125
|
+
18 => match key.as_bytes() {
|
|
126
|
+
b"registered_country" => Some(ruby.get_inner(&$crate::REGISTERED_COUNTRY_KEY).as_value()),
|
|
127
|
+
b"population_density" => Some(ruby.get_inner(&$crate::POPULATION_DENSITY_KEY).as_value()),
|
|
128
|
+
_ => None,
|
|
129
|
+
},
|
|
130
|
+
19 => match key.as_bytes() {
|
|
131
|
+
b"represented_country" => Some(ruby.get_inner(&$crate::REPRESENTED_COUNTRY_KEY).as_value()),
|
|
132
|
+
b"is_anonymous_proxy" => Some(ruby.get_inner(&$crate::IS_ANONYMOUS_PROXY_KEY).as_value()),
|
|
133
|
+
_ => None,
|
|
134
|
+
},
|
|
135
|
+
21 => match key.as_bytes() {
|
|
136
|
+
b"is_satellite_provider" => Some(ruby.get_inner(&$crate::IS_SATELLITE_PROVIDER_KEY).as_value()),
|
|
137
|
+
_ => None,
|
|
138
|
+
},
|
|
49
139
|
_ => None,
|
|
50
140
|
}
|
|
51
141
|
}
|
|
@@ -79,6 +169,37 @@ define_interned_keys!(
|
|
|
79
169
|
PT_BR_KEY => "pt-BR",
|
|
80
170
|
RU_KEY => "ru",
|
|
81
171
|
ZH_CN_KEY => "zh-CN",
|
|
172
|
+
// Common keys
|
|
173
|
+
CODE_KEY => "code",
|
|
174
|
+
NETWORK_KEY => "network",
|
|
175
|
+
IS_ANONYMOUS_PROXY_KEY => "is_anonymous_proxy",
|
|
176
|
+
IS_SATELLITE_PROVIDER_KEY => "is_satellite_provider",
|
|
177
|
+
// Continent codes
|
|
178
|
+
AF_KEY => "AF",
|
|
179
|
+
AN_KEY => "AN",
|
|
180
|
+
AS_KEY => "AS",
|
|
181
|
+
EU_KEY => "EU",
|
|
182
|
+
NA_KEY => "NA",
|
|
183
|
+
OC_KEY => "OC",
|
|
184
|
+
SA_KEY => "SA",
|
|
185
|
+
// Major Country ISO codes
|
|
186
|
+
US_VAL => "US",
|
|
187
|
+
CN_VAL => "CN",
|
|
188
|
+
JP_VAL => "JP",
|
|
189
|
+
DE_VAL => "DE",
|
|
190
|
+
IN_VAL => "IN",
|
|
191
|
+
GB_VAL => "GB",
|
|
192
|
+
FR_VAL => "FR",
|
|
193
|
+
BR_VAL => "BR",
|
|
194
|
+
IT_VAL => "IT",
|
|
195
|
+
CA_VAL => "CA",
|
|
196
|
+
RU_VAL => "RU", // Already defined above as RU_KEY? No, RU_KEY is "ru" (lang), this is "RU" (country)
|
|
197
|
+
KR_VAL => "KR",
|
|
198
|
+
AU_VAL => "AU",
|
|
199
|
+
ES_VAL => "ES", // "ES" (country) vs "es" (lang). ES_KEY is "es".
|
|
200
|
+
MX_VAL => "MX",
|
|
201
|
+
ID_VAL => "ID",
|
|
202
|
+
TR_VAL => "TR",
|
|
82
203
|
);
|
|
83
204
|
|
|
84
205
|
/// Wrapper that owns the Ruby value produced by deserializing a MaxMind record
|
|
@@ -289,8 +410,8 @@ impl ReaderSource {
|
|
|
289
410
|
ip: IpAddr,
|
|
290
411
|
) -> Result<Option<RubyDecodedValue>, maxminddb_crate::MaxMindDbError> {
|
|
291
412
|
match self {
|
|
292
|
-
ReaderSource::Mmap(reader) => reader.lookup(ip),
|
|
293
|
-
ReaderSource::Memory(reader) => reader.lookup(ip),
|
|
413
|
+
ReaderSource::Mmap(reader) => reader.lookup(ip)?.decode(),
|
|
414
|
+
ReaderSource::Memory(reader) => reader.lookup(ip)?.decode(),
|
|
294
415
|
}
|
|
295
416
|
}
|
|
296
417
|
|
|
@@ -299,10 +420,35 @@ impl ReaderSource {
|
|
|
299
420
|
&self,
|
|
300
421
|
ip: IpAddr,
|
|
301
422
|
) -> Result<(Option<RubyDecodedValue>, usize), maxminddb_crate::MaxMindDbError> {
|
|
302
|
-
match self {
|
|
303
|
-
ReaderSource::Mmap(reader) =>
|
|
304
|
-
|
|
305
|
-
|
|
423
|
+
let (result, prefix_len) = match self {
|
|
424
|
+
ReaderSource::Mmap(reader) => {
|
|
425
|
+
let result = reader.lookup(ip)?;
|
|
426
|
+
let network = result.network()?;
|
|
427
|
+
let prefix = network.prefix();
|
|
428
|
+
|
|
429
|
+
let prefix_len = if ip.is_ipv4() && network.is_ipv6() {
|
|
430
|
+
0
|
|
431
|
+
} else {
|
|
432
|
+
prefix as usize
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
(result.decode()?, prefix_len)
|
|
436
|
+
}
|
|
437
|
+
ReaderSource::Memory(reader) => {
|
|
438
|
+
let result = reader.lookup(ip)?;
|
|
439
|
+
let network = result.network()?;
|
|
440
|
+
let prefix = network.prefix();
|
|
441
|
+
|
|
442
|
+
let prefix_len = if ip.is_ipv4() && network.is_ipv6() {
|
|
443
|
+
0
|
|
444
|
+
} else {
|
|
445
|
+
prefix as usize
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
(result.decode()?, prefix_len)
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
Ok((result, prefix_len))
|
|
306
452
|
}
|
|
307
453
|
|
|
308
454
|
#[inline]
|
|
@@ -317,24 +463,18 @@ impl ReaderSource {
|
|
|
317
463
|
fn within(&self, network: IpNetwork) -> Result<ReaderWithin, MaxMindDbError> {
|
|
318
464
|
match self {
|
|
319
465
|
ReaderSource::Mmap(reader) => {
|
|
320
|
-
let iter = reader.within
|
|
466
|
+
let iter = reader.within(network, Default::default())?;
|
|
321
467
|
// SAFETY: the iterator holds a reference into `reader`. We'll store an Arc guard
|
|
322
468
|
// alongside it so the reader outlives the transmuted iterator.
|
|
323
469
|
Ok(ReaderWithin::Mmap(unsafe {
|
|
324
|
-
std::mem::transmute::<
|
|
325
|
-
Within<'_, RubyDecodedValue, Mmap>,
|
|
326
|
-
Within<'static, RubyDecodedValue, Mmap>,
|
|
327
|
-
>(iter)
|
|
470
|
+
std::mem::transmute::<Within<'_, Mmap>, Within<'static, Mmap>>(iter)
|
|
328
471
|
}))
|
|
329
472
|
}
|
|
330
473
|
ReaderSource::Memory(reader) => {
|
|
331
|
-
let iter = reader.within
|
|
474
|
+
let iter = reader.within(network, Default::default())?;
|
|
332
475
|
// SAFETY: same as above, the Arc guard keeps the reader alive.
|
|
333
476
|
Ok(ReaderWithin::Memory(unsafe {
|
|
334
|
-
std::mem::transmute::<
|
|
335
|
-
Within<'_, RubyDecodedValue, Vec<u8>>,
|
|
336
|
-
Within<'static, RubyDecodedValue, Vec<u8>>,
|
|
337
|
-
>(iter)
|
|
477
|
+
std::mem::transmute::<Within<'_, Vec<u8>>, Within<'static, Vec<u8>>>(iter)
|
|
338
478
|
}))
|
|
339
479
|
}
|
|
340
480
|
}
|
|
@@ -343,15 +483,47 @@ impl ReaderSource {
|
|
|
343
483
|
|
|
344
484
|
/// Wrapper enum for Within iterators
|
|
345
485
|
enum ReaderWithin {
|
|
346
|
-
Mmap(Within<'static,
|
|
347
|
-
Memory(Within<'static,
|
|
486
|
+
Mmap(Within<'static, Mmap>),
|
|
487
|
+
Memory(Within<'static, Vec<u8>>),
|
|
348
488
|
}
|
|
349
489
|
|
|
350
490
|
impl ReaderWithin {
|
|
351
|
-
fn next(&mut self) -> Option<Result<
|
|
491
|
+
fn next(&mut self) -> Option<Result<(IpNetwork, RubyDecodedValue), MaxMindDbError>> {
|
|
352
492
|
match self {
|
|
353
|
-
ReaderWithin::Mmap(iter) =>
|
|
354
|
-
|
|
493
|
+
ReaderWithin::Mmap(iter) => loop {
|
|
494
|
+
match iter.next() {
|
|
495
|
+
None => return None,
|
|
496
|
+
Some(Err(e)) => return Some(Err(e)),
|
|
497
|
+
Some(Ok(lookup_result)) => {
|
|
498
|
+
let network = match lookup_result.network() {
|
|
499
|
+
Ok(n) => n,
|
|
500
|
+
Err(e) => return Some(Err(e)),
|
|
501
|
+
};
|
|
502
|
+
match lookup_result.decode::<RubyDecodedValue>() {
|
|
503
|
+
Ok(Some(data)) => return Some(Ok((network, data))),
|
|
504
|
+
Ok(None) => continue, // Skip networks without data
|
|
505
|
+
Err(e) => return Some(Err(e)),
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
ReaderWithin::Memory(iter) => loop {
|
|
511
|
+
match iter.next() {
|
|
512
|
+
None => return None,
|
|
513
|
+
Some(Err(e)) => return Some(Err(e)),
|
|
514
|
+
Some(Ok(lookup_result)) => {
|
|
515
|
+
let network = match lookup_result.network() {
|
|
516
|
+
Ok(n) => n,
|
|
517
|
+
Err(e) => return Some(Err(e)),
|
|
518
|
+
};
|
|
519
|
+
match lookup_result.decode::<RubyDecodedValue>() {
|
|
520
|
+
Ok(Some(data)) => return Some(Ok((network, data))),
|
|
521
|
+
Ok(None) => continue, // Skip networks without data
|
|
522
|
+
Err(e) => return Some(Err(e)),
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
},
|
|
355
527
|
}
|
|
356
528
|
}
|
|
357
529
|
}
|
|
@@ -482,7 +654,9 @@ impl Reader {
|
|
|
482
654
|
fn get(&self, ip_address: Value) -> Result<Value, Error> {
|
|
483
655
|
let ruby = magnus::Ruby::get().expect("Ruby VM should be available in Ruby method");
|
|
484
656
|
|
|
485
|
-
let
|
|
657
|
+
let guard = self.get_reader(&ruby)?;
|
|
658
|
+
let reader_option = guard.as_ref();
|
|
659
|
+
let reader = reader_option.as_ref().unwrap();
|
|
486
660
|
|
|
487
661
|
// Parse IP address
|
|
488
662
|
let parsed_ip = parse_ip_address_fast(ip_address, &ruby)?;
|
|
@@ -498,7 +672,7 @@ impl Reader {
|
|
|
498
672
|
match reader.lookup(parsed_ip) {
|
|
499
673
|
Ok(Some(data)) => Ok(data.into_value()),
|
|
500
674
|
Ok(None) => Ok(ruby.qnil().as_value()),
|
|
501
|
-
Err(MaxMindDbError::InvalidDatabase
|
|
675
|
+
Err(MaxMindDbError::InvalidDatabase { .. }) | Err(MaxMindDbError::Io(_)) => {
|
|
502
676
|
Err(Error::new(
|
|
503
677
|
ExceptionClass::from_value(invalid_database_error().as_value())
|
|
504
678
|
.expect("InvalidDatabaseError should convert to ExceptionClass"),
|
|
@@ -516,7 +690,9 @@ impl Reader {
|
|
|
516
690
|
fn get_with_prefix_length(&self, ip_address: Value) -> Result<RArray, Error> {
|
|
517
691
|
let ruby = magnus::Ruby::get().expect("Ruby VM should be available in Ruby method");
|
|
518
692
|
|
|
519
|
-
let
|
|
693
|
+
let guard = self.get_reader(&ruby)?;
|
|
694
|
+
let reader_option = guard.as_ref();
|
|
695
|
+
let reader = reader_option.as_ref().unwrap();
|
|
520
696
|
|
|
521
697
|
// Parse IP address
|
|
522
698
|
let parsed_ip = parse_ip_address_fast(ip_address, &ruby)?;
|
|
@@ -542,7 +718,7 @@ impl Reader {
|
|
|
542
718
|
arr.push(prefix.into_value_with(&ruby))?;
|
|
543
719
|
Ok(arr)
|
|
544
720
|
}
|
|
545
|
-
Err(MaxMindDbError::InvalidDatabase
|
|
721
|
+
Err(MaxMindDbError::InvalidDatabase { .. }) | Err(MaxMindDbError::Io(_)) => {
|
|
546
722
|
Err(Error::new(
|
|
547
723
|
ExceptionClass::from_value(invalid_database_error().as_value())
|
|
548
724
|
.expect("InvalidDatabaseError should convert to ExceptionClass"),
|
|
@@ -559,7 +735,9 @@ impl Reader {
|
|
|
559
735
|
fn metadata(&self) -> Result<Metadata, Error> {
|
|
560
736
|
let ruby = magnus::Ruby::get().expect("Ruby VM should be available in Ruby method");
|
|
561
737
|
|
|
562
|
-
let
|
|
738
|
+
let guard = self.get_reader(&ruby)?;
|
|
739
|
+
let reader_option = guard.as_ref();
|
|
740
|
+
let reader = reader_option.as_ref().unwrap();
|
|
563
741
|
let meta = reader.metadata();
|
|
564
742
|
|
|
565
743
|
Ok(Metadata {
|
|
@@ -589,7 +767,9 @@ impl Reader {
|
|
|
589
767
|
fn each(&self, args: &[Value]) -> Result<Value, Error> {
|
|
590
768
|
let ruby = magnus::Ruby::get().expect("Ruby VM should be available in Ruby method");
|
|
591
769
|
|
|
592
|
-
let
|
|
770
|
+
let guard = self.get_reader(&ruby)?;
|
|
771
|
+
let reader_option = guard.as_ref();
|
|
772
|
+
let reader = reader_option.as_ref().unwrap();
|
|
593
773
|
|
|
594
774
|
// If no block given, return enumerator
|
|
595
775
|
if !ruby.block_given() {
|
|
@@ -681,16 +861,16 @@ impl Reader {
|
|
|
681
861
|
// Iterate over all networks
|
|
682
862
|
while let Some(result) = iter.next() {
|
|
683
863
|
match result {
|
|
684
|
-
Ok(
|
|
864
|
+
Ok((network, data)) => {
|
|
685
865
|
// Convert IpNetwork to IPAddr
|
|
686
|
-
let ip_str =
|
|
866
|
+
let ip_str = network.to_string();
|
|
687
867
|
let ipaddr = ipaddr_class.funcall::<_, _, Value>("new", (ip_str,))?;
|
|
688
868
|
|
|
689
869
|
// Yield [network, data] to block
|
|
690
|
-
let values = (ipaddr,
|
|
870
|
+
let values = (ipaddr, data.into_value());
|
|
691
871
|
ruby.yield_values::<(Value, Value), Value>(values)?;
|
|
692
872
|
}
|
|
693
|
-
Err(MaxMindDbError::InvalidDatabase
|
|
873
|
+
Err(MaxMindDbError::InvalidDatabase { .. }) | Err(MaxMindDbError::Io(_)) => {
|
|
694
874
|
return Err(Error::new(
|
|
695
875
|
ExceptionClass::from_value(invalid_database_error().as_value())
|
|
696
876
|
.expect("InvalidDatabaseError should convert to ExceptionClass"),
|
|
@@ -710,10 +890,12 @@ impl Reader {
|
|
|
710
890
|
}
|
|
711
891
|
|
|
712
892
|
/// Helper method to get the reader from the ArcSwapOption
|
|
713
|
-
fn get_reader(&self, ruby: &magnus::Ruby) -> Result<Arc<ReaderSource
|
|
714
|
-
self.reader
|
|
715
|
-
|
|
716
|
-
|
|
893
|
+
fn get_reader(&self, ruby: &magnus::Ruby) -> Result<Guard<Option<Arc<ReaderSource>>>, Error> {
|
|
894
|
+
let guard = self.reader.load();
|
|
895
|
+
if guard.is_none() {
|
|
896
|
+
return Err(Error::new(ruby.exception_runtime_error(), ERR_CLOSED_DB));
|
|
897
|
+
}
|
|
898
|
+
Ok(guard)
|
|
717
899
|
}
|
|
718
900
|
}
|
|
719
901
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: maxmind-db-rust
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gregory Oschwald
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-11-
|
|
11
|
+
date: 2025-11-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rb_sys
|