wreq 1.2.3-x64-mingw-ucrt

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 (69) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +1687 -0
  3. data/Cargo.toml +54 -0
  4. data/Gemfile +18 -0
  5. data/LICENSE +201 -0
  6. data/README.md +153 -0
  7. data/Rakefile +90 -0
  8. data/build.rs +16 -0
  9. data/docs/windows-gnu-tokio-crash.md +70 -0
  10. data/examples/body.rb +42 -0
  11. data/examples/client.rb +33 -0
  12. data/examples/cookie.rb +24 -0
  13. data/examples/emulate_request.rb +37 -0
  14. data/examples/headers.rb +27 -0
  15. data/examples/proxy.rb +113 -0
  16. data/examples/send_stream.rb +85 -0
  17. data/examples/stream.rb +14 -0
  18. data/examples/thread_interrupt.rb +83 -0
  19. data/extconf.rb +7 -0
  20. data/lib/wreq.rb +303 -0
  21. data/lib/wreq_ruby/3.3/wreq_ruby.so +0 -0
  22. data/lib/wreq_ruby/3.4/wreq_ruby.so +0 -0
  23. data/lib/wreq_ruby/4.0/wreq_ruby.so +0 -0
  24. data/lib/wreq_ruby/body.rb +36 -0
  25. data/lib/wreq_ruby/client.rb +526 -0
  26. data/lib/wreq_ruby/cookie.rb +156 -0
  27. data/lib/wreq_ruby/emulate.rb +232 -0
  28. data/lib/wreq_ruby/error.rb +157 -0
  29. data/lib/wreq_ruby/header.rb +205 -0
  30. data/lib/wreq_ruby/http.rb +160 -0
  31. data/lib/wreq_ruby/response.rb +209 -0
  32. data/script/build_platform_gem.rb +34 -0
  33. data/script/build_windows_gnu.ps1 +257 -0
  34. data/src/arch.rs +33 -0
  35. data/src/client/body/form.rs +2 -0
  36. data/src/client/body/json.rs +16 -0
  37. data/src/client/body/stream.rs +146 -0
  38. data/src/client/body.rs +57 -0
  39. data/src/client/param.rs +19 -0
  40. data/src/client/query.rs +2 -0
  41. data/src/client/req.rs +274 -0
  42. data/src/client/resp.rs +248 -0
  43. data/src/client.rs +413 -0
  44. data/src/cookie.rs +312 -0
  45. data/src/emulate.rs +376 -0
  46. data/src/error.rs +163 -0
  47. data/src/extractor.rs +117 -0
  48. data/src/gvl.rs +154 -0
  49. data/src/header.rs +245 -0
  50. data/src/http.rs +142 -0
  51. data/src/lib.rs +98 -0
  52. data/src/macros.rs +123 -0
  53. data/src/rt.rs +46 -0
  54. data/test/client_cookie_test.rb +46 -0
  55. data/test/client_test.rb +136 -0
  56. data/test/cookie_test.rb +182 -0
  57. data/test/emulation_test.rb +21 -0
  58. data/test/error_handling_test.rb +92 -0
  59. data/test/header_test.rb +290 -0
  60. data/test/inspect_test.rb +125 -0
  61. data/test/module_methods_test.rb +75 -0
  62. data/test/orig_header_test.rb +115 -0
  63. data/test/request_parameters_test.rb +175 -0
  64. data/test/request_test.rb +244 -0
  65. data/test/response_test.rb +69 -0
  66. data/test/stream_test.rb +397 -0
  67. data/test/test_helper.rb +52 -0
  68. data/wreq.gemspec +68 -0
  69. metadata +123 -0
data/src/cookie.rs ADDED
@@ -0,0 +1,312 @@
1
+ use std::{fmt, sync::Arc, time::SystemTime};
2
+
3
+ use bytes::Bytes;
4
+ use cookie::{Cookie as RawCookie, Expiration, ParseError, time::Duration};
5
+ use magnus::{
6
+ Error, Module, Object, RHash, RModule, RString, Ruby, TryConvert, Value, function, method,
7
+ r_hash::ForEach, typed_data::Obj, value::ReprValue,
8
+ };
9
+ use wreq::header::{self, HeaderMap, HeaderValue};
10
+
11
+ use crate::{error::header_value_error_to_magnus, gvl};
12
+
13
+ define_ruby_enum!(
14
+ /// The Cookie SameSite attribute.
15
+ const,
16
+ SameSite,
17
+ "Wreq::SameSite",
18
+ cookie::SameSite,
19
+ Strict,
20
+ Lax,
21
+ None,
22
+ );
23
+
24
+ /// A single HTTP cookie.
25
+ #[derive(Clone)]
26
+ #[magnus::wrap(class = "Wreq::Cookie", free_immediately, size)]
27
+ pub struct Cookie(RawCookie<'static>);
28
+
29
+ /// A collection of HTTP cookies.
30
+ #[derive(Default)]
31
+ pub struct Cookies(pub Vec<HeaderValue>);
32
+
33
+ /// A good default `CookieStore` implementation.
34
+ ///
35
+ /// This is the implementation used when simply calling `cookie_store(true)`.
36
+ /// This type is exposed to allow creating one and filling it with some
37
+ /// existing cookies more easily, before creating a `Client`.
38
+ #[derive(Clone, Default)]
39
+ #[magnus::wrap(class = "Wreq::Jar", free_immediately, size)]
40
+ pub struct Jar(pub Arc<wreq::cookie::Jar>);
41
+
42
+ // ===== impl Cookie =====
43
+
44
+ impl Cookie {
45
+ /// Create a new [`Cookie`].
46
+ pub fn new(args: &[Value]) -> Result<Self, Error> {
47
+ let args =
48
+ magnus::scan_args::scan_args::<(String, String), (), (), (), magnus::RHash, ()>(args)?;
49
+ #[allow(clippy::type_complexity)]
50
+ let keywords: magnus::scan_args::KwArgs<
51
+ (),
52
+ (
53
+ Option<String>,
54
+ Option<String>,
55
+ Option<u64>,
56
+ Option<f64>,
57
+ Option<bool>,
58
+ Option<bool>,
59
+ Option<Obj<SameSite>>,
60
+ ),
61
+ (),
62
+ > = magnus::scan_args::get_kwargs(
63
+ args.keywords,
64
+ &[],
65
+ &[
66
+ "domain",
67
+ "path",
68
+ "max_age",
69
+ "expires",
70
+ "http_only",
71
+ "secure",
72
+ "same_site",
73
+ ],
74
+ )?;
75
+
76
+ let (name, value) = args.required;
77
+
78
+ let mut cookie = RawCookie::new(name, value);
79
+
80
+ if let Some(domain) = keywords.optional.0 {
81
+ cookie.set_domain(domain);
82
+ }
83
+
84
+ if let Some(path) = keywords.optional.1 {
85
+ cookie.set_path(path);
86
+ }
87
+
88
+ if let Some(max_age) = keywords.optional.2 {
89
+ cookie.set_max_age(Duration::seconds(max_age as i64));
90
+ }
91
+
92
+ if let Some(expires) = keywords.optional.3 {
93
+ let duration = std::time::Duration::from_secs_f64(expires);
94
+ if let Some(system_time) = SystemTime::UNIX_EPOCH.checked_add(duration) {
95
+ cookie.set_expires(Expiration::DateTime(system_time.into()));
96
+ }
97
+ }
98
+
99
+ cookie.set_http_only(keywords.optional.4);
100
+ cookie.set_secure(keywords.optional.5);
101
+
102
+ if let Some(same_site) = keywords.optional.6 {
103
+ cookie.set_same_site(same_site.into_ffi());
104
+ }
105
+
106
+ Ok(Self(cookie))
107
+ }
108
+
109
+ /// The name of the cookie.
110
+ #[inline]
111
+ pub fn name(&self) -> &str {
112
+ self.0.name()
113
+ }
114
+
115
+ /// The value of the cookie.
116
+ #[inline]
117
+ pub fn value(&self) -> &str {
118
+ self.0.value()
119
+ }
120
+
121
+ /// Returns true if the 'HttpOnly' directive is enabled.
122
+ #[inline]
123
+ pub fn http_only(&self) -> bool {
124
+ self.0.http_only().unwrap_or(false)
125
+ }
126
+
127
+ /// Returns true if the 'Secure' directive is enabled.
128
+ #[inline]
129
+ pub fn secure(&self) -> bool {
130
+ self.0.secure().unwrap_or(false)
131
+ }
132
+
133
+ /// Returns true if 'SameSite' directive is 'Lax'.
134
+ #[inline]
135
+ pub fn same_site_lax(&self) -> bool {
136
+ self.0.same_site() == Some(cookie::SameSite::Lax)
137
+ }
138
+
139
+ /// Returns true if 'SameSite' directive is 'Strict'.
140
+ #[inline]
141
+ pub fn same_site_strict(&self) -> bool {
142
+ self.0.same_site() == Some(cookie::SameSite::Strict)
143
+ }
144
+
145
+ /// Returns the path directive of the cookie, if set.
146
+ #[inline]
147
+ pub fn path(&self) -> Option<&str> {
148
+ self.0.path()
149
+ }
150
+
151
+ /// Returns the domain directive of the cookie, if set.
152
+ #[inline]
153
+ pub fn domain(&self) -> Option<&str> {
154
+ self.0.domain()
155
+ }
156
+
157
+ /// Get the Max-Age information.
158
+ #[inline]
159
+ pub fn max_age(&self) -> Option<i64> {
160
+ self.0.max_age().map(|d| d.whole_seconds())
161
+ }
162
+
163
+ /// The cookie expiration time.
164
+ #[inline]
165
+ pub fn expires(&self) -> Option<f64> {
166
+ match self.0.expires() {
167
+ Some(Expiration::DateTime(offset)) => {
168
+ let system_time = SystemTime::from(offset);
169
+ system_time
170
+ .duration_since(SystemTime::UNIX_EPOCH)
171
+ .ok()
172
+ .map(|d| d.as_secs_f64())
173
+ }
174
+ None | Some(Expiration::Session) => None,
175
+ }
176
+ }
177
+ }
178
+
179
+ impl Cookie {
180
+ /// Parse cookies from a `HeaderMap`.
181
+ pub fn extract_headers_cookies(headers: &HeaderMap) -> Vec<Cookie> {
182
+ headers
183
+ .get_all(header::SET_COOKIE)
184
+ .iter()
185
+ .map(Cookie::parse)
186
+ .flat_map(Result::ok)
187
+ .map(RawCookie::into_owned)
188
+ .map(Cookie)
189
+ .collect()
190
+ }
191
+
192
+ fn parse<'a>(value: &'a HeaderValue) -> Result<RawCookie<'a>, ParseError> {
193
+ std::str::from_utf8(value.as_bytes())
194
+ .map_err(cookie::ParseError::from)
195
+ .and_then(RawCookie::parse)
196
+ }
197
+ }
198
+
199
+ impl fmt::Display for Cookie {
200
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201
+ write!(f, "{}", self.0)
202
+ }
203
+ }
204
+
205
+ // ===== impl Cookies =====
206
+
207
+ impl TryConvert for Cookies {
208
+ fn try_convert(value: magnus::Value) -> Result<Self, magnus::Error> {
209
+ // try extract uncompressed cookies
210
+ if let Some(rhash) = RHash::from_value(value) {
211
+ let mut cookies = Vec::new();
212
+ rhash.foreach(|name: RString, value: RString| {
213
+ let cookie = format!("{name}={value}");
214
+ let header_value = HeaderValue::from_maybe_shared(Bytes::from(cookie))
215
+ .map_err(header_value_error_to_magnus)?;
216
+ cookies.push(header_value);
217
+ Ok(ForEach::Continue)
218
+ })?;
219
+
220
+ return Ok(Self(cookies));
221
+ }
222
+
223
+ // try extract compressed cookies
224
+ if let Some(cookies) = RString::from_value(value) {
225
+ return Ok(Self(vec![
226
+ HeaderValue::from_maybe_shared(cookies.to_bytes())
227
+ .map_err(header_value_error_to_magnus)?,
228
+ ]));
229
+ }
230
+
231
+ Ok(Self::default())
232
+ }
233
+ }
234
+
235
+ // ===== impl Jar =====
236
+
237
+ impl Jar {
238
+ /// Create a new [`Jar`] with an empty cookie store.
239
+ pub fn new() -> Self {
240
+ Jar(Arc::new(wreq::cookie::Jar::default()))
241
+ }
242
+
243
+ /// Get all cookies.
244
+ pub fn get_all(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
245
+ let cookies: Vec<Cookie> = rb_self
246
+ .0
247
+ .get_all()
248
+ .map(RawCookie::from)
249
+ .map(Cookie)
250
+ .collect();
251
+ let ary = ruby.ary_new_capa(cookies.len());
252
+ for cookie in cookies {
253
+ ary.push(cookie)?;
254
+ }
255
+ Ok(ary.as_value())
256
+ }
257
+
258
+ /// Add a cookie to this jar.
259
+ pub fn add(&self, cookie: Value, url: String) {
260
+ if let Ok(cookie) = Obj::<Cookie>::try_convert(cookie) {
261
+ gvl::nogvl(|| self.0.add(cookie.0.clone(), &url))
262
+ }
263
+
264
+ if let Ok(cookie_str) = String::try_convert(cookie) {
265
+ gvl::nogvl(|| self.0.add(cookie_str.as_ref(), &url))
266
+ }
267
+ }
268
+
269
+ /// Remove a cookie from this jar by name and URL.
270
+ pub fn remove(&self, name: String, url: String) {
271
+ gvl::nogvl(|| self.0.remove(name, &url))
272
+ }
273
+
274
+ /// Clear all cookies in this jar.
275
+ pub fn clear(&self) {
276
+ gvl::nogvl(|| self.0.clear())
277
+ }
278
+ }
279
+
280
+ pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
281
+ // SameSite enum
282
+ let same_site_class = gem_module.define_class("SameSite", ruby.class_object())?;
283
+ same_site_class.const_set("Strict", SameSite::Strict)?;
284
+ same_site_class.const_set("Lax", SameSite::Lax)?;
285
+ same_site_class.const_set("None", SameSite::None)?;
286
+
287
+ // Cookie class
288
+ let cookie_class = gem_module.define_class("Cookie", ruby.class_object())?;
289
+ cookie_class.define_singleton_method("new", function!(Cookie::new, -1))?;
290
+ cookie_class.define_method("name", method!(Cookie::name, 0))?;
291
+ cookie_class.define_method("value", method!(Cookie::value, 0))?;
292
+ cookie_class.define_method("http_only", method!(Cookie::http_only, 0))?;
293
+ cookie_class.define_method("http_only?", method!(Cookie::http_only, 0))?;
294
+ cookie_class.define_method("secure", method!(Cookie::secure, 0))?;
295
+ cookie_class.define_method("secure?", method!(Cookie::secure, 0))?;
296
+ cookie_class.define_method("same_site_lax?", method!(Cookie::same_site_lax, 0))?;
297
+ cookie_class.define_method("same_site_strict?", method!(Cookie::same_site_strict, 0))?;
298
+ cookie_class.define_method("path", method!(Cookie::path, 0))?;
299
+ cookie_class.define_method("domain", method!(Cookie::domain, 0))?;
300
+ cookie_class.define_method("max_age", method!(Cookie::max_age, 0))?;
301
+ cookie_class.define_method("expires", method!(Cookie::expires, 0))?;
302
+
303
+ // Jar class
304
+ let jar_class = gem_module.define_class("Jar", ruby.class_object())?;
305
+ jar_class.define_singleton_method("new", function!(Jar::new, 0))?;
306
+ jar_class.define_method("get_all", method!(Jar::get_all, 0))?;
307
+ jar_class.define_method("add", method!(Jar::add, 2))?;
308
+ jar_class.define_method("remove", method!(Jar::remove, 2))?;
309
+ jar_class.define_method("clear", method!(Jar::clear, 0))?;
310
+
311
+ Ok(())
312
+ }
data/src/emulate.rs ADDED
@@ -0,0 +1,376 @@
1
+ use magnus::{
2
+ Error, Module, Object, RHash, RModule, Ruby, TryConvert, Value, function, method,
3
+ typed_data::{Inspect, Obj},
4
+ };
5
+
6
+ define_ruby_enum!(
7
+ /// An emulation profile.
8
+ const,
9
+ Profile,
10
+ "Wreq::Profile",
11
+ wreq_util::Profile,
12
+ Chrome100,
13
+ Chrome101,
14
+ Chrome104,
15
+ Chrome105,
16
+ Chrome106,
17
+ Chrome107,
18
+ Chrome108,
19
+ Chrome109,
20
+ Chrome110,
21
+ Chrome114,
22
+ Chrome116,
23
+ Chrome117,
24
+ Chrome118,
25
+ Chrome119,
26
+ Chrome120,
27
+ Chrome123,
28
+ Chrome124,
29
+ Chrome126,
30
+ Chrome127,
31
+ Chrome128,
32
+ Chrome129,
33
+ Chrome130,
34
+ Chrome131,
35
+ Chrome132,
36
+ Chrome133,
37
+ Chrome134,
38
+ Chrome135,
39
+ Chrome136,
40
+ Chrome137,
41
+ Chrome138,
42
+ Chrome139,
43
+ Chrome140,
44
+ Chrome141,
45
+ Chrome142,
46
+ Chrome143,
47
+ Chrome144,
48
+ Chrome145,
49
+ Chrome146,
50
+ Chrome147,
51
+ Chrome148,
52
+ Chrome149,
53
+
54
+ Edge101,
55
+ Edge122,
56
+ Edge127,
57
+ Edge131,
58
+ Edge134,
59
+ Edge135,
60
+ Edge136,
61
+ Edge137,
62
+ Edge138,
63
+ Edge139,
64
+ Edge140,
65
+ Edge141,
66
+ Edge142,
67
+ Edge143,
68
+ Edge144,
69
+ Edge145,
70
+ Edge146,
71
+ Edge147,
72
+ Edge148,
73
+
74
+ Firefox109,
75
+ Firefox117,
76
+ Firefox128,
77
+ Firefox133,
78
+ Firefox135,
79
+ FirefoxPrivate135,
80
+ FirefoxAndroid135,
81
+ Firefox136,
82
+ FirefoxPrivate136,
83
+ Firefox139,
84
+ Firefox142,
85
+ Firefox143,
86
+ Firefox144,
87
+ Firefox145,
88
+ Firefox146,
89
+ Firefox147,
90
+ Firefox148,
91
+ Firefox149,
92
+ Firefox150,
93
+ Firefox151,
94
+
95
+ SafariIos17_2,
96
+ SafariIos17_4_1,
97
+ SafariIos16_5,
98
+ Safari15_3,
99
+ Safari15_5,
100
+ Safari15_6_1,
101
+ Safari16,
102
+ Safari16_5,
103
+ Safari17_0,
104
+ Safari17_2_1,
105
+ Safari17_4_1,
106
+ Safari17_5,
107
+ Safari17_6,
108
+ Safari18,
109
+ SafariIPad18,
110
+ Safari18_2,
111
+ Safari18_3,
112
+ Safari18_3_1,
113
+ SafariIos18_1_1,
114
+ Safari18_5,
115
+ Safari26,
116
+ Safari26_1,
117
+ Safari26_2,
118
+ Safari26_3,
119
+ Safari26_4,
120
+ SafariIos26,
121
+ SafariIos26_2,
122
+ SafariIPad26,
123
+ SafariIpad26_2,
124
+
125
+ OkHttp3_9,
126
+ OkHttp3_11,
127
+ OkHttp3_13,
128
+ OkHttp3_14,
129
+ OkHttp4_9,
130
+ OkHttp4_10,
131
+ OkHttp4_12,
132
+ OkHttp5,
133
+
134
+ Opera116,
135
+ Opera117,
136
+ Opera118,
137
+ Opera119,
138
+ Opera120,
139
+ Opera121,
140
+ Opera122,
141
+ Opera123,
142
+ Opera124,
143
+ Opera125,
144
+ Opera126,
145
+ Opera127,
146
+ Opera128,
147
+ Opera129,
148
+ Opera130,
149
+ Opera131,
150
+ );
151
+
152
+ define_ruby_enum!(
153
+ /// An emulation profile for OS.
154
+ const,
155
+ Platform,
156
+ "Wreq::Platform",
157
+ wreq_util::Platform,
158
+ Windows,
159
+ MacOS,
160
+ Linux,
161
+ Android,
162
+ IOS,
163
+ );
164
+
165
+ /// A struct to represent the `EmulationOption` class.
166
+ #[derive(Clone)]
167
+ #[magnus::wrap(class = "Wreq::Emulation", free_immediately, size)]
168
+ pub struct Emulation(pub wreq_util::Emulation);
169
+
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
+ // ===== impl Emulation =====
187
+
188
+ impl Emulation {
189
+ 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
+ }
209
+
210
+ 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))
215
+ .build();
216
+
217
+ Ok(Self(emulation))
218
+ }
219
+ }
220
+
221
+ pub fn include(ruby: &Ruby, gem_module: &RModule) -> Result<(), Error> {
222
+ // Profile enum binding
223
+ let profile = gem_module.define_class("Profile", ruby.class_object())?;
224
+ profile.define_method("to_s", method!(Profile::to_s, 0))?;
225
+ profile.const_set("Chrome100", Profile::Chrome100)?;
226
+ profile.const_set("Chrome101", Profile::Chrome101)?;
227
+ profile.const_set("Chrome104", Profile::Chrome104)?;
228
+ profile.const_set("Chrome105", Profile::Chrome105)?;
229
+ profile.const_set("Chrome106", Profile::Chrome106)?;
230
+ profile.const_set("Chrome107", Profile::Chrome107)?;
231
+ profile.const_set("Chrome108", Profile::Chrome108)?;
232
+ profile.const_set("Chrome109", Profile::Chrome109)?;
233
+ profile.const_set("Chrome110", Profile::Chrome110)?;
234
+ profile.const_set("Chrome114", Profile::Chrome114)?;
235
+ profile.const_set("Chrome116", Profile::Chrome116)?;
236
+ profile.const_set("Chrome117", Profile::Chrome117)?;
237
+ profile.const_set("Chrome118", Profile::Chrome118)?;
238
+ profile.const_set("Chrome119", Profile::Chrome119)?;
239
+ profile.const_set("Chrome120", Profile::Chrome120)?;
240
+ profile.const_set("Chrome123", Profile::Chrome123)?;
241
+ profile.const_set("Chrome124", Profile::Chrome124)?;
242
+ profile.const_set("Chrome126", Profile::Chrome126)?;
243
+ profile.const_set("Chrome127", Profile::Chrome127)?;
244
+ profile.const_set("Chrome128", Profile::Chrome128)?;
245
+ profile.const_set("Chrome129", Profile::Chrome129)?;
246
+ profile.const_set("Chrome130", Profile::Chrome130)?;
247
+ profile.const_set("Chrome131", Profile::Chrome131)?;
248
+ profile.const_set("Chrome132", Profile::Chrome132)?;
249
+ profile.const_set("Chrome133", Profile::Chrome133)?;
250
+ profile.const_set("Chrome134", Profile::Chrome134)?;
251
+ profile.const_set("Chrome135", Profile::Chrome135)?;
252
+ profile.const_set("Chrome136", Profile::Chrome136)?;
253
+ profile.const_set("Chrome137", Profile::Chrome137)?;
254
+ profile.const_set("Chrome138", Profile::Chrome138)?;
255
+ profile.const_set("Chrome139", Profile::Chrome139)?;
256
+ profile.const_set("Chrome140", Profile::Chrome140)?;
257
+ profile.const_set("Chrome141", Profile::Chrome141)?;
258
+ profile.const_set("Chrome142", Profile::Chrome142)?;
259
+ profile.const_set("Chrome143", Profile::Chrome143)?;
260
+ profile.const_set("Chrome144", Profile::Chrome144)?;
261
+ profile.const_set("Chrome145", Profile::Chrome145)?;
262
+ profile.const_set("Chrome146", Profile::Chrome146)?;
263
+ profile.const_set("Chrome147", Profile::Chrome147)?;
264
+ profile.const_set("Chrome148", Profile::Chrome148)?;
265
+
266
+ profile.const_set("Edge101", Profile::Edge101)?;
267
+ profile.const_set("Edge122", Profile::Edge122)?;
268
+ profile.const_set("Edge127", Profile::Edge127)?;
269
+ profile.const_set("Edge131", Profile::Edge131)?;
270
+ profile.const_set("Edge134", Profile::Edge134)?;
271
+ profile.const_set("Edge135", Profile::Edge135)?;
272
+ profile.const_set("Edge136", Profile::Edge136)?;
273
+ profile.const_set("Edge137", Profile::Edge137)?;
274
+ profile.const_set("Edge138", Profile::Edge138)?;
275
+ profile.const_set("Edge139", Profile::Edge139)?;
276
+ profile.const_set("Edge140", Profile::Edge140)?;
277
+ profile.const_set("Edge141", Profile::Edge141)?;
278
+ profile.const_set("Edge142", Profile::Edge142)?;
279
+ profile.const_set("Edge143", Profile::Edge143)?;
280
+ profile.const_set("Edge144", Profile::Edge144)?;
281
+ profile.const_set("Edge145", Profile::Edge145)?;
282
+ profile.const_set("Edge146", Profile::Edge146)?;
283
+ profile.const_set("Edge147", Profile::Edge147)?;
284
+ profile.const_set("Edge148", Profile::Edge148)?;
285
+
286
+ profile.const_set("Firefox109", Profile::Firefox109)?;
287
+ profile.const_set("Firefox117", Profile::Firefox117)?;
288
+ profile.const_set("Firefox128", Profile::Firefox128)?;
289
+ profile.const_set("Firefox133", Profile::Firefox133)?;
290
+ profile.const_set("Firefox135", Profile::Firefox135)?;
291
+ profile.const_set("FirefoxPrivate135", Profile::FirefoxPrivate135)?;
292
+ profile.const_set("FirefoxAndroid135", Profile::FirefoxAndroid135)?;
293
+ profile.const_set("Firefox136", Profile::Firefox136)?;
294
+ profile.const_set("FirefoxPrivate136", Profile::FirefoxPrivate136)?;
295
+ profile.const_set("Firefox139", Profile::Firefox139)?;
296
+ profile.const_set("Firefox142", Profile::Firefox142)?;
297
+ profile.const_set("Firefox143", Profile::Firefox143)?;
298
+ profile.const_set("Firefox144", Profile::Firefox144)?;
299
+ profile.const_set("Firefox145", Profile::Firefox145)?;
300
+ profile.const_set("Firefox146", Profile::Firefox146)?;
301
+ profile.const_set("Firefox147", Profile::Firefox147)?;
302
+ profile.const_set("Firefox148", Profile::Firefox148)?;
303
+ profile.const_set("Firefox149", Profile::Firefox149)?;
304
+ profile.const_set("Firefox150", Profile::Firefox150)?;
305
+ profile.const_set("Firefox151", Profile::Firefox151)?;
306
+
307
+ profile.const_set("SafariIos17_2", Profile::SafariIos17_2)?;
308
+ profile.const_set("SafariIos17_4_1", Profile::SafariIos17_4_1)?;
309
+ profile.const_set("SafariIos16_5", Profile::SafariIos16_5)?;
310
+ profile.const_set("Safari15_3", Profile::Safari15_3)?;
311
+ profile.const_set("Safari15_5", Profile::Safari15_5)?;
312
+ profile.const_set("Safari15_6_1", Profile::Safari15_6_1)?;
313
+ profile.const_set("Safari16", Profile::Safari16)?;
314
+ profile.const_set("Safari16_5", Profile::Safari16_5)?;
315
+ profile.const_set("Safari17_0", Profile::Safari17_0)?;
316
+ profile.const_set("Safari17_2_1", Profile::Safari17_2_1)?;
317
+ profile.const_set("Safari17_4_1", Profile::Safari17_4_1)?;
318
+ profile.const_set("Safari17_5", Profile::Safari17_5)?;
319
+ profile.const_set("Safari17_6", Profile::Safari17_6)?;
320
+ profile.const_set("Safari18", Profile::Safari18)?;
321
+ profile.const_set("SafariIPad18", Profile::SafariIPad18)?;
322
+ profile.const_set("Safari18_2", Profile::Safari18_2)?;
323
+ profile.const_set("Safari18_3", Profile::Safari18_3)?;
324
+ profile.const_set("Safari18_3_1", Profile::Safari18_3_1)?;
325
+ profile.const_set("SafariIos18_1_1", Profile::SafariIos18_1_1)?;
326
+ profile.const_set("Safari18_5", Profile::Safari18_5)?;
327
+ profile.const_set("Safari26", Profile::Safari26)?;
328
+ profile.const_set("Safari26_1", Profile::Safari26_1)?;
329
+ profile.const_set("Safari26_2", Profile::Safari26_2)?;
330
+ profile.const_set("Safari26_3", Profile::Safari26_3)?;
331
+ profile.const_set("Safari26_4", Profile::Safari26_4)?;
332
+ profile.const_set("SafariIos26", Profile::SafariIos26)?;
333
+ profile.const_set("SafariIos26_2", Profile::SafariIos26_2)?;
334
+ profile.const_set("SafariIPad26", Profile::SafariIPad26)?;
335
+ profile.const_set("SafariIpad26_2", Profile::SafariIpad26_2)?;
336
+
337
+ profile.const_set("OkHttp3_9", Profile::OkHttp3_9)?;
338
+ profile.const_set("OkHttp3_11", Profile::OkHttp3_11)?;
339
+ profile.const_set("OkHttp3_13", Profile::OkHttp3_13)?;
340
+ profile.const_set("OkHttp3_14", Profile::OkHttp3_14)?;
341
+ profile.const_set("OkHttp4_9", Profile::OkHttp4_9)?;
342
+ profile.const_set("OkHttp4_10", Profile::OkHttp4_10)?;
343
+ profile.const_set("OkHttp4_12", Profile::OkHttp4_12)?;
344
+ profile.const_set("OkHttp5", Profile::OkHttp5)?;
345
+
346
+ profile.const_set("Opera116", Profile::Opera116)?;
347
+ profile.const_set("Opera117", Profile::Opera117)?;
348
+ profile.const_set("Opera118", Profile::Opera118)?;
349
+ profile.const_set("Opera119", Profile::Opera119)?;
350
+ profile.const_set("Opera120", Profile::Opera120)?;
351
+ profile.const_set("Opera121", Profile::Opera121)?;
352
+ profile.const_set("Opera122", Profile::Opera122)?;
353
+ profile.const_set("Opera123", Profile::Opera123)?;
354
+ profile.const_set("Opera124", Profile::Opera124)?;
355
+ profile.const_set("Opera125", Profile::Opera125)?;
356
+ profile.const_set("Opera126", Profile::Opera126)?;
357
+ profile.const_set("Opera127", Profile::Opera127)?;
358
+ profile.const_set("Opera128", Profile::Opera128)?;
359
+ profile.const_set("Opera129", Profile::Opera129)?;
360
+ profile.const_set("Opera130", Profile::Opera130)?;
361
+ profile.const_set("Opera131", Profile::Opera131)?;
362
+
363
+ // Platform enum binding
364
+ let platform = gem_module.define_class("Platform", ruby.class_object())?;
365
+ platform.define_method("to_s", method!(Platform::to_s, 0))?;
366
+ platform.const_set("Windows", Platform::Windows)?;
367
+ platform.const_set("MacOS", Platform::MacOS)?;
368
+ platform.const_set("Linux", Platform::Linux)?;
369
+ platform.const_set("Android", Platform::Android)?;
370
+ platform.const_set("IOS", Platform::IOS)?;
371
+
372
+ // Emulation class binding
373
+ let emulation = gem_module.define_class("Emulation", ruby.class_object())?;
374
+ emulation.define_singleton_method("new", function!(Emulation::new, -1))?;
375
+ Ok(())
376
+ }