wreq 1.2.5 → 1.2.6
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/Cargo.lock +20 -13
- data/Cargo.toml +10 -2
- data/crates/wreq-util/src/emulate/macros.rs +12 -10
- data/crates/wreq-util/src/emulate/profile/chrome/header.rs +18 -3
- data/crates/wreq-util/src/emulate/profile/firefox/header.rs +16 -0
- data/crates/wreq-util/src/emulate/profile/opera/header.rs +6 -7
- data/crates/wreq-util/tests/client.rs +103 -1
- data/lib/wreq.rb +70 -45
- data/lib/wreq_ruby/body.rb +29 -11
- data/lib/wreq_ruby/client.rb +88 -55
- data/lib/wreq_ruby/cookie.rb +79 -19
- data/lib/wreq_ruby/emulate.rb +74 -6
- data/lib/wreq_ruby/error.rb +5 -7
- data/lib/wreq_ruby/http.rb +74 -0
- data/lib/wreq_ruby/response.rb +3 -0
- data/script/build_windows_gnu.ps1 +6 -0
- data/src/arch.rs +22 -0
- data/src/client/body/form.rs +2 -0
- data/src/client/body/json.rs +47 -14
- data/src/client/body/stream.rs +147 -43
- data/src/client/body.rs +11 -15
- data/src/client/param.rs +7 -7
- data/src/client/req.rs +87 -47
- data/src/client/resp.rs +23 -24
- data/src/client.rs +310 -230
- data/src/cookie.rs +280 -87
- data/src/emulate.rs +85 -50
- data/src/error.rs +198 -41
- data/src/extractor.rs +16 -76
- data/src/header.rs +146 -23
- data/src/http.rs +62 -33
- data/src/lib.rs +26 -20
- data/src/macros.rs +71 -46
- data/src/options.rs +284 -0
- data/src/rt.rs +22 -11
- data/src/serde/de/array_deserializer.rs +48 -0
- data/src/serde/de/array_enumerator.rs +59 -0
- data/src/serde/de/deserializer.rs +307 -0
- data/src/serde/de/enum_deserializer.rs +47 -0
- data/src/serde/de/hash_deserializer.rs +89 -0
- data/src/serde/de/number_deserializer.rs +51 -0
- data/src/serde/de/variant_deserializer.rs +101 -0
- data/src/serde/de.rs +33 -0
- data/src/serde/error.rs +146 -0
- data/src/serde/ser/enums.rs +14 -0
- data/src/serde/ser/map_serializer.rs +56 -0
- data/src/serde/ser/seq_serializer.rs +71 -0
- data/src/serde/ser/serializer.rs +194 -0
- data/src/serde/ser/struct_serializer.rs +106 -0
- data/src/serde/ser/struct_variant_serializer.rs +44 -0
- data/src/serde/ser/tuple_variant_serializer.rs +41 -0
- data/src/serde/ser.rs +18 -0
- data/src/serde/tests.rs +237 -0
- data/src/serde.rs +202 -0
- data/test/body_sender_test.rb +246 -0
- data/test/cookie_test.rb +211 -10
- data/test/json_precision_test.rb +209 -0
- data/test/option_validation_test.rb +311 -0
- data/test/value_semantics_test.rb +238 -0
- metadata +25 -2
- data/src/header/helper.rs +0 -112
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
use ::serde::forward_to_deserialize_any;
|
|
2
|
+
use magnus::{
|
|
3
|
+
Fixnum, Float, Integer, RArray, RBignum, RHash, RString, Ruby, Symbol, Value,
|
|
4
|
+
value::{Qfalse, Qtrue, ReprValue},
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
use super::super::{Error, MAX_JSON_NESTING};
|
|
8
|
+
use super::{
|
|
9
|
+
array_deserializer::ArrayDeserializer, enum_deserializer::EnumDeserializer,
|
|
10
|
+
hash_deserializer::HashDeserializer, number_deserializer::NumberDeserializer,
|
|
11
|
+
};
|
|
12
|
+
use crate::options::NATIVE_OPTION_TOKEN;
|
|
13
|
+
|
|
14
|
+
/// Implement typed Serde integer entry points with Magnus's checked conversions.
|
|
15
|
+
macro_rules! impl_deserialize_integers {
|
|
16
|
+
($($method:ident => ($visit:ident, $convert:ident)),+ $(,)?) => {
|
|
17
|
+
$(
|
|
18
|
+
fn $method<Visitor>(self, visitor: Visitor) -> Result<Visitor::Value, Self::Error>
|
|
19
|
+
where
|
|
20
|
+
Visitor: ::serde::de::Visitor<'de>,
|
|
21
|
+
{
|
|
22
|
+
match Integer::from_value(self.value) {
|
|
23
|
+
Some(value) => visitor.$visit(value.$convert()?),
|
|
24
|
+
None => self.deserialize_any(visitor),
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
)+
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Data model applied to a Ruby value during deserialization.
|
|
32
|
+
#[derive(Clone, Copy)]
|
|
33
|
+
pub(super) enum Mode {
|
|
34
|
+
/// Preserve the native Ruby-to-Serde conversion behavior.
|
|
35
|
+
Ruby,
|
|
36
|
+
/// Visit option names while skipping their values.
|
|
37
|
+
OptionKeys,
|
|
38
|
+
/// Enforce the JSON data model and preserve arbitrary-size numbers.
|
|
39
|
+
Json,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
impl Mode {
|
|
43
|
+
/// Return whether JSON-specific validation is enabled.
|
|
44
|
+
fn is_json(self) -> bool {
|
|
45
|
+
matches!(self, Self::Json)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// Serde deserializer over Ruby values.
|
|
50
|
+
pub(in crate::serde) struct Deserializer<'ruby> {
|
|
51
|
+
ruby: &'ruby Ruby,
|
|
52
|
+
value: Value,
|
|
53
|
+
depth: usize,
|
|
54
|
+
mode: Mode,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
impl<'ruby> Deserializer<'ruby> {
|
|
58
|
+
/// Create a deserializer with native `serde_magnus` Ruby behavior.
|
|
59
|
+
pub(in crate::serde) fn new_ruby(ruby: &'ruby Ruby, value: Value) -> Self {
|
|
60
|
+
Self::with_mode(ruby, value, 0, Mode::Ruby)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// Create a deserializer that validates option keys without reading values.
|
|
64
|
+
pub(in crate::serde) fn new_option_keys(ruby: &'ruby Ruby, value: Value) -> Self {
|
|
65
|
+
Self::with_mode(ruby, value, 0, Mode::OptionKeys)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/// Create a JSON deserializer with validation and arbitrary precision.
|
|
69
|
+
pub(super) fn new_json(ruby: &'ruby Ruby, value: Value) -> Self {
|
|
70
|
+
Self::with_mode(ruby, value, 0, Mode::Json)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// Create a nested deserializer that inherits its conversion mode.
|
|
74
|
+
pub(super) fn with_mode(ruby: &'ruby Ruby, value: Value, depth: usize, mode: Mode) -> Self {
|
|
75
|
+
Self {
|
|
76
|
+
ruby,
|
|
77
|
+
value,
|
|
78
|
+
depth,
|
|
79
|
+
mode,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// Validate and return the depth used by a nested container.
|
|
84
|
+
pub(super) fn nested_depth(&self) -> Result<usize, Error> {
|
|
85
|
+
let depth = self
|
|
86
|
+
.depth
|
|
87
|
+
.checked_add(1)
|
|
88
|
+
.ok_or_else(|| Error::message("JSON nesting depth overflow"))?;
|
|
89
|
+
if self.mode.is_json() && depth > MAX_JSON_NESTING {
|
|
90
|
+
Err(Error::message(format!(
|
|
91
|
+
"JSON nesting exceeds {MAX_JSON_NESTING} levels"
|
|
92
|
+
)))
|
|
93
|
+
} else {
|
|
94
|
+
Ok(depth)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
impl<'de> ::serde::Deserializer<'de> for Deserializer<'_> {
|
|
100
|
+
type Error = Error;
|
|
101
|
+
|
|
102
|
+
fn deserialize_any<Visitor>(self, visitor: Visitor) -> Result<Visitor::Value, Self::Error>
|
|
103
|
+
where
|
|
104
|
+
Visitor: ::serde::de::Visitor<'de>,
|
|
105
|
+
{
|
|
106
|
+
if self.value.is_nil() {
|
|
107
|
+
return visitor.visit_unit();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if let Some(value) = Qtrue::from_value(self.value) {
|
|
111
|
+
return visitor.visit_bool(value.to_bool());
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if let Some(value) = Qfalse::from_value(self.value) {
|
|
115
|
+
return visitor.visit_bool(value.to_bool());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if let Some(value) = Fixnum::from_value(self.value) {
|
|
119
|
+
return visitor.visit_i64(value.to_i64());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if let Some(value) = RBignum::from_value(self.value) {
|
|
123
|
+
if self.mode.is_json() {
|
|
124
|
+
let source: String = value.funcall_public("to_s", ())?;
|
|
125
|
+
return visitor.visit_map(NumberDeserializer::new(source));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return visitor.visit_i64(value.to_i64()?);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if let Some(value) = Float::from_value(self.value) {
|
|
132
|
+
let value = value.to_f64();
|
|
133
|
+
if self.mode.is_json() && !value.is_finite() {
|
|
134
|
+
return Err(Error::message("non-finite Float values are not valid JSON"));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return visitor.visit_f64(value);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if let Some(value) = RString::from_value(self.value) {
|
|
141
|
+
return visitor.visit_string(value.to_string()?);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if let Some(value) = Symbol::from_value(self.value) {
|
|
145
|
+
return visitor.visit_string(value.name()?.into_owned());
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if let Some(value) = RArray::from_value(self.value) {
|
|
149
|
+
let depth = self.nested_depth()?;
|
|
150
|
+
return visitor.visit_seq(ArrayDeserializer::new(self.ruby, value, depth, self.mode));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if let Some(value) = RHash::from_value(self.value) {
|
|
154
|
+
let depth = self.nested_depth()?;
|
|
155
|
+
return visitor.visit_map(HashDeserializer::new(self.ruby, value, depth, self.mode)?);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
Err(Error::type_error(format!(
|
|
159
|
+
"can't deserialize {}",
|
|
160
|
+
// SAFETY: conversion runs while the Ruby GVL is held.
|
|
161
|
+
unsafe { self.value.classname() }
|
|
162
|
+
)))
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
fn deserialize_bytes<Visitor>(self, _visitor: Visitor) -> Result<Visitor::Value, Self::Error>
|
|
166
|
+
where
|
|
167
|
+
Visitor: ::serde::de::Visitor<'de>,
|
|
168
|
+
{
|
|
169
|
+
Err(Error::type_error("can't deserialize into byte slice"))
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
fn deserialize_byte_buf<Visitor>(self, visitor: Visitor) -> Result<Visitor::Value, Self::Error>
|
|
173
|
+
where
|
|
174
|
+
Visitor: ::serde::de::Visitor<'de>,
|
|
175
|
+
{
|
|
176
|
+
if let Some(string) = RString::from_value(self.value) {
|
|
177
|
+
// SAFETY: the bytes are copied before any further Ruby API call.
|
|
178
|
+
visitor.visit_byte_buf(unsafe { string.as_slice() }.to_owned())
|
|
179
|
+
} else {
|
|
180
|
+
Err(Error::type_error(format!(
|
|
181
|
+
"no implicit conversion of {} to String",
|
|
182
|
+
// SAFETY: conversion runs while the Ruby GVL is held.
|
|
183
|
+
unsafe { self.value.classname() }
|
|
184
|
+
)))
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
fn deserialize_option<Visitor>(self, visitor: Visitor) -> Result<Visitor::Value, Self::Error>
|
|
189
|
+
where
|
|
190
|
+
Visitor: ::serde::de::Visitor<'de>,
|
|
191
|
+
{
|
|
192
|
+
if matches!(self.mode, Mode::OptionKeys) || self.value.is_nil() {
|
|
193
|
+
visitor.visit_none()
|
|
194
|
+
} else {
|
|
195
|
+
visitor.visit_some(self)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
fn deserialize_enum<Visitor>(
|
|
200
|
+
self,
|
|
201
|
+
_name: &'static str,
|
|
202
|
+
_variants: &'static [&'static str],
|
|
203
|
+
visitor: Visitor,
|
|
204
|
+
) -> Result<Visitor::Value, Self::Error>
|
|
205
|
+
where
|
|
206
|
+
Visitor: ::serde::de::Visitor<'de>,
|
|
207
|
+
{
|
|
208
|
+
if let Some(variant) = RString::from_value(self.value) {
|
|
209
|
+
return visitor.visit_enum(EnumDeserializer::new(
|
|
210
|
+
self.ruby,
|
|
211
|
+
variant.to_string()?,
|
|
212
|
+
self.ruby.qnil().as_value(),
|
|
213
|
+
self.depth,
|
|
214
|
+
self.mode,
|
|
215
|
+
));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if let Some(hash) = RHash::from_value(self.value) {
|
|
219
|
+
if hash.len() == 1 {
|
|
220
|
+
let keys: RArray = hash.funcall("keys", ())?;
|
|
221
|
+
let key: String = keys.entry(0)?;
|
|
222
|
+
let value = hash
|
|
223
|
+
.get(key.as_str())
|
|
224
|
+
.unwrap_or_else(|| self.ruby.qnil().as_value());
|
|
225
|
+
return visitor.visit_enum(EnumDeserializer::new(
|
|
226
|
+
self.ruby, key, value, self.depth, self.mode,
|
|
227
|
+
));
|
|
228
|
+
}
|
|
229
|
+
return Err(Error::type_error(format!(
|
|
230
|
+
"can't deserialize Hash of length {} to Enum",
|
|
231
|
+
hash.len()
|
|
232
|
+
)));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
Err(Error::type_error(format!(
|
|
236
|
+
"can't deserialize {} to Enum",
|
|
237
|
+
// SAFETY: conversion runs while the Ruby GVL is held.
|
|
238
|
+
unsafe { self.value.classname() }
|
|
239
|
+
)))
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
fn deserialize_newtype_struct<Visitor>(
|
|
243
|
+
self,
|
|
244
|
+
name: &'static str,
|
|
245
|
+
visitor: Visitor,
|
|
246
|
+
) -> Result<Visitor::Value, Self::Error>
|
|
247
|
+
where
|
|
248
|
+
Visitor: ::serde::de::Visitor<'de>,
|
|
249
|
+
{
|
|
250
|
+
if name == NATIVE_OPTION_TOKEN {
|
|
251
|
+
visitor.visit_unit()
|
|
252
|
+
} else {
|
|
253
|
+
visitor.visit_newtype_struct(self)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
fn deserialize_ignored_any<Visitor>(
|
|
258
|
+
self,
|
|
259
|
+
visitor: Visitor,
|
|
260
|
+
) -> Result<Visitor::Value, Self::Error>
|
|
261
|
+
where
|
|
262
|
+
Visitor: ::serde::de::Visitor<'de>,
|
|
263
|
+
{
|
|
264
|
+
visitor.visit_unit()
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
fn deserialize_identifier<Visitor>(
|
|
268
|
+
self,
|
|
269
|
+
visitor: Visitor,
|
|
270
|
+
) -> Result<Visitor::Value, Self::Error>
|
|
271
|
+
where
|
|
272
|
+
Visitor: ::serde::de::Visitor<'de>,
|
|
273
|
+
{
|
|
274
|
+
if matches!(self.mode, Mode::OptionKeys) {
|
|
275
|
+
if let Some(value) = RString::from_value(self.value) {
|
|
276
|
+
return visitor.visit_string(value.to_string()?);
|
|
277
|
+
}
|
|
278
|
+
if let Some(value) = Symbol::from_value(self.value) {
|
|
279
|
+
return match value.name()? {
|
|
280
|
+
std::borrow::Cow::Borrowed(name) => visitor.visit_borrowed_str(name),
|
|
281
|
+
std::borrow::Cow::Owned(name) => visitor.visit_string(name),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return Err(Error::type_error("option keys must be Symbols or Strings"));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
self.deserialize_any(visitor)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
impl_deserialize_integers! {
|
|
291
|
+
deserialize_i8 => (visit_i8, to_i8),
|
|
292
|
+
deserialize_i16 => (visit_i16, to_i16),
|
|
293
|
+
deserialize_i32 => (visit_i32, to_i32),
|
|
294
|
+
deserialize_i64 => (visit_i64, to_i64),
|
|
295
|
+
deserialize_i128 => (visit_i128, to_i128),
|
|
296
|
+
deserialize_u8 => (visit_u8, to_u8),
|
|
297
|
+
deserialize_u16 => (visit_u16, to_u16),
|
|
298
|
+
deserialize_u32 => (visit_u32, to_u32),
|
|
299
|
+
deserialize_u64 => (visit_u64, to_u64),
|
|
300
|
+
deserialize_u128 => (visit_u128, to_u128),
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
forward_to_deserialize_any! {
|
|
304
|
+
<Visitor: Visitor<'de>>
|
|
305
|
+
bool f32 f64 char str string unit unit_struct seq tuple tuple_struct map struct
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
use ::serde::de::{DeserializeSeed, EnumAccess, value::StringDeserializer};
|
|
2
|
+
use magnus::{Ruby, Value};
|
|
3
|
+
|
|
4
|
+
use super::{Mode, VariantDeserializer};
|
|
5
|
+
use crate::serde::Error;
|
|
6
|
+
|
|
7
|
+
/// Serde enum access over a Ruby string or one-entry hash.
|
|
8
|
+
pub(super) struct EnumDeserializer<'ruby> {
|
|
9
|
+
ruby: &'ruby Ruby,
|
|
10
|
+
variant: String,
|
|
11
|
+
value: Value,
|
|
12
|
+
depth: usize,
|
|
13
|
+
mode: Mode,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl<'ruby> EnumDeserializer<'ruby> {
|
|
17
|
+
/// Create enum access for a variant and its associated Ruby value.
|
|
18
|
+
pub(super) fn new(
|
|
19
|
+
ruby: &'ruby Ruby,
|
|
20
|
+
variant: String,
|
|
21
|
+
value: Value,
|
|
22
|
+
depth: usize,
|
|
23
|
+
mode: Mode,
|
|
24
|
+
) -> Self {
|
|
25
|
+
Self {
|
|
26
|
+
ruby,
|
|
27
|
+
variant,
|
|
28
|
+
value,
|
|
29
|
+
depth,
|
|
30
|
+
mode,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
impl<'ruby, 'de> EnumAccess<'de> for EnumDeserializer<'ruby> {
|
|
36
|
+
type Variant = VariantDeserializer<'ruby>;
|
|
37
|
+
type Error = Error;
|
|
38
|
+
|
|
39
|
+
fn variant_seed<Seed>(self, seed: Seed) -> Result<(Seed::Value, Self::Variant), Self::Error>
|
|
40
|
+
where
|
|
41
|
+
Seed: DeserializeSeed<'de>,
|
|
42
|
+
{
|
|
43
|
+
let variant = VariantDeserializer::new(self.ruby, self.value, self.depth, self.mode);
|
|
44
|
+
seed.deserialize(StringDeserializer::<Error>::new(self.variant))
|
|
45
|
+
.map(|value| (value, variant))
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
use std::iter::Peekable;
|
|
2
|
+
|
|
3
|
+
use ::serde::de::{DeserializeSeed, MapAccess};
|
|
4
|
+
use magnus::{RHash, RString, Ruby, Symbol, Value, r_hash::ForEach};
|
|
5
|
+
|
|
6
|
+
use super::{Deserializer, Mode, array_enumerator::ArrayEnumerator};
|
|
7
|
+
use crate::serde::Error;
|
|
8
|
+
|
|
9
|
+
/// Serde map access over a Ruby hash.
|
|
10
|
+
pub(super) struct HashDeserializer<'ruby> {
|
|
11
|
+
ruby: &'ruby Ruby,
|
|
12
|
+
hash: RHash,
|
|
13
|
+
keys: Peekable<ArrayEnumerator<'ruby>>,
|
|
14
|
+
depth: usize,
|
|
15
|
+
mode: Mode,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
impl<'ruby> HashDeserializer<'ruby> {
|
|
19
|
+
/// Create map access while preserving Ruby hash insertion order.
|
|
20
|
+
pub(super) fn new(
|
|
21
|
+
ruby: &'ruby Ruby,
|
|
22
|
+
hash: RHash,
|
|
23
|
+
depth: usize,
|
|
24
|
+
mode: Mode,
|
|
25
|
+
) -> Result<Self, Error> {
|
|
26
|
+
let keys = ruby.ary_new_capa(hash.len());
|
|
27
|
+
hash.foreach(|key: Value, _value: Value| {
|
|
28
|
+
keys.push(key)?;
|
|
29
|
+
Ok(ForEach::Continue)
|
|
30
|
+
})?;
|
|
31
|
+
Ok(Self {
|
|
32
|
+
ruby,
|
|
33
|
+
hash,
|
|
34
|
+
keys: ArrayEnumerator::new(ruby, keys).peekable(),
|
|
35
|
+
depth,
|
|
36
|
+
mode,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// Reject object keys that JSON cannot represent.
|
|
41
|
+
fn validate_key(key: Value) -> Result<(), Error> {
|
|
42
|
+
if RString::from_value(key).is_some() || Symbol::from_value(key).is_some() {
|
|
43
|
+
Ok(())
|
|
44
|
+
} else {
|
|
45
|
+
Err(Error::message(
|
|
46
|
+
"JSON object keys must be String or Symbol values",
|
|
47
|
+
))
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
impl<'de> MapAccess<'de> for HashDeserializer<'_> {
|
|
53
|
+
type Error = Error;
|
|
54
|
+
|
|
55
|
+
fn next_key_seed<Seed>(&mut self, seed: Seed) -> Result<Option<Seed::Value>, Self::Error>
|
|
56
|
+
where
|
|
57
|
+
Seed: DeserializeSeed<'de>,
|
|
58
|
+
{
|
|
59
|
+
match self.keys.peek() {
|
|
60
|
+
Some(Ok(key)) => {
|
|
61
|
+
if matches!(self.mode, Mode::Json) {
|
|
62
|
+
Self::validate_key(*key)?;
|
|
63
|
+
}
|
|
64
|
+
seed.deserialize(Deserializer::with_mode(
|
|
65
|
+
self.ruby, *key, self.depth, self.mode,
|
|
66
|
+
))
|
|
67
|
+
.map(Some)
|
|
68
|
+
}
|
|
69
|
+
Some(Err(error)) => Err(Error::message(format!("failed to read map key: {error}"))),
|
|
70
|
+
None => Ok(None),
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fn next_value_seed<Seed>(&mut self, seed: Seed) -> Result<Seed::Value, Self::Error>
|
|
75
|
+
where
|
|
76
|
+
Seed: DeserializeSeed<'de>,
|
|
77
|
+
{
|
|
78
|
+
match self.keys.next() {
|
|
79
|
+
Some(Ok(key)) => seed.deserialize(Deserializer::with_mode(
|
|
80
|
+
self.ruby,
|
|
81
|
+
self.hash.aref(key)?,
|
|
82
|
+
self.depth,
|
|
83
|
+
self.mode,
|
|
84
|
+
)),
|
|
85
|
+
Some(Err(error)) => Err(error),
|
|
86
|
+
None => Err(Error::message("map value has no matching key")),
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
use ::serde::de::{
|
|
2
|
+
DeserializeSeed, MapAccess,
|
|
3
|
+
value::{BorrowedStrDeserializer, StringDeserializer},
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
use super::super::{Error, JSON_NUMBER_TOKEN};
|
|
7
|
+
|
|
8
|
+
/// Serde map representation used by `serde_json` for arbitrary-precision numbers.
|
|
9
|
+
pub(super) struct NumberDeserializer {
|
|
10
|
+
source: Option<String>,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
impl NumberDeserializer {
|
|
14
|
+
/// Create a one-entry number map from a Ruby Integer decimal string.
|
|
15
|
+
pub(super) fn new(source: String) -> Self {
|
|
16
|
+
Self {
|
|
17
|
+
source: Some(source),
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
impl<'de> MapAccess<'de> for NumberDeserializer {
|
|
23
|
+
type Error = Error;
|
|
24
|
+
|
|
25
|
+
fn next_key_seed<Seed>(&mut self, seed: Seed) -> Result<Option<Seed::Value>, Self::Error>
|
|
26
|
+
where
|
|
27
|
+
Seed: DeserializeSeed<'de>,
|
|
28
|
+
{
|
|
29
|
+
if self.source.is_none() {
|
|
30
|
+
return Ok(None);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
seed.deserialize(BorrowedStrDeserializer::<Error>::new(JSON_NUMBER_TOKEN))
|
|
34
|
+
.map(Some)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fn next_value_seed<Seed>(&mut self, seed: Seed) -> Result<Seed::Value, Self::Error>
|
|
38
|
+
where
|
|
39
|
+
Seed: DeserializeSeed<'de>,
|
|
40
|
+
{
|
|
41
|
+
let source = self
|
|
42
|
+
.source
|
|
43
|
+
.take()
|
|
44
|
+
.ok_or_else(|| Error::message("JSON number value is missing"))?;
|
|
45
|
+
seed.deserialize(StringDeserializer::<Error>::new(source))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fn size_hint(&self) -> Option<usize> {
|
|
49
|
+
Some(usize::from(self.source.is_some()))
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
use ::serde::de::{DeserializeSeed, Unexpected, VariantAccess};
|
|
2
|
+
use magnus::{RArray, RHash, Ruby, Value, value::ReprValue};
|
|
3
|
+
|
|
4
|
+
use super::{ArrayDeserializer, Deserializer, HashDeserializer, Mode};
|
|
5
|
+
use crate::serde::Error;
|
|
6
|
+
|
|
7
|
+
/// Serde access to the payload of a Ruby enum representation.
|
|
8
|
+
pub(super) struct VariantDeserializer<'ruby> {
|
|
9
|
+
ruby: &'ruby Ruby,
|
|
10
|
+
value: Value,
|
|
11
|
+
depth: usize,
|
|
12
|
+
mode: Mode,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
impl<'ruby> VariantDeserializer<'ruby> {
|
|
16
|
+
/// Create variant access for a Ruby payload.
|
|
17
|
+
pub(super) fn new(ruby: &'ruby Ruby, value: Value, depth: usize, mode: Mode) -> Self {
|
|
18
|
+
Self {
|
|
19
|
+
ruby,
|
|
20
|
+
value,
|
|
21
|
+
depth,
|
|
22
|
+
mode,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Return the depth assigned to a container payload.
|
|
27
|
+
fn nested_depth(&self) -> Result<usize, Error> {
|
|
28
|
+
Deserializer::with_mode(self.ruby, self.value, self.depth, self.mode).nested_depth()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
impl<'de> VariantAccess<'de> for VariantDeserializer<'_> {
|
|
33
|
+
type Error = Error;
|
|
34
|
+
|
|
35
|
+
fn unit_variant(self) -> Result<(), Self::Error> {
|
|
36
|
+
if self.value.is_nil() {
|
|
37
|
+
Ok(())
|
|
38
|
+
} else {
|
|
39
|
+
Err(::serde::de::Error::invalid_type(
|
|
40
|
+
Unexpected::Other(
|
|
41
|
+
// SAFETY: conversion runs while the Ruby GVL is held.
|
|
42
|
+
&unsafe { self.value.classname() },
|
|
43
|
+
),
|
|
44
|
+
&"unit variant",
|
|
45
|
+
))
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fn newtype_variant_seed<Seed>(self, seed: Seed) -> Result<Seed::Value, Self::Error>
|
|
50
|
+
where
|
|
51
|
+
Seed: DeserializeSeed<'de>,
|
|
52
|
+
{
|
|
53
|
+
seed.deserialize(Deserializer::with_mode(
|
|
54
|
+
self.ruby, self.value, self.depth, self.mode,
|
|
55
|
+
))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fn tuple_variant<Visitor>(
|
|
59
|
+
self,
|
|
60
|
+
_len: usize,
|
|
61
|
+
visitor: Visitor,
|
|
62
|
+
) -> Result<Visitor::Value, Self::Error>
|
|
63
|
+
where
|
|
64
|
+
Visitor: ::serde::de::Visitor<'de>,
|
|
65
|
+
{
|
|
66
|
+
if let Some(array) = RArray::from_value(self.value) {
|
|
67
|
+
let depth = self.nested_depth()?;
|
|
68
|
+
visitor.visit_seq(ArrayDeserializer::new(self.ruby, array, depth, self.mode))
|
|
69
|
+
} else {
|
|
70
|
+
Err(::serde::de::Error::invalid_type(
|
|
71
|
+
Unexpected::Other(
|
|
72
|
+
// SAFETY: conversion runs while the Ruby GVL is held.
|
|
73
|
+
&unsafe { self.value.classname() },
|
|
74
|
+
),
|
|
75
|
+
&"tuple variant",
|
|
76
|
+
))
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn struct_variant<Visitor>(
|
|
81
|
+
self,
|
|
82
|
+
_fields: &'static [&'static str],
|
|
83
|
+
visitor: Visitor,
|
|
84
|
+
) -> Result<Visitor::Value, Self::Error>
|
|
85
|
+
where
|
|
86
|
+
Visitor: ::serde::de::Visitor<'de>,
|
|
87
|
+
{
|
|
88
|
+
if let Some(hash) = RHash::from_value(self.value) {
|
|
89
|
+
let depth = self.nested_depth()?;
|
|
90
|
+
visitor.visit_map(HashDeserializer::new(self.ruby, hash, depth, self.mode)?)
|
|
91
|
+
} else {
|
|
92
|
+
Err(::serde::de::Error::invalid_type(
|
|
93
|
+
Unexpected::Other(
|
|
94
|
+
// SAFETY: conversion runs while the Ruby GVL is held.
|
|
95
|
+
&unsafe { self.value.classname() },
|
|
96
|
+
),
|
|
97
|
+
&"struct variant",
|
|
98
|
+
))
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
data/src/serde/de.rs
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
mod array_deserializer;
|
|
2
|
+
mod array_enumerator;
|
|
3
|
+
mod deserializer;
|
|
4
|
+
mod enum_deserializer;
|
|
5
|
+
mod hash_deserializer;
|
|
6
|
+
mod number_deserializer;
|
|
7
|
+
mod variant_deserializer;
|
|
8
|
+
|
|
9
|
+
use ::serde::Deserialize;
|
|
10
|
+
use magnus::{Ruby, Value};
|
|
11
|
+
|
|
12
|
+
use super::Error;
|
|
13
|
+
use array_deserializer::ArrayDeserializer;
|
|
14
|
+
pub(super) use deserializer::Deserializer;
|
|
15
|
+
use deserializer::Mode;
|
|
16
|
+
use hash_deserializer::HashDeserializer;
|
|
17
|
+
use variant_deserializer::VariantDeserializer;
|
|
18
|
+
|
|
19
|
+
/// Deserialize one Ruby value using native Ruby data model semantics.
|
|
20
|
+
pub(super) fn deserialize_ruby<'de, Output>(ruby: &Ruby, value: Value) -> Result<Output, Error>
|
|
21
|
+
where
|
|
22
|
+
Output: Deserialize<'de>,
|
|
23
|
+
{
|
|
24
|
+
Output::deserialize(Deserializer::new_ruby(ruby, value))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Deserialize one Ruby value using JSON-specific conversion rules.
|
|
28
|
+
pub(super) fn deserialize_json<'de, Output>(ruby: &Ruby, value: Value) -> Result<Output, Error>
|
|
29
|
+
where
|
|
30
|
+
Output: Deserialize<'de>,
|
|
31
|
+
{
|
|
32
|
+
Output::deserialize(Deserializer::new_json(ruby, value))
|
|
33
|
+
}
|