wreq 1.2.4 → 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.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +22 -19
  3. data/Cargo.toml +15 -2
  4. data/crates/wreq-util/.github/FUNDING.yml +15 -0
  5. data/crates/wreq-util/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  6. data/crates/wreq-util/.github/ISSUE_TEMPLATE/custom.md +10 -0
  7. data/crates/wreq-util/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  8. data/crates/wreq-util/.github/dependabot.yml +22 -0
  9. data/crates/wreq-util/.github/workflows/ci.yml +82 -0
  10. data/crates/wreq-util/.github/workflows/release-plz.yml +52 -0
  11. data/crates/wreq-util/.gitignore +24 -0
  12. data/crates/wreq-util/CHANGELOG.md +74 -0
  13. data/crates/wreq-util/Cargo.toml +94 -0
  14. data/crates/wreq-util/LICENSE +201 -0
  15. data/crates/wreq-util/README.md +62 -0
  16. data/crates/wreq-util/examples/emulate.rs +39 -0
  17. data/crates/wreq-util/examples/tower_delay.rs +191 -0
  18. data/crates/wreq-util/release-plz.toml +2 -0
  19. data/crates/wreq-util/rustfmt.toml +5 -0
  20. data/crates/wreq-util/src/emulate/compress.rs +85 -0
  21. data/crates/wreq-util/src/emulate/macros.rs +372 -0
  22. data/crates/wreq-util/src/emulate/profile/chrome/header.rs +74 -0
  23. data/crates/wreq-util/src/emulate/profile/chrome/http2.rs +65 -0
  24. data/crates/wreq-util/src/emulate/profile/chrome/tls.rs +153 -0
  25. data/crates/wreq-util/src/emulate/profile/chrome.rs +2028 -0
  26. data/crates/wreq-util/src/emulate/profile/firefox/header.rs +37 -0
  27. data/crates/wreq-util/src/emulate/profile/firefox/http2.rs +113 -0
  28. data/crates/wreq-util/src/emulate/profile/firefox/tls.rs +253 -0
  29. data/crates/wreq-util/src/emulate/profile/firefox.rs +518 -0
  30. data/crates/wreq-util/src/emulate/profile/okhttp.rs +241 -0
  31. data/crates/wreq-util/src/emulate/profile/opera/header.rs +29 -0
  32. data/crates/wreq-util/src/emulate/profile/opera/http2.rs +50 -0
  33. data/crates/wreq-util/src/emulate/profile/opera/tls.rs +97 -0
  34. data/crates/wreq-util/src/emulate/profile/opera.rs +299 -0
  35. data/crates/wreq-util/src/emulate/profile/safari/header.rs +59 -0
  36. data/crates/wreq-util/src/emulate/profile/safari/http2.rs +116 -0
  37. data/crates/wreq-util/src/emulate/profile/safari/tls.rs +177 -0
  38. data/crates/wreq-util/src/emulate/profile/safari.rs +222 -0
  39. data/crates/wreq-util/src/emulate/profile.rs +47 -0
  40. data/crates/wreq-util/src/emulate.rs +369 -0
  41. data/crates/wreq-util/src/lib.rs +14 -0
  42. data/crates/wreq-util/src/rand.rs +24 -0
  43. data/crates/wreq-util/src/tower/delay/future.rs +43 -0
  44. data/crates/wreq-util/src/tower/delay/layer.rs +201 -0
  45. data/crates/wreq-util/src/tower/delay/service.rs +190 -0
  46. data/crates/wreq-util/src/tower/delay.rs +98 -0
  47. data/crates/wreq-util/src/tower.rs +7 -0
  48. data/crates/wreq-util/tests/client.rs +153 -0
  49. data/crates/wreq-util/tests/emulate_chrome.rs +270 -0
  50. data/crates/wreq-util/tests/emulate_firefox.rs +92 -0
  51. data/crates/wreq-util/tests/emulate_okhttp.rs +46 -0
  52. data/crates/wreq-util/tests/emulate_opera.rs +113 -0
  53. data/crates/wreq-util/tests/emulate_safari.rs +151 -0
  54. data/crates/wreq-util/tests/support/mod.rs +55 -0
  55. data/crates/wreq-util/tests/support/server.rs +232 -0
  56. data/examples/emulate_request.rb +1 -1
  57. data/lib/wreq.rb +72 -45
  58. data/lib/wreq_ruby/body.rb +30 -9
  59. data/lib/wreq_ruby/client.rb +88 -55
  60. data/lib/wreq_ruby/cookie.rb +81 -21
  61. data/lib/wreq_ruby/emulate.rb +77 -7
  62. data/lib/wreq_ruby/error.rb +5 -7
  63. data/lib/wreq_ruby/header.rb +205 -114
  64. data/lib/wreq_ruby/http.rb +74 -0
  65. data/lib/wreq_ruby/response.rb +31 -3
  66. data/script/build_windows_gnu.ps1 +6 -0
  67. data/src/arch.rs +22 -0
  68. data/src/client/body/form.rs +2 -0
  69. data/src/client/body/json.rs +47 -14
  70. data/src/client/body/stream.rs +147 -43
  71. data/src/client/body.rs +11 -15
  72. data/src/client/param.rs +7 -7
  73. data/src/client/req.rs +87 -47
  74. data/src/client/resp.rs +23 -24
  75. data/src/client.rs +310 -230
  76. data/src/cookie.rs +280 -87
  77. data/src/emulate.rs +86 -50
  78. data/src/error.rs +198 -45
  79. data/src/extractor.rs +16 -76
  80. data/src/header.rs +318 -130
  81. data/src/http.rs +62 -33
  82. data/src/lib.rs +26 -20
  83. data/src/macros.rs +71 -46
  84. data/src/options.rs +284 -0
  85. data/src/rt.rs +22 -11
  86. data/src/serde/de/array_deserializer.rs +48 -0
  87. data/src/serde/de/array_enumerator.rs +59 -0
  88. data/src/serde/de/deserializer.rs +307 -0
  89. data/src/serde/de/enum_deserializer.rs +47 -0
  90. data/src/serde/de/hash_deserializer.rs +89 -0
  91. data/src/serde/de/number_deserializer.rs +51 -0
  92. data/src/serde/de/variant_deserializer.rs +101 -0
  93. data/src/serde/de.rs +33 -0
  94. data/src/serde/error.rs +146 -0
  95. data/src/serde/ser/enums.rs +14 -0
  96. data/src/serde/ser/map_serializer.rs +56 -0
  97. data/src/serde/ser/seq_serializer.rs +71 -0
  98. data/src/serde/ser/serializer.rs +194 -0
  99. data/src/serde/ser/struct_serializer.rs +106 -0
  100. data/src/serde/ser/struct_variant_serializer.rs +44 -0
  101. data/src/serde/ser/tuple_variant_serializer.rs +41 -0
  102. data/src/serde/ser.rs +18 -0
  103. data/src/serde/tests.rs +237 -0
  104. data/src/serde.rs +202 -0
  105. data/test/body_sender_test.rb +246 -0
  106. data/test/cookie_test.rb +211 -10
  107. data/test/header_test.rb +228 -192
  108. data/test/json_precision_test.rb +209 -0
  109. data/test/option_validation_test.rb +311 -0
  110. data/test/stream_test.rb +36 -0
  111. data/test/value_semantics_test.rb +238 -0
  112. metadata +77 -1
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,
@@ -50,6 +86,7 @@ define_ruby_enum!(
50
86
  Chrome147,
51
87
  Chrome148,
52
88
  Chrome149,
89
+ Chrome150,
53
90
 
54
91
  Edge101,
55
92
  Edge122,
@@ -149,17 +186,19 @@ define_ruby_enum!(
149
186
  Opera131,
150
187
  );
151
188
 
189
+ // Defines constant registration, `into_ffi`/`from_ffi`, and handlers for
190
+ // Ruby's `to_s`, `to_sym`, `==`, `eql?`, and `hash` methods.
152
191
  define_ruby_enum!(
153
192
  /// An emulation profile for OS.
154
- const,
155
193
  Platform,
156
194
  "Wreq::Platform",
157
195
  wreq_util::Platform,
158
- Windows,
159
- MacOS,
160
- Linux,
161
- Android,
162
- IOS,
196
+ symbols:
197
+ Windows => "windows",
198
+ MacOS => "macos",
199
+ Linux => "linux",
200
+ Android => "android",
201
+ IOS => "ios",
163
202
  );
164
203
 
165
204
  /// A struct to represent the `EmulationOption` class.
@@ -167,51 +206,41 @@ define_ruby_enum!(
167
206
  #[magnus::wrap(class = "Wreq::Emulation", free_immediately, size)]
168
207
  pub struct Emulation(pub wreq_util::Emulation);
169
208
 
170
- // ===== impl Profile =====
171
-
172
- impl Profile {
173
- pub fn to_s(&self) -> String {
174
- self.into_ffi().inspect()
175
- }
176
- }
177
-
178
- // ===== impl Platform =====
179
-
180
- impl Platform {
181
- pub fn to_s(&self) -> String {
182
- self.into_ffi().inspect()
183
- }
184
- }
185
-
186
209
  // ===== impl Emulation =====
187
210
 
188
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.
189
221
  fn new(ruby: &Ruby, args: &[Value]) -> Result<Self, Error> {
190
- let mut profile = None;
191
- let mut platform = None;
192
- let mut http2 = None;
193
- let mut headers = None;
194
-
195
- if let Some(hash) = args.first().and_then(|v| RHash::from_value(*v)) {
196
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(profile))) {
197
- profile = Some(Obj::<Profile>::try_convert(v)?);
198
- }
199
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(platform))) {
200
- platform = Some(Obj::<Platform>::try_convert(v)?);
201
- }
202
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(http2))) {
203
- http2 = Some(bool::try_convert(v)?);
204
- }
205
- if let Some(v) = hash.get(ruby.to_symbol(stringify!(headers))) {
206
- headers = Some(bool::try_convert(v)?);
207
- }
208
- }
222
+ let mut params = Options::from_args(ruby, args, "emulation")?
223
+ .map(Builder::from_options)
224
+ .transpose()?
225
+ .unwrap_or_default();
209
226
 
210
227
  let emulation = wreq_util::Emulation::builder()
211
- .profile(profile.map(|obj| obj.into_ffi()).unwrap_or_default())
212
- .platform(platform.map(|os| os.into_ffi()).unwrap_or_default())
213
- .http2(http2.unwrap_or(true))
214
- .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))
215
244
  .build();
216
245
 
217
246
  Ok(Self(emulation))
@@ -222,11 +251,18 @@ pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
222
251
  // Profile enum binding
223
252
  let profile = gem_module.define_class("Profile", ruby.class_object())?;
224
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))?;
225
257
  Profile::define_constants(profile)?;
226
258
 
227
259
  // Platform enum binding
228
260
  let platform = gem_module.define_class("Platform", ruby.class_object())?;
229
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))?;
230
266
  Platform::define_constants(platform)?;
231
267
 
232
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
  {
@@ -72,64 +84,128 @@ define_exception!(DECODING_ERROR, "DecodingError", exception_runtime_error);
72
84
  // Configuration and builder errors
73
85
  define_exception!(BUILDER_ERROR, "BuilderError", exception_runtime_error);
74
86
 
75
- // Thread interruption error
76
- define_exception!(INTERRUPT_ERROR, "InterruptError", exception_interrupt);
77
-
78
87
  /// Memory error constant
79
- pub fn memory_error() -> MagnusError {
80
- 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)
81
90
  }
82
91
 
83
- /// Thread interruption error (raised when Thread.kill cancels a request)
84
- pub fn interrupt_error() -> MagnusError {
85
- MagnusError::new(ruby!().get_inner(&INTERRUPT_ERROR), "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
+ )
86
103
  }
87
104
 
88
105
  /// LocalJumpError for methods that require a Ruby block.
89
- 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 {
117
+ MagnusError::new(
118
+ ruby.exception_io_error(),
119
+ format!("closed body sender: {err}"),
120
+ )
121
+ }
122
+
123
+ /// Map an immutable sender-state borrow failure to `Wreq::BodyError`.
124
+ pub fn body_sender_borrow_error(ruby: &Ruby, err: BorrowError) -> MagnusError {
90
125
  MagnusError::new(
91
- ruby!().exception_local_jump_error(),
92
- "no block given (yield)",
126
+ ruby.get_inner(&BODY_ERROR),
127
+ format!("body sender state is unavailable: {err}"),
93
128
  )
94
129
  }
95
130
 
96
- /// Map [`tokio::sync::mpsc::error::SendError`] to corresponding [`magnus::Error`]
97
- pub fn mpsc_send_error_to_magnus<T>(err: SendError<T>) -> MagnusError {
131
+ /// Map a mutable sender-state borrow failure to `Wreq::BodyError`.
132
+ pub fn body_sender_borrow_mut_error(ruby: &Ruby, err: BorrowMutError) -> MagnusError {
98
133
  MagnusError::new(
99
- ruby!().get_inner(&BODY_ERROR),
100
- format!("failed to send body chunk: {}", err),
134
+ ruby.get_inner(&BODY_ERROR),
135
+ format!("body sender state is unavailable: {err}"),
101
136
  )
102
137
  }
103
138
 
104
139
  /// Map [`wreq::header::InvalidHeaderName`] to corresponding [`magnus::Error`]
105
- 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 {
106
141
  MagnusError::new(
107
- ruby!().get_inner(&BUILDER_ERROR),
142
+ ruby.get_inner(&BUILDER_ERROR),
108
143
  format!("invalid header name: {err}"),
109
144
  )
110
145
  }
111
146
 
112
147
  /// Map [`wreq::header::InvalidHeaderValue`] to corresponding [`magnus::Error`]
113
- 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 {
114
149
  MagnusError::new(
115
- ruby!().get_inner(&BUILDER_ERROR),
150
+ ruby.get_inner(&BUILDER_ERROR),
116
151
  format!("invalid header value: {err}"),
117
152
  )
118
153
  }
119
154
 
120
- /// Map type/value errors to corresponding [`magnus::Error`]
121
- 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 {
122
162
  MagnusError::new(
123
- ruby!().get_inner(&BUILDER_ERROR),
124
- format!("type error: {err}"),
163
+ ruby.get_inner(&BUILDER_ERROR),
164
+ format!("JSON serialization error: {err}"),
125
165
  )
126
166
  }
127
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
+ }
128
204
  /// Map [`wreq::Error`] to corresponding [`magnus::Error`]
129
- pub fn wreq_error_to_magnus(err: wreq::Error) -> MagnusError {
205
+ pub fn wreq_error(ruby: &Ruby, err: wreq::Error) -> MagnusError {
130
206
  let error_msg = err.to_string();
131
207
  map_wreq_error!(
132
- ruby!(),
208
+ ruby,
133
209
  err,
134
210
  error_msg,
135
211
  is_builder => BUILDER_ERROR,
@@ -146,18 +222,95 @@ pub fn wreq_error_to_magnus(err: wreq::Error) -> MagnusError {
146
222
  )
147
223
  }
148
224
 
149
- pub fn include(ruby: &Ruby) {
150
- Lazy::force(&MEMORY, ruby);
151
- Lazy::force(&CONNECTION_ERROR, ruby);
152
- Lazy::force(&PROXY_CONNECTION_ERROR, ruby);
153
- Lazy::force(&CONNECTION_RESET_ERROR, ruby);
154
- Lazy::force(&TLS_ERROR, ruby);
155
- Lazy::force(&REQUEST_ERROR, ruby);
156
- Lazy::force(&STATUS_ERROR, ruby);
157
- Lazy::force(&REDIRECT_ERROR, ruby);
158
- Lazy::force(&TIMEOUT_ERROR, ruby);
159
- Lazy::force(&BODY_ERROR, ruby);
160
- Lazy::force(&DECODING_ERROR, ruby);
161
- Lazy::force(&BUILDER_ERROR, ruby);
162
- Lazy::force(&INTERRUPT_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(())
163
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
  }