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/http.rs CHANGED
@@ -1,31 +1,38 @@
1
- use magnus::{Error, Module, RModule, Ruby, TryConvert, Value, method, typed_data::Inspect};
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
- HTTP_09,
10
- HTTP_10,
11
- HTTP_11,
12
- HTTP_2,
13
- HTTP_3,
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
- GET,
22
- HEAD,
23
- POST,
24
- PUT,
25
- DELETE,
26
- OPTIONS,
27
- TRACE,
28
- PATCH,
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::client::{Client, resp::Response};
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::request(&Client::default(), args)
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::get(&Client::default(), args)
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::post(&Client::default(), args)
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::put(&Client::default(), args)
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::delete(&Client::default(), args)
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::head(&Client::default(), args)
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::options(&Client::default(), args)
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::trace(&Client::default(), args)
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::patch(&Client::default(), args)
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
- (set_if_true_with, $builder:expr, $option:expr, $method:ident, $default:expr, $value:expr) => {
38
- if $option.unwrap_or($default) {
39
- $builder = $builder.$method($value);
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, $( ($variant, $variant) ),*);
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])* const, $enum_type:ident, $ruby_class:expr, $ffi_type:ty, $($variant:ident),* $(,)?) => {
50
- define_ruby_enum!($(#[$meta])* const, $enum_type, $ruby_class, $ffi_type, $( ($variant, $variant) ),*);
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, $(($rust_variant:ident, $ffi_variant:ident)),* $(,)?) => {
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
- $($rust_variant),*
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>::$rust_variant => <$ffi_type>::$ffi_variant,)*
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>::$ffi_variant => <$enum_type>::$rust_variant,)*
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!($rust_variant), <$enum_type>::$rust_variant)?;)*
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
- impl $enum_type {
97
- pub const fn into_ffi(self) -> $ffi_type {
98
- match self {
99
- $(<$enum_type>::$rust_variant => <$ffi_type>::$ffi_variant,)*
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
- #[allow(dead_code)]
104
- pub const fn from_ffi(ffi: $ffi_type) -> Self {
105
- #[allow(unreachable_patterns)]
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
- pub fn define_constants(class: magnus::RClass) -> Result<(), magnus::Error> {
113
- $(class.const_set(stringify!($rust_variant), <$enum_type>::$rust_variant)?;)*
114
- Ok(())
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
- macro_rules! ruby {
121
- () => {
122
- magnus::Ruby::get().expect("Failed to get Ruby VM instance")
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 request = crate::client::req::Request::new(&ruby!(), args.keywords)?;
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
  }