wreq 1.2.5 → 1.2.7
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/config.toml +4 -0
- data/Cargo.lock +135 -119
- data/Cargo.toml +11 -3
- data/Rakefile +6 -0
- data/crates/wreq-util/README.md +1 -1
- data/crates/wreq-util/examples/emulate.rs +3 -3
- data/crates/wreq-util/src/emulate/macros.rs +13 -11
- 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/docs/windows-gnu-tokio-crash.md +49 -3
- data/extconf.rb +4 -0
- 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/rust_env.rb +85 -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
- data/wreq.gemspec +1 -1
- metadata +28 -4
- data/script/build_windows_gnu.ps1 +0 -264
- data/src/header/helper.rs +0 -112
data/lib/wreq_ruby/emulate.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Profile and platform constants mirror the native enum variant names.
|
|
4
|
+
# standard:disable Naming/ConstantName
|
|
5
|
+
|
|
3
6
|
module Wreq
|
|
4
7
|
# Browser and client fingerprint profile enumeration backed by Rust.
|
|
5
8
|
#
|
|
@@ -164,6 +167,29 @@ module Wreq
|
|
|
164
167
|
def to_s
|
|
165
168
|
end
|
|
166
169
|
end
|
|
170
|
+
|
|
171
|
+
unless method_defined?(:==)
|
|
172
|
+
# Value-based equality.
|
|
173
|
+
# @param other [Object]
|
|
174
|
+
# @return [Boolean]
|
|
175
|
+
def ==(other)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
unless method_defined?(:eql?)
|
|
180
|
+
# Strict equality for Hash key and Set member semantics.
|
|
181
|
+
# @param other [Object]
|
|
182
|
+
# @return [Boolean]
|
|
183
|
+
def eql?(other)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
unless method_defined?(:hash)
|
|
188
|
+
# Hash value consistent with {#eql?} for use as Hash keys.
|
|
189
|
+
# @return [Integer]
|
|
190
|
+
def hash
|
|
191
|
+
end
|
|
192
|
+
end
|
|
167
193
|
end
|
|
168
194
|
|
|
169
195
|
# Operating system platform enumeration backed by Rust.
|
|
@@ -195,6 +221,36 @@ module Wreq
|
|
|
195
221
|
def to_s
|
|
196
222
|
end
|
|
197
223
|
end
|
|
224
|
+
|
|
225
|
+
unless method_defined?(:to_sym)
|
|
226
|
+
# Returns the platform as a lowercase symbol (e.g. :windows, :linux).
|
|
227
|
+
# @return [Symbol]
|
|
228
|
+
def to_sym
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
unless method_defined?(:==)
|
|
233
|
+
# Value-based equality.
|
|
234
|
+
# @param other [Object]
|
|
235
|
+
# @return [Boolean]
|
|
236
|
+
def ==(other)
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
unless method_defined?(:eql?)
|
|
241
|
+
# Strict equality for Hash key and Set member semantics.
|
|
242
|
+
# @param other [Object]
|
|
243
|
+
# @return [Boolean]
|
|
244
|
+
def eql?(other)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
unless method_defined?(:hash)
|
|
249
|
+
# Hash value consistent with {#eql?} for use as Hash keys.
|
|
250
|
+
# @return [Integer]
|
|
251
|
+
def hash
|
|
252
|
+
end
|
|
253
|
+
end
|
|
198
254
|
end
|
|
199
255
|
|
|
200
256
|
# Emulation option wrapper.
|
|
@@ -216,19 +272,31 @@ module Wreq
|
|
|
216
272
|
#
|
|
217
273
|
# @param profile [Wreq::Profile, nil] Fingerprint profile to emulate
|
|
218
274
|
# @param platform [Wreq::Platform, nil] Operating system platform to emulate
|
|
219
|
-
# @param http2 [Boolean] Whether HTTP/2
|
|
220
|
-
#
|
|
275
|
+
# @param http2 [Boolean, nil] Whether HTTP/2 emulation is enabled; defaults
|
|
276
|
+
# to true when omitted or nil
|
|
277
|
+
# @param headers [Boolean, nil] Whether default emulation headers are enabled;
|
|
278
|
+
# defaults to true when omitted or nil
|
|
279
|
+
# @return [Wreq::Emulation] Configured emulation settings
|
|
280
|
+
# @raise [ArgumentError] if an option is unknown or extra arguments are given
|
|
281
|
+
# @raise [TypeError] if the option argument is not a Hash or a value has the
|
|
282
|
+
# wrong Ruby type
|
|
221
283
|
class Emulation
|
|
222
284
|
# Native fields and methods are set by the extension.
|
|
223
285
|
# This stub is for documentation only.
|
|
224
|
-
unless
|
|
286
|
+
unless singleton_methods(false).include?(:new)
|
|
225
287
|
# @param profile [Wreq::Profile, nil] Fingerprint profile to emulate
|
|
226
288
|
# @param platform [Wreq::Platform, nil] Operating system platform to emulate
|
|
227
|
-
# @param http2 [Boolean] Whether HTTP/2
|
|
228
|
-
#
|
|
289
|
+
# @param http2 [Boolean, nil] Whether HTTP/2 emulation is enabled; defaults
|
|
290
|
+
# to true when omitted or nil
|
|
291
|
+
# @param headers [Boolean, nil] Whether default emulation headers are enabled;
|
|
292
|
+
# defaults to true when omitted or nil
|
|
229
293
|
# @return [Wreq::Emulation] Configured emulation settings
|
|
230
|
-
|
|
294
|
+
# @raise [ArgumentError] if an option is unknown or extra arguments are given
|
|
295
|
+
# @raise [TypeError] if an option or value has the wrong Ruby type
|
|
296
|
+
def self.new(**options)
|
|
231
297
|
end
|
|
232
298
|
end
|
|
233
299
|
end
|
|
234
300
|
end
|
|
301
|
+
|
|
302
|
+
# standard:enable Naming/ConstantName
|
data/lib/wreq_ruby/error.rb
CHANGED
|
@@ -88,7 +88,7 @@ unless defined?(Wreq)
|
|
|
88
88
|
#
|
|
89
89
|
# @example
|
|
90
90
|
# begin
|
|
91
|
-
# client = Wreq::Client.new(max_redirects: 3)
|
|
91
|
+
# client = Wreq::Client.new(allow_redirects: true, max_redirects: 3)
|
|
92
92
|
# client.get("https://httpbin.io/redirect/10")
|
|
93
93
|
# rescue Wreq::RedirectError => e
|
|
94
94
|
# puts "Too many redirects: #{e.message}"
|
|
@@ -139,16 +139,14 @@ unless defined?(Wreq)
|
|
|
139
139
|
|
|
140
140
|
# Configuration and builder errors
|
|
141
141
|
|
|
142
|
-
#
|
|
142
|
+
# A native client or request configuration could not be built.
|
|
143
143
|
#
|
|
144
|
-
# Raised when
|
|
144
|
+
# Raised when validated Ruby options cannot be represented by the native
|
|
145
|
+
# builder or request body.
|
|
145
146
|
#
|
|
146
147
|
# @example
|
|
147
148
|
# begin
|
|
148
|
-
# client = Wreq::Client.new(
|
|
149
|
-
# proxy: "invalid://proxy",
|
|
150
|
-
# timeout: -1
|
|
151
|
-
# )
|
|
149
|
+
# client = Wreq::Client.new(proxy: "invalid://")
|
|
152
150
|
# rescue Wreq::BuilderError => e
|
|
153
151
|
# puts "Invalid configuration: #{e.message}"
|
|
154
152
|
# end
|
data/lib/wreq_ruby/http.rb
CHANGED
|
@@ -25,6 +25,43 @@ module Wreq
|
|
|
25
25
|
TRACE = nil # @return [Wreq::Method] HTTP TRACE method
|
|
26
26
|
PATCH = nil # @return [Wreq::Method] HTTP PATCH method
|
|
27
27
|
end
|
|
28
|
+
|
|
29
|
+
# Returns the HTTP method token (e.g. "GET", "POST").
|
|
30
|
+
# @return [String]
|
|
31
|
+
unless method_defined?(:to_s)
|
|
32
|
+
def to_s
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Returns the HTTP method as a lowercase symbol (e.g. :get, :post).
|
|
37
|
+
# @return [Symbol]
|
|
38
|
+
unless method_defined?(:to_sym)
|
|
39
|
+
def to_sym
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Value-based equality. Returns true when both represent the same HTTP method.
|
|
44
|
+
# @param other [Object]
|
|
45
|
+
# @return [Boolean]
|
|
46
|
+
unless method_defined?(:==)
|
|
47
|
+
def ==(other)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Strict equality for Hash key and Set member semantics.
|
|
52
|
+
# @param other [Object]
|
|
53
|
+
# @return [Boolean]
|
|
54
|
+
unless method_defined?(:eql?)
|
|
55
|
+
def eql?(other)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Hash value consistent with {#eql?} for use as Hash keys.
|
|
60
|
+
# @return [Integer]
|
|
61
|
+
unless method_defined?(:hash)
|
|
62
|
+
def hash
|
|
63
|
+
end
|
|
64
|
+
end
|
|
28
65
|
end
|
|
29
66
|
|
|
30
67
|
# HTTP version enumeration backed by Rust.
|
|
@@ -63,6 +100,21 @@ module Wreq
|
|
|
63
100
|
def ==(other)
|
|
64
101
|
end
|
|
65
102
|
end
|
|
103
|
+
|
|
104
|
+
# Strict equality for Hash key and Set member semantics.
|
|
105
|
+
# @param other [Object]
|
|
106
|
+
# @return [Boolean]
|
|
107
|
+
unless method_defined?(:eql?)
|
|
108
|
+
def eql?(other)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Hash value consistent with {#eql?} for use as Hash keys.
|
|
113
|
+
# @return [Integer]
|
|
114
|
+
unless method_defined?(:hash)
|
|
115
|
+
def hash
|
|
116
|
+
end
|
|
117
|
+
end
|
|
66
118
|
end
|
|
67
119
|
|
|
68
120
|
# HTTP status code wrapper.
|
|
@@ -141,6 +193,28 @@ module Wreq
|
|
|
141
193
|
# @return [String] Status code as string
|
|
142
194
|
def to_s
|
|
143
195
|
end
|
|
196
|
+
|
|
197
|
+
# Returns the status code as an integer.
|
|
198
|
+
# @return [Integer] the numeric HTTP status code
|
|
199
|
+
def to_i
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Value-based equality. Only compares with other StatusCode instances.
|
|
203
|
+
# @param other [Object]
|
|
204
|
+
# @return [Boolean]
|
|
205
|
+
def ==(other)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Strict equality for Hash key and Set member semantics.
|
|
209
|
+
# @param other [Object]
|
|
210
|
+
# @return [Boolean]
|
|
211
|
+
def eql?(other)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Hash value consistent with {#eql?} for use as Hash keys.
|
|
215
|
+
# @return [Integer]
|
|
216
|
+
def hash
|
|
217
|
+
end
|
|
144
218
|
end
|
|
145
219
|
end
|
|
146
220
|
end
|
data/lib/wreq_ruby/response.rb
CHANGED
|
@@ -127,6 +127,9 @@ unless defined?(Wreq)
|
|
|
127
127
|
|
|
128
128
|
# Parse the response body as JSON.
|
|
129
129
|
#
|
|
130
|
+
# Integral JSON numbers are returned as arbitrary-precision Ruby Integer
|
|
131
|
+
# values. Fractional and exponent-form numbers are returned as Float values.
|
|
132
|
+
#
|
|
130
133
|
# @return [Object] Parsed JSON (Hash, Array, String, Integer, Float, Boolean, nil)
|
|
131
134
|
# @raise [Wreq::DecodingError] if body is not valid JSON
|
|
132
135
|
# @example
|
data/script/rust_env.rb
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module Wreq
|
|
7
|
+
module RustEnv
|
|
8
|
+
WINDOWS_GNU_TARGET = "x86_64-pc-windows-gnu"
|
|
9
|
+
WINDOWS_RUBY_PLATFORM = "x64-mingw-ucrt"
|
|
10
|
+
|
|
11
|
+
# Uses the UCRT toolchain shipped with the active RubyInstaller installation.
|
|
12
|
+
def self.activate
|
|
13
|
+
unless RbConfig::CONFIG["host_os"].match?(/mingw|mswin/)
|
|
14
|
+
FileUtils.rm_f(cargo_config_path)
|
|
15
|
+
return
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
ruby_platform = RbConfig::CONFIG.fetch("arch")
|
|
19
|
+
unless ruby_platform == WINDOWS_RUBY_PLATFORM
|
|
20
|
+
raise "Unsupported Windows Ruby platform: #{ruby_platform}. " \
|
|
21
|
+
"Use RubyInstaller UCRT (#{WINDOWS_RUBY_PLATFORM})."
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
ruby_root = RbConfig::CONFIG.fetch("prefix")
|
|
25
|
+
msys_root = File.join(ruby_root, "msys64")
|
|
26
|
+
ucrt_bin = File.join(msys_root, "ucrt64", "bin")
|
|
27
|
+
msys_bin = File.join(msys_root, "usr", "bin")
|
|
28
|
+
|
|
29
|
+
validate_tools(ucrt_bin, msys_bin)
|
|
30
|
+
write_cargo_config(ucrt_bin, msys_bin)
|
|
31
|
+
ENV["CARGO_BUILD_TARGET"] = WINDOWS_GNU_TARGET
|
|
32
|
+
ENV["LIBCLANG_PATH"] = ucrt_bin
|
|
33
|
+
ENV.delete("CMAKE_GENERATOR")
|
|
34
|
+
ENV["PATH"] = [ucrt_bin, msys_bin, ENV.fetch("PATH", nil)].compact.join(File::PATH_SEPARATOR)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Reports missing RubyInstaller packages before Cargo starts compiling.
|
|
38
|
+
def self.validate_tools(ucrt_bin, msys_bin)
|
|
39
|
+
required = %w[gcc.exe g++.exe gcc-ar.exe clang.exe libclang.dll cmake.exe]
|
|
40
|
+
.map { |tool| File.join(ucrt_bin, tool) }
|
|
41
|
+
.append(File.join(msys_bin, "make.exe"))
|
|
42
|
+
missing = required.reject { |tool| File.file?(tool) }
|
|
43
|
+
unless %w[pkgconf.exe pkg-config.exe].any? { |tool| File.file?(File.join(ucrt_bin, tool)) }
|
|
44
|
+
missing << File.join(ucrt_bin, "pkgconf.exe")
|
|
45
|
+
end
|
|
46
|
+
return if missing.empty?
|
|
47
|
+
|
|
48
|
+
raise "Missing RubyInstaller UCRT tools: #{missing.join(", ")}. " \
|
|
49
|
+
"Install the Windows build dependencies documented in " \
|
|
50
|
+
"docs/windows-gnu-tokio-crash.md."
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Generates machine-local Cargo defaults that work in command lines and IDEs.
|
|
54
|
+
def self.write_cargo_config(ucrt_bin, msys_bin)
|
|
55
|
+
config = <<~TOML
|
|
56
|
+
# Generated by script/rust_env.rb for the active RubyInstaller.
|
|
57
|
+
|
|
58
|
+
[build]
|
|
59
|
+
target = "#{WINDOWS_GNU_TARGET}"
|
|
60
|
+
|
|
61
|
+
[target.#{WINDOWS_GNU_TARGET}]
|
|
62
|
+
linker = "#{File.join(ucrt_bin, "gcc.exe")}"
|
|
63
|
+
|
|
64
|
+
[env]
|
|
65
|
+
CC_x86_64_pc_windows_gnu = { value = "#{File.join(ucrt_bin, "gcc.exe")}", force = true }
|
|
66
|
+
CXX_x86_64_pc_windows_gnu = { value = "#{File.join(ucrt_bin, "g++.exe")}", force = true }
|
|
67
|
+
AR_x86_64_pc_windows_gnu = { value = "#{File.join(ucrt_bin, "gcc-ar.exe")}", force = true }
|
|
68
|
+
CMAKE = { value = "#{File.join(ucrt_bin, "cmake.exe")}", force = true }
|
|
69
|
+
CMAKE_GENERATOR = { value = "MSYS Makefiles", force = true }
|
|
70
|
+
CMAKE_MAKE_PROGRAM = { value = "#{File.join(msys_bin, "make.exe")}", force = true }
|
|
71
|
+
LIBCLANG_PATH = { value = "#{ucrt_bin}", force = true }
|
|
72
|
+
TOML
|
|
73
|
+
|
|
74
|
+
return if File.exist?(cargo_config_path) && File.binread(cargo_config_path) == config
|
|
75
|
+
|
|
76
|
+
FileUtils.mkdir_p(File.dirname(cargo_config_path))
|
|
77
|
+
File.binwrite(cargo_config_path, config)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns the ignored Cargo configuration shared by Rake and language servers.
|
|
81
|
+
def self.cargo_config_path
|
|
82
|
+
File.expand_path("../.cargo/windows.toml", __dir__)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
data/src/arch.rs
CHANGED
|
@@ -4,6 +4,28 @@
|
|
|
4
4
|
//! ABI boundaries, or OS APIs used by the Rust extension. Normal HTTP client
|
|
5
5
|
//! behavior should stay in the client/runtime modules so platform workarounds
|
|
6
6
|
//! do not leak into the rest of the binding.
|
|
7
|
+
#![allow(unsafe_code)]
|
|
8
|
+
|
|
9
|
+
/// Whether the native client exposes TCP user-timeout configuration.
|
|
10
|
+
pub(crate) const SUPPORTS_TCP_USER_TIMEOUT: bool = cfg!(any(
|
|
11
|
+
target_os = "android",
|
|
12
|
+
target_os = "fuchsia",
|
|
13
|
+
target_os = "linux"
|
|
14
|
+
));
|
|
15
|
+
|
|
16
|
+
/// Whether the native client exposes network-interface binding.
|
|
17
|
+
pub(crate) const SUPPORTS_INTERFACE: bool = cfg!(any(
|
|
18
|
+
target_os = "android",
|
|
19
|
+
target_os = "fuchsia",
|
|
20
|
+
target_os = "illumos",
|
|
21
|
+
target_os = "ios",
|
|
22
|
+
target_os = "linux",
|
|
23
|
+
target_os = "macos",
|
|
24
|
+
target_os = "solaris",
|
|
25
|
+
target_os = "tvos",
|
|
26
|
+
target_os = "visionos",
|
|
27
|
+
target_os = "watchos",
|
|
28
|
+
));
|
|
7
29
|
|
|
8
30
|
#[cfg(all(target_os = "windows", target_env = "gnu"))]
|
|
9
31
|
mod windows_gnu {
|
data/src/client/body/form.rs
CHANGED
data/src/client/body/json.rs
CHANGED
|
@@ -1,16 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//! JSON conversion at the Ruby request and response boundary.
|
|
2
|
+
//!
|
|
3
|
+
//! Request values are converted from Ruby into an owned [`Json`] tree before
|
|
4
|
+
//! network I/O. Response bodies are deserialized into the same tree by wreq and
|
|
5
|
+
//! then converted back into Ruby values.
|
|
6
|
+
//!
|
|
7
|
+
//! The underlying `serde_json` configuration preserves object insertion order
|
|
8
|
+
//! and arbitrary-size integer tokens in both directions.
|
|
3
9
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
use ::serde::{Deserialize, Serialize};
|
|
11
|
+
use magnus::{Error, Ruby, TryConvert, Value};
|
|
12
|
+
|
|
13
|
+
use crate::{error::json_serialization_error, serde::deserialize_json};
|
|
14
|
+
|
|
15
|
+
/// An owned JSON tree shared by request and response conversion.
|
|
16
|
+
///
|
|
17
|
+
/// This wrapper keeps the configured `serde_json::Value` representation private
|
|
18
|
+
/// so callers use the same precision and ordering behavior in both directions.
|
|
19
|
+
#[derive(Deserialize, Serialize)]
|
|
20
|
+
#[serde(transparent)]
|
|
21
|
+
pub struct Json(serde_json::Value);
|
|
22
|
+
|
|
23
|
+
/// Convert supported Ruby request values into an owned JSON tree.
|
|
24
|
+
///
|
|
25
|
+
/// Supported values are `Hash`, `Array`, `String`, `Symbol`, `Integer`, finite
|
|
26
|
+
/// `Float`, booleans, and `nil`. Hash keys must be strings or symbols. Arrays
|
|
27
|
+
/// and hashes are limited to 100 nesting levels, which also bounds cyclic input.
|
|
28
|
+
/// Unsupported values are reported as `Wreq::BuilderError` before network I/O.
|
|
29
|
+
impl TryConvert for Json {
|
|
30
|
+
fn try_convert(value: Value) -> Result<Self, Error> {
|
|
31
|
+
let ruby = Ruby::get_with(value);
|
|
32
|
+
deserialize_json(&ruby, value)
|
|
33
|
+
.map(Self)
|
|
34
|
+
.map_err(|error| json_serialization_error(&ruby, error))
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#[cfg(test)]
|
|
39
|
+
mod tests {
|
|
40
|
+
use super::Json;
|
|
41
|
+
|
|
42
|
+
#[test]
|
|
43
|
+
fn preserves_number_precision_and_object_order() {
|
|
44
|
+
let source = br#"{"second":115792089237316195423570985008687907853269984665640564039457584007913129639936,"first":1}"#;
|
|
45
|
+
let json: Json = serde_json::from_slice(source).unwrap();
|
|
46
|
+
|
|
47
|
+
assert_eq!(source, serde_json::to_vec(&json).unwrap().as_slice());
|
|
48
|
+
}
|
|
16
49
|
}
|