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/src/header.rs
CHANGED
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
//!
|
|
9
9
|
//! [RFC 9110 section 5.1]: https://www.rfc-editor.org/rfc/rfc9110.html#section-5.1
|
|
10
10
|
|
|
11
|
-
mod helper;
|
|
12
|
-
|
|
13
11
|
use std::cell::RefCell;
|
|
14
12
|
|
|
15
13
|
use bytes::Bytes;
|
|
@@ -17,11 +15,12 @@ use http::{HeaderMap, HeaderValue};
|
|
|
17
15
|
use magnus::{
|
|
18
16
|
Error, RArray, RModule, RString, Ruby, TryConvert, Value, function, method,
|
|
19
17
|
prelude::*,
|
|
18
|
+
scan_args::scan_args,
|
|
20
19
|
typed_data::{Inspect, Obj},
|
|
21
20
|
};
|
|
22
21
|
use wreq::header::OrigHeaderMap;
|
|
23
22
|
|
|
24
|
-
use crate::error::{
|
|
23
|
+
use crate::error::{header_type_error, header_value_error};
|
|
25
24
|
|
|
26
25
|
use self::helper::{
|
|
27
26
|
ensure_header_count, from_source, header_count_error, parse_header_name, parse_header_values,
|
|
@@ -45,10 +44,11 @@ pub struct OrigHeaders(pub OrigHeaderMap);
|
|
|
45
44
|
|
|
46
45
|
impl TryConvert for UserAgent {
|
|
47
46
|
fn try_convert(value: Value) -> Result<Self, Error> {
|
|
47
|
+
let ruby = Ruby::get_with(value);
|
|
48
48
|
let s = RString::try_convert(value)?;
|
|
49
49
|
HeaderValue::from_maybe_shared(s.to_bytes())
|
|
50
50
|
.map(Self)
|
|
51
|
-
.map_err(
|
|
51
|
+
.map_err(|err| header_value_error(&ruby, err))
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -78,11 +78,12 @@ impl Headers {
|
|
|
78
78
|
/// A String stores one occurrence, while an Array stores each String as a
|
|
79
79
|
/// separate occurrence. An empty Array removes the header.
|
|
80
80
|
pub fn set(&self, name: Value, value: Value) -> Result<(), Error> {
|
|
81
|
+
let ruby = Ruby::get_with(name);
|
|
81
82
|
let name = parse_header_name(name)?;
|
|
82
83
|
let values = parse_header_values(value)?;
|
|
83
84
|
let mut headers = self.0.borrow_mut();
|
|
84
85
|
let replaced = headers.get_all(&name).iter().count();
|
|
85
|
-
ensure_header_count(headers.len(), replaced, values.len())?;
|
|
86
|
+
ensure_header_count(&ruby, headers.len(), replaced, values.len())?;
|
|
86
87
|
|
|
87
88
|
let mut values = values.into_iter();
|
|
88
89
|
let Some(first) = values.next() else {
|
|
@@ -92,11 +93,11 @@ impl Headers {
|
|
|
92
93
|
|
|
93
94
|
headers
|
|
94
95
|
.try_insert(name.clone(), first)
|
|
95
|
-
.map_err(|_| header_count_error())?;
|
|
96
|
+
.map_err(|_| header_count_error(&ruby))?;
|
|
96
97
|
for value in values {
|
|
97
98
|
headers
|
|
98
99
|
.try_append(name.clone(), value)
|
|
99
|
-
.map_err(|_| header_count_error())?;
|
|
100
|
+
.map_err(|_| header_count_error(&ruby))?;
|
|
100
101
|
}
|
|
101
102
|
Ok(())
|
|
102
103
|
}
|
|
@@ -105,15 +106,16 @@ impl Headers {
|
|
|
105
106
|
///
|
|
106
107
|
/// Array elements are appended separately and are never comma-folded.
|
|
107
108
|
pub fn append(&self, name: Value, value: Value) -> Result<(), Error> {
|
|
109
|
+
let ruby = Ruby::get_with(name);
|
|
108
110
|
let name = parse_header_name(name)?;
|
|
109
111
|
let values = parse_header_values(value)?;
|
|
110
112
|
let mut headers = self.0.borrow_mut();
|
|
111
|
-
ensure_header_count(headers.len(), 0, values.len())?;
|
|
113
|
+
ensure_header_count(&ruby, headers.len(), 0, values.len())?;
|
|
112
114
|
|
|
113
115
|
for value in values {
|
|
114
116
|
headers
|
|
115
117
|
.try_append(name.clone(), value)
|
|
116
|
-
.map_err(|_| header_count_error())?;
|
|
118
|
+
.map_err(|_| header_count_error(&ruby))?;
|
|
117
119
|
}
|
|
118
120
|
Ok(())
|
|
119
121
|
}
|
|
@@ -170,18 +172,13 @@ impl Headers {
|
|
|
170
172
|
///
|
|
171
173
|
/// The optional source may be a Hash, another `Wreq::Headers`, or an
|
|
172
174
|
/// Enumerable whose elements are name-value pairs.
|
|
173
|
-
pub fn new(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
"wrong number of arguments (given {}, expected 0..1)",
|
|
181
|
-
args.len()
|
|
182
|
-
),
|
|
183
|
-
)),
|
|
184
|
-
}
|
|
175
|
+
pub fn new(args: &[Value]) -> Result<Self, Error> {
|
|
176
|
+
scan_args::<(), (Option<Value>,), (), (), (), ()>(args)?
|
|
177
|
+
.optional
|
|
178
|
+
.0
|
|
179
|
+
.map(from_source)
|
|
180
|
+
.transpose()
|
|
181
|
+
.map(Option::unwrap_or_default)
|
|
185
182
|
}
|
|
186
183
|
|
|
187
184
|
/// Return a value using Ruby collection semantics.
|
|
@@ -268,12 +265,15 @@ impl TryConvert for Headers {
|
|
|
268
265
|
|
|
269
266
|
impl TryConvert for OrigHeaders {
|
|
270
267
|
fn try_convert(value: Value) -> Result<Self, Error> {
|
|
268
|
+
let ruby = Ruby::get_with(value);
|
|
271
269
|
let mut map = OrigHeaderMap::new();
|
|
272
270
|
|
|
273
271
|
let rarray = RArray::from_value(value)
|
|
274
|
-
.ok_or_else(||
|
|
272
|
+
.ok_or_else(|| header_type_error(&ruby, "Expected an array of strings"))?;
|
|
275
273
|
|
|
276
|
-
for value in rarray
|
|
274
|
+
for value in rarray {
|
|
275
|
+
let value = RString::try_convert(value)
|
|
276
|
+
.map_err(|_| header_type_error(&ruby, "Expected an array of strings"))?;
|
|
277
277
|
map.insert(value.to_bytes());
|
|
278
278
|
}
|
|
279
279
|
|
|
@@ -281,6 +281,129 @@ impl TryConvert for OrigHeaders {
|
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
mod helper {
|
|
285
|
+
//! Ruby value conversion helpers for `Wreq::Headers`.
|
|
286
|
+
|
|
287
|
+
use bytes::Bytes;
|
|
288
|
+
use http::{HeaderName, HeaderValue};
|
|
289
|
+
use magnus::{
|
|
290
|
+
Error, RArray, RString, Ruby, Symbol, TryConvert, Value, prelude::*, typed_data::Obj,
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
use crate::error::{header_name_error, header_type_error, header_value_error};
|
|
294
|
+
|
|
295
|
+
use super::Headers;
|
|
296
|
+
|
|
297
|
+
/// Maximum number of field-value occurrences supported by `HeaderMap`.
|
|
298
|
+
const MAX_HEADER_ENTRIES: usize = 1 << 15;
|
|
299
|
+
|
|
300
|
+
/// Build a header collection from a Ruby source object.
|
|
301
|
+
///
|
|
302
|
+
/// Accepts another `Wreq::Headers`, a Hash, or any object whose `to_a` result
|
|
303
|
+
/// contains two-element name-value pairs. Array values are delegated to
|
|
304
|
+
/// [`Headers::append`] so each value remains a separate occurrence.
|
|
305
|
+
pub(super) fn from_source(source: Value) -> Result<Headers, Error> {
|
|
306
|
+
let ruby = Ruby::get_with(source);
|
|
307
|
+
if let Ok(headers) = Obj::<Headers>::try_convert(source) {
|
|
308
|
+
return Ok((*headers).clone());
|
|
309
|
+
}
|
|
310
|
+
if !source.respond_to("to_a", false)? {
|
|
311
|
+
return Err(header_type_error(
|
|
312
|
+
&ruby,
|
|
313
|
+
"Expected Headers, a Hash, or an enumerable of pairs",
|
|
314
|
+
));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
let pairs: RArray = source.funcall_public("to_a", ())?;
|
|
318
|
+
let headers = Headers::default();
|
|
319
|
+
for pair in pairs {
|
|
320
|
+
let pair = RArray::try_convert(pair)
|
|
321
|
+
.map_err(|_| header_type_error(&ruby, "Expected each header entry to be a pair"))?;
|
|
322
|
+
if pair.len() != 2 {
|
|
323
|
+
return Err(header_type_error(
|
|
324
|
+
&ruby,
|
|
325
|
+
"Expected each header entry to contain a name and value",
|
|
326
|
+
));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
headers.append(pair.entry(0)?, pair.entry(1)?)?;
|
|
330
|
+
}
|
|
331
|
+
Ok(headers)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/// Convert a Ruby String or Symbol into a normalized HTTP header name.
|
|
335
|
+
///
|
|
336
|
+
/// Symbol underscores are changed to hyphens before [`HeaderName`] validates
|
|
337
|
+
/// and normalizes the name. Other Ruby types produce `Wreq::BuilderError`.
|
|
338
|
+
pub(super) fn parse_header_name(value: Value) -> Result<HeaderName, Error> {
|
|
339
|
+
let ruby = Ruby::get_with(value);
|
|
340
|
+
let name = match (RString::from_value(value), Symbol::from_value(value)) {
|
|
341
|
+
(Some(name), _) => name.to_bytes(),
|
|
342
|
+
(None, Some(name)) => Bytes::from(name.name()?.replace('_', "-")),
|
|
343
|
+
(None, None) => {
|
|
344
|
+
return Err(header_type_error(
|
|
345
|
+
&ruby,
|
|
346
|
+
"Expected a String or Symbol header name",
|
|
347
|
+
));
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
HeaderName::from_bytes(name.as_ref()).map_err(|err| header_name_error(&ruby, err))
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/// Convert a Ruby String or Array of Strings into validated header values.
|
|
354
|
+
///
|
|
355
|
+
/// Each Array element becomes one [`HeaderValue`]. An empty Array therefore
|
|
356
|
+
/// produces no values, allowing `set` to remove a header and `append` to do
|
|
357
|
+
/// nothing.
|
|
358
|
+
pub(super) fn parse_header_values(value: Value) -> Result<Vec<HeaderValue>, Error> {
|
|
359
|
+
if let Some(values) = RArray::from_value(value) {
|
|
360
|
+
values.into_iter().map(parse_header_value).collect()
|
|
361
|
+
} else {
|
|
362
|
+
Ok(vec![parse_header_value(value)?])
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/// Convert one Ruby String into a validated HTTP header value.
|
|
367
|
+
///
|
|
368
|
+
/// Invalid Ruby types and bytes rejected by [`HeaderValue`] are mapped to
|
|
369
|
+
/// `Wreq::BuilderError`.
|
|
370
|
+
fn parse_header_value(value: Value) -> Result<HeaderValue, Error> {
|
|
371
|
+
let ruby = Ruby::get_with(value);
|
|
372
|
+
let value = RString::try_convert(value)
|
|
373
|
+
.map_err(|_| header_type_error(&ruby, "Expected a String header value"))?;
|
|
374
|
+
HeaderValue::from_maybe_shared(value.to_bytes())
|
|
375
|
+
.map_err(|err| header_value_error(&ruby, err))
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/// Validate the resulting number of header occurrences before a mutation.
|
|
379
|
+
///
|
|
380
|
+
/// `current` is the collection length, `replaced` is the number of existing
|
|
381
|
+
/// occurrences removed by `set`, and `added` is the incoming value count.
|
|
382
|
+
/// Checked arithmetic prevents overflow; an invalid calculation or a result
|
|
383
|
+
/// above the native [`HeaderMap`](http::HeaderMap) limit returns
|
|
384
|
+
/// `Wreq::BuilderError` without mutating the collection.
|
|
385
|
+
pub(super) fn ensure_header_count(
|
|
386
|
+
ruby: &Ruby,
|
|
387
|
+
current: usize,
|
|
388
|
+
replaced: usize,
|
|
389
|
+
added: usize,
|
|
390
|
+
) -> Result<(), Error> {
|
|
391
|
+
let count = current
|
|
392
|
+
.checked_sub(replaced)
|
|
393
|
+
.and_then(|count| count.checked_add(added));
|
|
394
|
+
if count.is_some_and(|count| count <= MAX_HEADER_ENTRIES) {
|
|
395
|
+
Ok(())
|
|
396
|
+
} else {
|
|
397
|
+
Err(header_count_error(ruby))
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/// Build the error returned when the native header map reaches its entry limit.
|
|
402
|
+
pub(super) fn header_count_error(ruby: &Ruby) -> Error {
|
|
403
|
+
header_type_error(ruby, "Header collection exceeds 32,768 entries")
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
284
407
|
/// Register `Wreq::Headers` and its native methods with Ruby.
|
|
285
408
|
pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
|
|
286
409
|
let headers_class = gem_module.define_class("Headers", ruby.class_object())?;
|
data/src/http.rs
CHANGED
|
@@ -1,31 +1,38 @@
|
|
|
1
|
-
use magnus::{Error, Module, RModule, Ruby, TryConvert, Value, method
|
|
1
|
+
use magnus::{Error, Module, RModule, Ruby, TryConvert, Value, method};
|
|
2
2
|
|
|
3
|
+
// Defines constant registration, `into_ffi`/`from_ffi`, and handlers for
|
|
4
|
+
// Ruby's `to_s`, `==`, `eql?`, and `hash` methods.
|
|
5
|
+
// `wreq::Version` only exposes protocol strings through `Debug`, so static
|
|
6
|
+
// labels avoid allocating an intermediate `String`.
|
|
3
7
|
define_ruby_enum!(
|
|
4
8
|
/// An HTTP version.
|
|
5
|
-
const,
|
|
6
9
|
Version,
|
|
7
10
|
"Wreq::Version",
|
|
8
11
|
wreq::Version,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
strings:
|
|
13
|
+
HTTP_09 => "HTTP/0.9",
|
|
14
|
+
HTTP_10 => "HTTP/1.0",
|
|
15
|
+
HTTP_11 => "HTTP/1.1",
|
|
16
|
+
HTTP_2 => "HTTP/2.0",
|
|
17
|
+
HTTP_3 => "HTTP/3.0",
|
|
14
18
|
);
|
|
15
19
|
|
|
20
|
+
// Defines constant registration, `into_ffi`/`from_ffi`, and handlers for
|
|
21
|
+
// Ruby's `to_s`, `to_sym`, `==`, `eql?`, and `hash` methods.
|
|
16
22
|
define_ruby_enum!(
|
|
17
23
|
/// An HTTP method.
|
|
18
24
|
Method,
|
|
19
25
|
"Wreq::Method",
|
|
20
26
|
wreq::Method,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
symbols:
|
|
28
|
+
GET => "get",
|
|
29
|
+
HEAD => "head",
|
|
30
|
+
POST => "post",
|
|
31
|
+
PUT => "put",
|
|
32
|
+
DELETE => "delete",
|
|
33
|
+
OPTIONS => "options",
|
|
34
|
+
TRACE => "trace",
|
|
35
|
+
PATCH => "patch",
|
|
29
36
|
);
|
|
30
37
|
|
|
31
38
|
/// HTTP status code.
|
|
@@ -33,24 +40,6 @@ define_ruby_enum!(
|
|
|
33
40
|
#[magnus::wrap(class = "Wreq::StatusCode", free_immediately, size)]
|
|
34
41
|
pub struct StatusCode(pub wreq::StatusCode);
|
|
35
42
|
|
|
36
|
-
// ===== impl Version =====
|
|
37
|
-
|
|
38
|
-
impl Version {
|
|
39
|
-
/// Convert version to string.
|
|
40
|
-
#[inline]
|
|
41
|
-
pub fn to_s(&self) -> String {
|
|
42
|
-
self.into_ffi().inspect()
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/// Value-based equality for Ruby (`==`).
|
|
46
|
-
#[inline]
|
|
47
|
-
pub fn equals(&self, other: Value) -> bool {
|
|
48
|
-
<&Version>::try_convert(other)
|
|
49
|
-
.map(|other| *self == *other)
|
|
50
|
-
.unwrap_or(false)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
43
|
impl TryConvert for Version {
|
|
55
44
|
fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
|
|
56
45
|
<&Version>::try_convert(value).cloned()
|
|
@@ -101,6 +90,35 @@ impl StatusCode {
|
|
|
101
90
|
pub fn to_s(&self) -> String {
|
|
102
91
|
self.0.to_string()
|
|
103
92
|
}
|
|
93
|
+
|
|
94
|
+
/// Value-based equality for Ruby (`==`).
|
|
95
|
+
#[inline]
|
|
96
|
+
pub fn equals(&self, other: Value) -> bool {
|
|
97
|
+
<&StatusCode>::try_convert(other)
|
|
98
|
+
.map(|other| *self == *other)
|
|
99
|
+
.unwrap_or(false)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// Strict equality for Ruby (`eql?`).
|
|
103
|
+
#[inline]
|
|
104
|
+
pub fn is_eql(&self, other: Value) -> bool {
|
|
105
|
+
self.equals(other)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/// Hash value for Ruby (`hash`).
|
|
109
|
+
#[inline]
|
|
110
|
+
pub fn hash_value(&self) -> u64 {
|
|
111
|
+
use std::hash::{Hash, Hasher};
|
|
112
|
+
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
113
|
+
self.0.hash(&mut hasher);
|
|
114
|
+
hasher.finish()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// Return the status code as an integer (Ruby `to_i`).
|
|
118
|
+
#[inline]
|
|
119
|
+
pub const fn to_i(&self) -> u16 {
|
|
120
|
+
self.0.as_u16()
|
|
121
|
+
}
|
|
104
122
|
}
|
|
105
123
|
|
|
106
124
|
impl From<wreq::StatusCode> for StatusCode {
|
|
@@ -112,11 +130,18 @@ impl From<wreq::StatusCode> for StatusCode {
|
|
|
112
130
|
pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
|
|
113
131
|
let method_class = gem_module.define_class("Method", ruby.class_object())?;
|
|
114
132
|
Method::define_constants(method_class)?;
|
|
133
|
+
method_class.define_method("to_s", method!(Method::to_s, 0))?;
|
|
134
|
+
method_class.define_method("to_sym", method!(Method::to_sym, 0))?;
|
|
135
|
+
method_class.define_method("==", method!(Method::equals, 1))?;
|
|
136
|
+
method_class.define_method("eql?", method!(Method::is_eql, 1))?;
|
|
137
|
+
method_class.define_method("hash", method!(Method::hash_value, 0))?;
|
|
115
138
|
|
|
116
139
|
let version_class = gem_module.define_class("Version", ruby.class_object())?;
|
|
117
140
|
Version::define_constants(version_class)?;
|
|
118
141
|
version_class.define_method("to_s", method!(Version::to_s, 0))?;
|
|
119
142
|
version_class.define_method("==", method!(Version::equals, 1))?;
|
|
143
|
+
version_class.define_method("eql?", method!(Version::is_eql, 1))?;
|
|
144
|
+
version_class.define_method("hash", method!(Version::hash_value, 0))?;
|
|
120
145
|
|
|
121
146
|
let status_code_class = gem_module.define_class("StatusCode", ruby.class_object())?;
|
|
122
147
|
status_code_class.define_method("as_int", method!(StatusCode::as_int, 0))?;
|
|
@@ -126,6 +151,10 @@ pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
|
|
|
126
151
|
status_code_class.define_method("client_error?", method!(StatusCode::is_client_error, 0))?;
|
|
127
152
|
status_code_class.define_method("server_error?", method!(StatusCode::is_server_error, 0))?;
|
|
128
153
|
status_code_class.define_method("to_s", method!(StatusCode::to_s, 0))?;
|
|
154
|
+
status_code_class.define_method("==", method!(StatusCode::equals, 1))?;
|
|
155
|
+
status_code_class.define_method("eql?", method!(StatusCode::is_eql, 1))?;
|
|
156
|
+
status_code_class.define_method("hash", method!(StatusCode::hash_value, 0))?;
|
|
157
|
+
status_code_class.define_method("to_i", method!(StatusCode::to_i, 0))?;
|
|
129
158
|
|
|
130
159
|
Ok(())
|
|
131
160
|
}
|
data/src/lib.rs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#![deny(unsafe_code)]
|
|
1
2
|
#![allow(clippy::wrong_self_convention)]
|
|
2
3
|
|
|
3
4
|
#[macro_use]
|
|
@@ -11,67 +12,72 @@ mod extractor;
|
|
|
11
12
|
mod gvl;
|
|
12
13
|
mod header;
|
|
13
14
|
mod http;
|
|
15
|
+
mod options;
|
|
14
16
|
mod rt;
|
|
17
|
+
mod serde;
|
|
15
18
|
|
|
16
19
|
use magnus::{Error, Module, Ruby, Value};
|
|
17
20
|
|
|
18
|
-
use crate::
|
|
21
|
+
use crate::{
|
|
22
|
+
client::{Client, resp::Response},
|
|
23
|
+
http::Method,
|
|
24
|
+
};
|
|
19
25
|
|
|
20
26
|
const RUBY_MODULE_NAME: &str = "Wreq";
|
|
21
27
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
22
28
|
|
|
23
29
|
/// Send a HTTP request.
|
|
24
30
|
#[inline]
|
|
25
|
-
pub fn request(args: &[Value]) -> Result<Response, magnus::Error> {
|
|
26
|
-
Client::
|
|
31
|
+
pub fn request(ruby: &Ruby, args: &[Value]) -> Result<Response, magnus::Error> {
|
|
32
|
+
Client::request_with_default_client(ruby, args)
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
/// Send a GET request.
|
|
30
36
|
#[inline]
|
|
31
|
-
pub fn get(args: &[Value]) -> Result<Response, magnus::Error> {
|
|
32
|
-
Client::
|
|
37
|
+
pub fn get(ruby: &Ruby, args: &[Value]) -> Result<Response, magnus::Error> {
|
|
38
|
+
Client::execute_with_default_client(ruby, Method::GET, args)
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
/// Send a POST request.
|
|
36
42
|
#[inline]
|
|
37
|
-
pub fn post(args: &[Value]) -> Result<Response, magnus::Error> {
|
|
38
|
-
Client::
|
|
43
|
+
pub fn post(ruby: &Ruby, args: &[Value]) -> Result<Response, magnus::Error> {
|
|
44
|
+
Client::execute_with_default_client(ruby, Method::POST, args)
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
/// Send a PUT request.
|
|
42
48
|
#[inline]
|
|
43
|
-
pub fn put(args: &[Value]) -> Result<Response, magnus::Error> {
|
|
44
|
-
Client::
|
|
49
|
+
pub fn put(ruby: &Ruby, args: &[Value]) -> Result<Response, magnus::Error> {
|
|
50
|
+
Client::execute_with_default_client(ruby, Method::PUT, args)
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
/// Send a DELETE request.
|
|
48
54
|
#[inline]
|
|
49
|
-
pub fn delete(args: &[Value]) -> Result<Response, magnus::Error> {
|
|
50
|
-
Client::
|
|
55
|
+
pub fn delete(ruby: &Ruby, args: &[Value]) -> Result<Response, magnus::Error> {
|
|
56
|
+
Client::execute_with_default_client(ruby, Method::DELETE, args)
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
/// Send a HEAD request.
|
|
54
60
|
#[inline]
|
|
55
|
-
pub fn head(args: &[Value]) -> Result<Response, magnus::Error> {
|
|
56
|
-
Client::
|
|
61
|
+
pub fn head(ruby: &Ruby, args: &[Value]) -> Result<Response, magnus::Error> {
|
|
62
|
+
Client::execute_with_default_client(ruby, Method::HEAD, args)
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
/// Send an OPTIONS request.
|
|
60
66
|
#[inline]
|
|
61
|
-
pub fn options(args: &[Value]) -> Result<Response, magnus::Error> {
|
|
62
|
-
Client::
|
|
67
|
+
pub fn options(ruby: &Ruby, args: &[Value]) -> Result<Response, magnus::Error> {
|
|
68
|
+
Client::execute_with_default_client(ruby, Method::OPTIONS, args)
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
/// Send a TRACE request.
|
|
66
72
|
#[inline]
|
|
67
|
-
pub fn trace(args: &[Value]) -> Result<Response, magnus::Error> {
|
|
68
|
-
Client::
|
|
73
|
+
pub fn trace(ruby: &Ruby, args: &[Value]) -> Result<Response, magnus::Error> {
|
|
74
|
+
Client::execute_with_default_client(ruby, Method::TRACE, args)
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
/// Send a PATCH request.
|
|
72
78
|
#[inline]
|
|
73
|
-
pub fn patch(args: &[Value]) -> Result<Response, magnus::Error> {
|
|
74
|
-
Client::
|
|
79
|
+
pub fn patch(ruby: &Ruby, args: &[Value]) -> Result<Response, magnus::Error> {
|
|
80
|
+
Client::execute_with_default_client(ruby, Method::PATCH, args)
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
/// wreq ruby binding
|
|
@@ -93,6 +99,6 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
93
99
|
cookie::include(ruby, &gem_module)?;
|
|
94
100
|
client::include(ruby, &gem_module)?;
|
|
95
101
|
emulate::include(ruby, &gem_module)?;
|
|
96
|
-
error::include(ruby)
|
|
102
|
+
error::include(ruby, &gem_module)?;
|
|
97
103
|
Ok(())
|
|
98
104
|
}
|
data/src/macros.rs
CHANGED
|
@@ -34,92 +34,116 @@ macro_rules! apply_option {
|
|
|
34
34
|
$builder = $builder.$method();
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Convert a Ruby-native option whose field name is also its keyword name.
|
|
40
|
+
macro_rules! extract_native_option {
|
|
41
|
+
($options:expr, $target:expr, $field:ident) => {{
|
|
42
|
+
$target.$field.set($options.convert(stringify!($field))?);
|
|
43
|
+
}};
|
|
44
|
+
($options:expr, $target:expr, $field:ident, present) => {{
|
|
45
|
+
$target
|
|
46
|
+
.$field
|
|
47
|
+
.set($options.convert_present(stringify!($field))?);
|
|
48
|
+
}};
|
|
49
|
+
($options:expr, $target:expr, $field:ident, $source:ty => $map:expr) => {{
|
|
50
|
+
$target
|
|
51
|
+
.$field
|
|
52
|
+
.set($options.convert::<$source>(stringify!($field))?.map($map));
|
|
53
|
+
}};
|
|
42
54
|
}
|
|
43
55
|
|
|
44
56
|
macro_rules! define_ruby_enum {
|
|
45
|
-
($(#[$meta:meta])* $enum_type:ident, $ruby_class:expr, $ffi_type:ty, $($variant:ident),* $(,)?) => {
|
|
46
|
-
define_ruby_enum!($(#[$meta])* $enum_type, $ruby_class, $ffi_type, $(
|
|
57
|
+
($(#[$meta:meta])* $enum_type:ident, $ruby_class:expr, $ffi_type:ty, strings: $($variant:ident => $display:expr),* $(,)?) => {
|
|
58
|
+
define_ruby_enum!(@impl $(#[$meta])* $enum_type, $ruby_class, $ffi_type, [$(($variant, $display)),*], []);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
($(#[$meta:meta])* $enum_type:ident, $ruby_class:expr, $ffi_type:ty, symbols: $($variant:ident => $symbol:expr),* $(,)?) => {
|
|
62
|
+
define_ruby_enum!(@impl $(#[$meta])* $enum_type, $ruby_class, $ffi_type, [$(($variant, stringify!($variant))),*], [$($variant => $symbol),*]);
|
|
47
63
|
};
|
|
48
64
|
|
|
49
|
-
($(#[$meta:meta])*
|
|
50
|
-
define_ruby_enum!($(#[$meta])*
|
|
65
|
+
($(#[$meta:meta])* $enum_type:ident, $ruby_class:expr, $ffi_type:ty, $($variant:ident),* $(,)?) => {
|
|
66
|
+
define_ruby_enum!(@impl $(#[$meta])* $enum_type, $ruby_class, $ffi_type, [$(($variant, stringify!($variant))),*], []);
|
|
51
67
|
};
|
|
52
68
|
|
|
53
|
-
($(#[$meta:meta])* $enum_type:ident, $ruby_class:expr, $ffi_type:ty, $(($
|
|
69
|
+
(@impl $(#[$meta:meta])* $enum_type:ident, $ruby_class:expr, $ffi_type:ty, [$(($variant:ident, $display:expr)),*], [$($symbol_variant:ident => $symbol:expr),*]) => {
|
|
54
70
|
$(#[$meta])*
|
|
55
71
|
#[magnus::wrap(class = $ruby_class, free_immediately, size)]
|
|
56
72
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
57
73
|
#[allow(non_camel_case_types)]
|
|
58
74
|
#[allow(clippy::upper_case_acronyms)]
|
|
59
75
|
pub enum $enum_type {
|
|
60
|
-
$($
|
|
76
|
+
$($variant),*
|
|
61
77
|
}
|
|
62
78
|
|
|
63
79
|
impl $enum_type {
|
|
80
|
+
/// Return the static text exposed through Ruby's `to_s`.
|
|
81
|
+
#[inline]
|
|
82
|
+
pub const fn to_s(&self) -> &'static str {
|
|
83
|
+
match self {
|
|
84
|
+
$(<$enum_type>::$variant => $display,)*
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
define_ruby_enum!(@to_sym $enum_type, [$($symbol_variant => $symbol),*]);
|
|
89
|
+
|
|
90
|
+
/// Convert this Ruby wrapper into its native enum value.
|
|
64
91
|
pub fn into_ffi(self) -> $ffi_type {
|
|
65
92
|
match self {
|
|
66
|
-
$(<$enum_type>::$
|
|
93
|
+
$(<$enum_type>::$variant => <$ffi_type>::$variant,)*
|
|
67
94
|
}
|
|
68
95
|
}
|
|
69
96
|
|
|
97
|
+
/// Convert a known native enum value into its Ruby wrapper.
|
|
70
98
|
#[allow(dead_code)]
|
|
71
99
|
pub fn from_ffi(ffi: $ffi_type) -> Self {
|
|
72
100
|
#[allow(unreachable_patterns)]
|
|
73
101
|
match ffi {
|
|
74
|
-
$(<$ffi_type>::$
|
|
102
|
+
$(<$ffi_type>::$variant => <$enum_type>::$variant,)*
|
|
75
103
|
_ => unreachable!(),
|
|
76
104
|
}
|
|
77
105
|
}
|
|
78
106
|
|
|
107
|
+
/// Register every enum variant as a constant on the Ruby class.
|
|
79
108
|
pub fn define_constants(class: magnus::RClass) -> Result<(), magnus::Error> {
|
|
80
|
-
$(class.const_set(stringify!($
|
|
109
|
+
$(class.const_set(stringify!($variant), <$enum_type>::$variant)?;)*
|
|
81
110
|
Ok(())
|
|
82
111
|
}
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
($(#[$meta:meta])* const, $enum_type:ident, $ruby_class:expr, $ffi_type:ty, $(($rust_variant:ident, $ffi_variant:ident)),* $(,)?) => {
|
|
87
|
-
$(#[$meta])*
|
|
88
|
-
#[magnus::wrap(class = $ruby_class, free_immediately, size)]
|
|
89
|
-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
90
|
-
#[allow(non_camel_case_types)]
|
|
91
|
-
#[allow(clippy::upper_case_acronyms)]
|
|
92
|
-
pub enum $enum_type {
|
|
93
|
-
$($rust_variant),*
|
|
94
|
-
}
|
|
95
112
|
|
|
96
|
-
|
|
97
|
-
pub
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
113
|
+
/// Compare two wrapped enum values for Ruby's `==`.
|
|
114
|
+
pub fn equals(&self, other: magnus::Value) -> bool {
|
|
115
|
+
use magnus::TryConvert;
|
|
116
|
+
<&$enum_type>::try_convert(other)
|
|
117
|
+
.map(|other| *self == *other)
|
|
118
|
+
.unwrap_or(false)
|
|
101
119
|
}
|
|
102
120
|
|
|
103
|
-
|
|
104
|
-
pub
|
|
105
|
-
|
|
106
|
-
match ffi {
|
|
107
|
-
$(<$ffi_type>::$ffi_variant => <$enum_type>::$rust_variant,)*
|
|
108
|
-
_ => unreachable!(),
|
|
109
|
-
}
|
|
121
|
+
/// Compare two wrapped enum values for Ruby's `eql?`.
|
|
122
|
+
pub fn is_eql(&self, other: magnus::Value) -> bool {
|
|
123
|
+
self.equals(other)
|
|
110
124
|
}
|
|
111
125
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
126
|
+
/// Return a hash consistent with Ruby's `eql?` contract.
|
|
127
|
+
pub fn hash_value(&self) -> u64 {
|
|
128
|
+
use std::hash::{Hash, Hasher};
|
|
129
|
+
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
130
|
+
self.hash(&mut hasher);
|
|
131
|
+
hasher.finish()
|
|
115
132
|
}
|
|
116
133
|
}
|
|
117
134
|
};
|
|
118
|
-
}
|
|
119
135
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
136
|
+
(@to_sym $enum_type:ident, []) => {};
|
|
137
|
+
|
|
138
|
+
(@to_sym $enum_type:ident, [$($variant:ident => $symbol:expr),+]) => {
|
|
139
|
+
/// Return the configured Ruby symbol for this enum value.
|
|
140
|
+
#[inline]
|
|
141
|
+
pub fn to_sym(ruby: &magnus::Ruby, rb_self: &Self) -> magnus::Symbol {
|
|
142
|
+
let name = match *rb_self {
|
|
143
|
+
$(<$enum_type>::$variant => $symbol,)+
|
|
144
|
+
};
|
|
145
|
+
ruby.to_symbol(name)
|
|
146
|
+
}
|
|
123
147
|
};
|
|
124
148
|
}
|
|
125
149
|
|
|
@@ -127,7 +151,8 @@ macro_rules! extract_request {
|
|
|
127
151
|
($args:expr, $required:ty) => {{
|
|
128
152
|
let args = magnus::scan_args::scan_args::<$required, (), (), (), magnus::RHash, ()>($args)?;
|
|
129
153
|
let required = args.required;
|
|
130
|
-
let
|
|
154
|
+
let ruby = magnus::Ruby::get_with(args.keywords);
|
|
155
|
+
let request = crate::client::req::Request::new(&ruby, args.keywords)?;
|
|
131
156
|
(required, request)
|
|
132
157
|
}};
|
|
133
158
|
}
|