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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.cargo/config.toml +4 -0
  3. data/Cargo.lock +135 -119
  4. data/Cargo.toml +11 -3
  5. data/Rakefile +6 -0
  6. data/crates/wreq-util/README.md +1 -1
  7. data/crates/wreq-util/examples/emulate.rs +3 -3
  8. data/crates/wreq-util/src/emulate/macros.rs +13 -11
  9. data/crates/wreq-util/src/emulate/profile/chrome/header.rs +18 -3
  10. data/crates/wreq-util/src/emulate/profile/firefox/header.rs +16 -0
  11. data/crates/wreq-util/src/emulate/profile/opera/header.rs +6 -7
  12. data/crates/wreq-util/tests/client.rs +103 -1
  13. data/docs/windows-gnu-tokio-crash.md +49 -3
  14. data/extconf.rb +4 -0
  15. data/lib/wreq.rb +70 -45
  16. data/lib/wreq_ruby/body.rb +29 -11
  17. data/lib/wreq_ruby/client.rb +88 -55
  18. data/lib/wreq_ruby/cookie.rb +79 -19
  19. data/lib/wreq_ruby/emulate.rb +74 -6
  20. data/lib/wreq_ruby/error.rb +5 -7
  21. data/lib/wreq_ruby/http.rb +74 -0
  22. data/lib/wreq_ruby/response.rb +3 -0
  23. data/script/rust_env.rb +85 -0
  24. data/src/arch.rs +22 -0
  25. data/src/client/body/form.rs +2 -0
  26. data/src/client/body/json.rs +47 -14
  27. data/src/client/body/stream.rs +147 -43
  28. data/src/client/body.rs +11 -15
  29. data/src/client/param.rs +7 -7
  30. data/src/client/req.rs +87 -47
  31. data/src/client/resp.rs +23 -24
  32. data/src/client.rs +310 -230
  33. data/src/cookie.rs +280 -87
  34. data/src/emulate.rs +85 -50
  35. data/src/error.rs +198 -41
  36. data/src/extractor.rs +16 -76
  37. data/src/header.rs +146 -23
  38. data/src/http.rs +62 -33
  39. data/src/lib.rs +26 -20
  40. data/src/macros.rs +71 -46
  41. data/src/options.rs +284 -0
  42. data/src/rt.rs +22 -11
  43. data/src/serde/de/array_deserializer.rs +48 -0
  44. data/src/serde/de/array_enumerator.rs +59 -0
  45. data/src/serde/de/deserializer.rs +307 -0
  46. data/src/serde/de/enum_deserializer.rs +47 -0
  47. data/src/serde/de/hash_deserializer.rs +89 -0
  48. data/src/serde/de/number_deserializer.rs +51 -0
  49. data/src/serde/de/variant_deserializer.rs +101 -0
  50. data/src/serde/de.rs +33 -0
  51. data/src/serde/error.rs +146 -0
  52. data/src/serde/ser/enums.rs +14 -0
  53. data/src/serde/ser/map_serializer.rs +56 -0
  54. data/src/serde/ser/seq_serializer.rs +71 -0
  55. data/src/serde/ser/serializer.rs +194 -0
  56. data/src/serde/ser/struct_serializer.rs +106 -0
  57. data/src/serde/ser/struct_variant_serializer.rs +44 -0
  58. data/src/serde/ser/tuple_variant_serializer.rs +41 -0
  59. data/src/serde/ser.rs +18 -0
  60. data/src/serde/tests.rs +237 -0
  61. data/src/serde.rs +202 -0
  62. data/test/body_sender_test.rb +246 -0
  63. data/test/cookie_test.rb +211 -10
  64. data/test/json_precision_test.rb +209 -0
  65. data/test/option_validation_test.rb +311 -0
  66. data/test/value_semantics_test.rb +238 -0
  67. data/wreq.gemspec +1 -1
  68. metadata +28 -4
  69. data/script/build_windows_gnu.ps1 +0 -264
  70. data/src/header/helper.rs +0 -112
data/src/emulate.rs CHANGED
@@ -1,11 +1,47 @@
1
- use magnus::{
2
- Error, Module, Object, RHash, RModule, Ruby, TryConvert, Value, function, method,
3
- typed_data::{Inspect, Obj},
4
- };
1
+ use ::serde::Deserialize;
2
+ use magnus::{Error, Module, Object, RModule, Ruby, Value, function, method, typed_data::Obj};
5
3
 
4
+ use crate::options::{NativeOption, Options};
5
+
6
+ /// Keyword arguments accepted by `Wreq::Emulation.new`.
7
+ #[derive(Default, Deserialize)]
8
+ struct Builder {
9
+ /// The browser profile to emulate.
10
+ #[serde(default)]
11
+ profile: NativeOption<Obj<Profile>>,
12
+
13
+ /// The operating-system profile to emulate.
14
+ #[serde(default)]
15
+ platform: NativeOption<Obj<Platform>>,
16
+
17
+ /// Whether HTTP/2 settings are emulated.
18
+ http2: Option<bool>,
19
+
20
+ /// Whether browser headers are emulated.
21
+ headers: Option<bool>,
22
+ }
23
+
24
+ // ===== impl Builder =====
25
+
26
+ impl Builder {
27
+ /// Deserialize and convert one validated emulation options Hash.
28
+ ///
29
+ /// # Errors
30
+ ///
31
+ /// Returns `ArgumentError` for unknown or duplicate options and `TypeError`
32
+ /// for invalid option values.
33
+ fn from_options(options: Options<'_>) -> Result<Self, Error> {
34
+ let mut builder = options.validate_keys::<Self>()?.deserialize::<Self>()?;
35
+ extract_native_option!(options, builder, profile);
36
+ extract_native_option!(options, builder, platform);
37
+ Ok(builder)
38
+ }
39
+ }
40
+
41
+ // Defines constant registration, `into_ffi`/`from_ffi`, and handlers for
42
+ // Ruby's `to_s`, `==`, `eql?`, and `hash` methods.
6
43
  define_ruby_enum!(
7
44
  /// An emulation profile.
8
- const,
9
45
  Profile,
10
46
  "Wreq::Profile",
11
47
  wreq_util::Profile,
@@ -150,17 +186,19 @@ define_ruby_enum!(
150
186
  Opera131,
151
187
  );
152
188
 
189
+ // Defines constant registration, `into_ffi`/`from_ffi`, and handlers for
190
+ // Ruby's `to_s`, `to_sym`, `==`, `eql?`, and `hash` methods.
153
191
  define_ruby_enum!(
154
192
  /// An emulation profile for OS.
155
- const,
156
193
  Platform,
157
194
  "Wreq::Platform",
158
195
  wreq_util::Platform,
159
- Windows,
160
- MacOS,
161
- Linux,
162
- Android,
163
- IOS,
196
+ symbols:
197
+ Windows => "windows",
198
+ MacOS => "macos",
199
+ Linux => "linux",
200
+ Android => "android",
201
+ IOS => "ios",
164
202
  );
165
203
 
166
204
  /// A struct to represent the `EmulationOption` class.
@@ -168,51 +206,41 @@ define_ruby_enum!(
168
206
  #[magnus::wrap(class = "Wreq::Emulation", free_immediately, size)]
169
207
  pub struct Emulation(pub wreq_util::Emulation);
170
208
 
171
- // ===== impl Profile =====
172
-
173
- impl Profile {
174
- pub fn to_s(&self) -> String {
175
- self.into_ffi().inspect()
176
- }
177
- }
178
-
179
- // ===== impl Platform =====
180
-
181
- impl Platform {
182
- pub fn to_s(&self) -> String {
183
- self.into_ffi().inspect()
184
- }
185
- }
186
-
187
209
  // ===== impl Emulation =====
188
210
 
189
211
  impl Emulation {
212
+ /// Create emulation settings from one optional Hash.
213
+ ///
214
+ /// Unknown keys, non-Hash arguments, and extra positional arguments are
215
+ /// rejected before the native emulation value is built.
216
+ ///
217
+ /// # Errors
218
+ ///
219
+ /// Returns `ArgumentError` for unknown keys or argument count and
220
+ /// `TypeError` for invalid option values.
190
221
  fn new(ruby: &Ruby, args: &[Value]) -> Result<Self, Error> {
191
- let mut profile = None;
192
- let mut platform = None;
193
- let mut http2 = None;
194
- let mut headers = None;
195
-
196
- if let Some(hash) = args.first().and_then(|v| RHash::from_value(*v)) {
197
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(profile))) {
198
- profile = Some(Obj::<Profile>::try_convert(v)?);
199
- }
200
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(platform))) {
201
- platform = Some(Obj::<Platform>::try_convert(v)?);
202
- }
203
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(http2))) {
204
- http2 = Some(bool::try_convert(v)?);
205
- }
206
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(headers))) {
207
- headers = Some(bool::try_convert(v)?);
208
- }
209
- }
222
+ let mut params = Options::from_args(ruby, args, "emulation")?
223
+ .map(Builder::from_options)
224
+ .transpose()?
225
+ .unwrap_or_default();
210
226
 
211
227
  let emulation = wreq_util::Emulation::builder()
212
- .profile(profile.map(|obj| obj.into_ffi()).unwrap_or_default())
213
- .platform(platform.map(|os| os.into_ffi()).unwrap_or_default())
214
- .http2(http2.unwrap_or(true))
215
- .headers(headers.unwrap_or(true))
228
+ .profile(
229
+ params
230
+ .profile
231
+ .take()
232
+ .map(|obj| obj.into_ffi())
233
+ .unwrap_or_default(),
234
+ )
235
+ .platform(
236
+ params
237
+ .platform
238
+ .take()
239
+ .map(|os| os.into_ffi())
240
+ .unwrap_or_default(),
241
+ )
242
+ .http2(params.http2.unwrap_or(true))
243
+ .headers(params.headers.unwrap_or(true))
216
244
  .build();
217
245
 
218
246
  Ok(Self(emulation))
@@ -223,11 +251,18 @@ pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
223
251
  // Profile enum binding
224
252
  let profile = gem_module.define_class("Profile", ruby.class_object())?;
225
253
  profile.define_method("to_s", method!(Profile::to_s, 0))?;
254
+ profile.define_method("==", method!(Profile::equals, 1))?;
255
+ profile.define_method("eql?", method!(Profile::is_eql, 1))?;
256
+ profile.define_method("hash", method!(Profile::hash_value, 0))?;
226
257
  Profile::define_constants(profile)?;
227
258
 
228
259
  // Platform enum binding
229
260
  let platform = gem_module.define_class("Platform", ruby.class_object())?;
230
261
  platform.define_method("to_s", method!(Platform::to_s, 0))?;
262
+ platform.define_method("to_sym", method!(Platform::to_sym, 0))?;
263
+ platform.define_method("==", method!(Platform::equals, 1))?;
264
+ platform.define_method("eql?", method!(Platform::is_eql, 1))?;
265
+ platform.define_method("hash", method!(Platform::hash_value, 0))?;
231
266
  Platform::define_constants(platform)?;
232
267
 
233
268
  // Emulation class binding
data/src/error.rs CHANGED
@@ -1,5 +1,11 @@
1
+ use std::{
2
+ cell::{BorrowError, BorrowMutError},
3
+ fmt,
4
+ };
5
+
1
6
  use magnus::{
2
- Error as MagnusError, RModule, Ruby, exception::ExceptionClass, prelude::*, value::Lazy,
7
+ Error as MagnusError, RModule, Ruby, error::ErrorType, exception::ExceptionClass, prelude::*,
8
+ value::Lazy,
3
9
  };
4
10
  use tokio::sync::mpsc::error::SendError;
5
11
 
@@ -17,18 +23,24 @@ Potential solutions:
17
23
  3) Change the order of operations to reference the instance before borrowing it.
18
24
  "#;
19
25
 
20
- static WREQ: Lazy<RModule> = Lazy::new(|ruby| ruby.define_module(crate::RUBY_MODULE_NAME).unwrap());
21
-
22
26
  macro_rules! define_exception {
23
27
  ($name:ident, $ruby_name:literal, $parent_method:ident) => {
24
28
  static $name: Lazy<ExceptionClass> = Lazy::new(|ruby| {
25
- ruby.get_inner(&WREQ)
26
- .define_error($ruby_name, ruby.$parent_method())
27
- .unwrap()
29
+ ruby.class_object()
30
+ .const_get::<_, RModule>(crate::RUBY_MODULE_NAME)
31
+ .and_then(|module| module.const_get::<_, ExceptionClass>($ruby_name))
32
+ .unwrap_or_else(|_| ruby.$parent_method())
28
33
  });
29
34
  };
30
35
  }
31
36
 
37
+ macro_rules! initialize_exception {
38
+ ($ruby:expr, $module:expr, $name:ident, $ruby_name:literal, $parent_method:ident) => {{
39
+ $module.define_error($ruby_name, $ruby.$parent_method())?;
40
+ Lazy::force(&$name, $ruby);
41
+ }};
42
+ }
43
+
32
44
  macro_rules! map_wreq_error {
33
45
  ($ruby:expr, $err:expr, $msg:expr, $($check_method:ident => $exception:ident),* $(,)?) => {
34
46
  {
@@ -73,60 +85,127 @@ define_exception!(DECODING_ERROR, "DecodingError", exception_runtime_error);
73
85
  define_exception!(BUILDER_ERROR, "BuilderError", exception_runtime_error);
74
86
 
75
87
  /// Memory error constant
76
- pub fn memory_error() -> MagnusError {
77
- MagnusError::new(ruby!().get_inner(&MEMORY), RACE_CONDITION_ERROR_MSG)
88
+ pub fn memory_error(ruby: &Ruby) -> MagnusError {
89
+ MagnusError::new(ruby.get_inner(&MEMORY), RACE_CONDITION_ERROR_MSG)
78
90
  }
79
91
 
80
- /// Create a Ruby thread interruption error.
81
- pub fn interrupt_error() -> MagnusError {
82
- MagnusError::new(ruby!().exception_interrupt(), "request interrupted")
92
+ /// Create Ruby's standard thread interruption error.
93
+ pub fn interrupt_error(ruby: &Ruby) -> MagnusError {
94
+ MagnusError::new(ruby.exception_interrupt(), "request interrupted")
95
+ }
96
+
97
+ /// Map a Tokio runtime initialization failure to `Wreq::BuilderError`.
98
+ pub fn runtime_initialization_error(ruby: &Ruby, err: &std::io::Error) -> MagnusError {
99
+ MagnusError::new(
100
+ ruby.get_inner(&BUILDER_ERROR),
101
+ format!("failed to initialize Tokio runtime: {err}"),
102
+ )
83
103
  }
84
104
 
85
105
  /// LocalJumpError for methods that require a Ruby block.
86
- pub fn no_block_given_error() -> MagnusError {
106
+ pub fn no_block_given_error(ruby: &Ruby) -> MagnusError {
107
+ MagnusError::new(ruby.exception_local_jump_error(), "no block given (yield)")
108
+ }
109
+
110
+ /// Build an `IOError` for writes to a closed request-body sender.
111
+ pub fn closed_body_sender_error(ruby: &Ruby) -> MagnusError {
112
+ MagnusError::new(ruby.exception_io_error(), "closed body sender")
113
+ }
114
+
115
+ /// Map a failed body-channel send to `IOError`.
116
+ pub fn body_sender_send_error<T>(ruby: &Ruby, err: SendError<T>) -> MagnusError {
87
117
  MagnusError::new(
88
- ruby!().exception_local_jump_error(),
89
- "no block given (yield)",
118
+ ruby.exception_io_error(),
119
+ format!("closed body sender: {err}"),
90
120
  )
91
121
  }
92
122
 
93
- /// Map [`tokio::sync::mpsc::error::SendError`] to corresponding [`magnus::Error`]
94
- pub fn mpsc_send_error_to_magnus<T>(err: SendError<T>) -> MagnusError {
123
+ /// Map an immutable sender-state borrow failure to `Wreq::BodyError`.
124
+ pub fn body_sender_borrow_error(ruby: &Ruby, err: BorrowError) -> MagnusError {
95
125
  MagnusError::new(
96
- ruby!().get_inner(&BODY_ERROR),
97
- format!("failed to send body chunk: {}", err),
126
+ ruby.get_inner(&BODY_ERROR),
127
+ format!("body sender state is unavailable: {err}"),
128
+ )
129
+ }
130
+
131
+ /// Map a mutable sender-state borrow failure to `Wreq::BodyError`.
132
+ pub fn body_sender_borrow_mut_error(ruby: &Ruby, err: BorrowMutError) -> MagnusError {
133
+ MagnusError::new(
134
+ ruby.get_inner(&BODY_ERROR),
135
+ format!("body sender state is unavailable: {err}"),
98
136
  )
99
137
  }
100
138
 
101
139
  /// Map [`wreq::header::InvalidHeaderName`] to corresponding [`magnus::Error`]
102
- pub fn header_name_error_to_magnus(err: wreq::header::InvalidHeaderName) -> MagnusError {
140
+ pub fn header_name_error(ruby: &Ruby, err: wreq::header::InvalidHeaderName) -> MagnusError {
103
141
  MagnusError::new(
104
- ruby!().get_inner(&BUILDER_ERROR),
142
+ ruby.get_inner(&BUILDER_ERROR),
105
143
  format!("invalid header name: {err}"),
106
144
  )
107
145
  }
108
146
 
109
147
  /// Map [`wreq::header::InvalidHeaderValue`] to corresponding [`magnus::Error`]
110
- pub fn header_value_error_to_magnus(err: wreq::header::InvalidHeaderValue) -> MagnusError {
148
+ pub fn header_value_error(ruby: &Ruby, err: wreq::header::InvalidHeaderValue) -> MagnusError {
111
149
  MagnusError::new(
112
- ruby!().get_inner(&BUILDER_ERROR),
150
+ ruby.get_inner(&BUILDER_ERROR),
113
151
  format!("invalid header value: {err}"),
114
152
  )
115
153
  }
116
154
 
117
- /// Map type/value errors to corresponding [`magnus::Error`]
118
- pub fn type_value_error_to_magnus(err: &str) -> MagnusError {
155
+ /// Build a `Wreq::BuilderError` for an invalid Ruby header structure.
156
+ pub fn header_type_error(ruby: &Ruby, err: &str) -> MagnusError {
157
+ MagnusError::new(ruby.get_inner(&BUILDER_ERROR), format!("type error: {err}"))
158
+ }
159
+
160
+ /// Build a `Wreq::BuilderError` for unsupported request JSON values.
161
+ pub fn json_serialization_error(ruby: &Ruby, err: MagnusError) -> MagnusError {
119
162
  MagnusError::new(
120
- ruby!().get_inner(&BUILDER_ERROR),
121
- format!("type error: {err}"),
163
+ ruby.get_inner(&BUILDER_ERROR),
164
+ format!("JSON serialization error: {err}"),
122
165
  )
123
166
  }
124
167
 
168
+ /// Prefix a Magnus error while preserving its original Ruby exception class.
169
+ pub(crate) fn contextualize_magnus_error(
170
+ err: MagnusError,
171
+ context: fmt::Arguments<'_>,
172
+ ) -> MagnusError {
173
+ match err.error_type() {
174
+ ErrorType::Error(class, message) => {
175
+ MagnusError::new(*class, format!("{context}: {message}"))
176
+ }
177
+ ErrorType::Exception(exception) => MagnusError::new(
178
+ exception.exception_class(),
179
+ format!("{context}: {exception}"),
180
+ ),
181
+ ErrorType::Jump(_) => err,
182
+ }
183
+ }
184
+
185
+ /// Add an option name while preserving the original Ruby exception class.
186
+ pub fn option_value_error(option: &str, err: MagnusError) -> MagnusError {
187
+ contextualize_magnus_error(err, format_args!("invalid value for :{option}"))
188
+ }
189
+
190
+ /// Build an `ArgumentError` from a validation message.
191
+ pub fn argument_error(ruby: &Ruby, message: impl Into<String>) -> MagnusError {
192
+ MagnusError::new(ruby.exception_arg_error(), message.into())
193
+ }
194
+
195
+ /// Builds a Ruby `RangeError` from a validation message.
196
+ pub fn range_error(ruby: &Ruby, message: impl Into<String>) -> MagnusError {
197
+ MagnusError::new(ruby.exception_range_error(), message.into())
198
+ }
199
+
200
+ /// Build a `TypeError` from a conversion message.
201
+ pub fn type_error(ruby: &Ruby, message: impl Into<String>) -> MagnusError {
202
+ MagnusError::new(ruby.exception_type_error(), message.into())
203
+ }
125
204
  /// Map [`wreq::Error`] to corresponding [`magnus::Error`]
126
- pub fn wreq_error_to_magnus(err: wreq::Error) -> MagnusError {
205
+ pub fn wreq_error(ruby: &Ruby, err: wreq::Error) -> MagnusError {
127
206
  let error_msg = err.to_string();
128
207
  map_wreq_error!(
129
- ruby!(),
208
+ ruby,
130
209
  err,
131
210
  error_msg,
132
211
  is_builder => BUILDER_ERROR,
@@ -143,17 +222,95 @@ pub fn wreq_error_to_magnus(err: wreq::Error) -> MagnusError {
143
222
  )
144
223
  }
145
224
 
146
- pub fn include(ruby: &Ruby) {
147
- Lazy::force(&MEMORY, ruby);
148
- Lazy::force(&CONNECTION_ERROR, ruby);
149
- Lazy::force(&PROXY_CONNECTION_ERROR, ruby);
150
- Lazy::force(&CONNECTION_RESET_ERROR, ruby);
151
- Lazy::force(&TLS_ERROR, ruby);
152
- Lazy::force(&REQUEST_ERROR, ruby);
153
- Lazy::force(&STATUS_ERROR, ruby);
154
- Lazy::force(&REDIRECT_ERROR, ruby);
155
- Lazy::force(&TIMEOUT_ERROR, ruby);
156
- Lazy::force(&BODY_ERROR, ruby);
157
- Lazy::force(&DECODING_ERROR, ruby);
158
- Lazy::force(&BUILDER_ERROR, ruby);
225
+ /// Define and retain the Ruby exception classes used by the binding.
226
+ ///
227
+ /// # Errors
228
+ ///
229
+ /// Returns the Ruby exception raised while defining an error class.
230
+ pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), MagnusError> {
231
+ initialize_exception!(
232
+ ruby,
233
+ gem_module,
234
+ MEMORY,
235
+ "MemoryError",
236
+ exception_runtime_error
237
+ );
238
+ initialize_exception!(
239
+ ruby,
240
+ gem_module,
241
+ CONNECTION_ERROR,
242
+ "ConnectionError",
243
+ exception_runtime_error
244
+ );
245
+ initialize_exception!(
246
+ ruby,
247
+ gem_module,
248
+ PROXY_CONNECTION_ERROR,
249
+ "ProxyConnectionError",
250
+ exception_runtime_error
251
+ );
252
+ initialize_exception!(
253
+ ruby,
254
+ gem_module,
255
+ CONNECTION_RESET_ERROR,
256
+ "ConnectionResetError",
257
+ exception_runtime_error
258
+ );
259
+ initialize_exception!(
260
+ ruby,
261
+ gem_module,
262
+ TLS_ERROR,
263
+ "TlsError",
264
+ exception_runtime_error
265
+ );
266
+ initialize_exception!(
267
+ ruby,
268
+ gem_module,
269
+ REQUEST_ERROR,
270
+ "RequestError",
271
+ exception_runtime_error
272
+ );
273
+ initialize_exception!(
274
+ ruby,
275
+ gem_module,
276
+ STATUS_ERROR,
277
+ "StatusError",
278
+ exception_runtime_error
279
+ );
280
+ initialize_exception!(
281
+ ruby,
282
+ gem_module,
283
+ REDIRECT_ERROR,
284
+ "RedirectError",
285
+ exception_runtime_error
286
+ );
287
+ initialize_exception!(
288
+ ruby,
289
+ gem_module,
290
+ TIMEOUT_ERROR,
291
+ "TimeoutError",
292
+ exception_runtime_error
293
+ );
294
+ initialize_exception!(
295
+ ruby,
296
+ gem_module,
297
+ BODY_ERROR,
298
+ "BodyError",
299
+ exception_runtime_error
300
+ );
301
+ initialize_exception!(
302
+ ruby,
303
+ gem_module,
304
+ DECODING_ERROR,
305
+ "DecodingError",
306
+ exception_runtime_error
307
+ );
308
+ initialize_exception!(
309
+ ruby,
310
+ gem_module,
311
+ BUILDER_ERROR,
312
+ "BuilderError",
313
+ exception_runtime_error
314
+ );
315
+ Ok(())
159
316
  }
data/src/extractor.rs CHANGED
@@ -1,12 +1,8 @@
1
- use magnus::{RArray, RHash, RString, Ruby, TryConvert, r_hash::ForEach};
2
- use wreq::{
3
- Proxy,
4
- header::{HeaderMap, HeaderName, HeaderValue, OrigHeaderMap},
5
- };
1
+ use magnus::{RHash, RString, Ruby, TryConvert, value::ReprValue};
2
+ use wreq::Proxy;
6
3
 
7
- use crate::error::{
8
- header_name_error_to_magnus, header_value_error_to_magnus, wreq_error_to_magnus,
9
- };
4
+ use crate::error::{option_value_error, wreq_error};
5
+ use crate::options;
10
6
 
11
7
  /// A trait that defines the parameter name for extraction.
12
8
  pub trait ExtractorName {
@@ -32,65 +28,6 @@ where
32
28
  }
33
29
  }
34
30
 
35
- // ===== impl Extractor<HeaderMap> =====
36
-
37
- impl ExtractorName for HeaderMap {
38
- const NAME: &str = "headers";
39
- }
40
-
41
- impl TryConvert for Extractor<HeaderMap> {
42
- fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
43
- let ruby = Ruby::get_with(value);
44
- let keyword = RHash::try_convert(value)?;
45
- let mut headers = HeaderMap::new();
46
-
47
- if let Some(hash) = keyword
48
- .get(ruby.to_symbol(HeaderMap::NAME))
49
- .and_then(RHash::from_value)
50
- {
51
- hash.foreach(|name: RString, value: RString| {
52
- let name = HeaderName::from_bytes(&name.to_bytes())
53
- .map_err(header_name_error_to_magnus)?;
54
- let value = HeaderValue::from_maybe_shared(value.to_bytes())
55
- .map_err(header_value_error_to_magnus)?;
56
- headers.insert(name, value);
57
-
58
- Ok(ForEach::Continue)
59
- })?;
60
-
61
- return Ok(Extractor(Some(headers)));
62
- }
63
-
64
- Ok(Extractor(None))
65
- }
66
- }
67
-
68
- // ===== impl Extractor<OrigHeaderMap> =====
69
-
70
- impl ExtractorName for OrigHeaderMap {
71
- const NAME: &str = "orig_headers";
72
- }
73
-
74
- impl TryConvert for Extractor<OrigHeaderMap> {
75
- fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
76
- let ruby = Ruby::get_with(value);
77
- let keyword = RHash::try_convert(value)?;
78
-
79
- if let Some(orig_headers) = keyword
80
- .get(ruby.to_symbol(OrigHeaderMap::NAME))
81
- .and_then(RArray::from_value)
82
- {
83
- let mut map = OrigHeaderMap::new();
84
- for value in orig_headers.into_iter().flat_map(RString::from_value) {
85
- map.insert(value.to_bytes());
86
- }
87
- return Ok(Extractor(Some(map)));
88
- }
89
-
90
- Ok(Extractor(None))
91
- }
92
- }
93
-
94
31
  // ===== impl Extractor<Proxy> =====
95
32
 
96
33
  impl ExtractorName for Proxy {
@@ -102,16 +39,19 @@ impl TryConvert for Extractor<Proxy> {
102
39
  let ruby = Ruby::get_with(value);
103
40
  let rhash = RHash::try_convert(value)?;
104
41
 
105
- if let Some(proxy) = rhash
106
- .get(ruby.to_symbol(Proxy::NAME))
107
- .and_then(RString::from_value)
108
- {
109
- return Proxy::all(proxy.to_bytes().as_ref())
110
- .map(Some)
111
- .map(Extractor)
112
- .map_err(wreq_error_to_magnus);
42
+ let Some(value) = options::get(&ruby, rhash, Proxy::NAME) else {
43
+ return Ok(Extractor(None));
44
+ };
45
+ if value.is_nil() {
46
+ return Ok(Extractor(None));
113
47
  }
114
48
 
115
- Ok(Extractor(None))
49
+ let proxy =
50
+ RString::try_convert(value).map_err(|error| option_value_error(Proxy::NAME, error))?;
51
+ let proxy = Proxy::all(proxy.to_bytes().as_ref())
52
+ .map_err(|err| wreq_error(&ruby, err))
53
+ .map_err(|error| option_value_error(Proxy::NAME, error))?;
54
+
55
+ Ok(Extractor(Some(proxy)))
116
56
  }
117
57
  }