jsonschema_rs 0.42.0

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.
data/src/ser.rs ADDED
@@ -0,0 +1,724 @@
1
+ //! Serialization between Ruby values and `serde_json::Value`.
2
+ use magnus::{
3
+ gc::register_mark_object,
4
+ prelude::*,
5
+ rb_sys::AsRawValue,
6
+ value::{Lazy, ReprValue},
7
+ Error, Integer, RArray, RClass, RHash, RString, Ruby, Symbol, TryConvert, Value,
8
+ };
9
+ use rb_sys::{ruby_value_type, RB_TYPE};
10
+ use serde_json::{Map, Number, Value as JsonValue};
11
+ use std::fmt;
12
+
13
+ static BIG_DECIMAL_CLASS: Lazy<RClass> = Lazy::new(|ruby| {
14
+ // Ensure bigdecimal is loaded
15
+ let _: Value = ruby
16
+ .eval("require 'bigdecimal'")
17
+ .expect("Failed to require bigdecimal");
18
+ let cls: RClass = ruby
19
+ .eval("BigDecimal")
20
+ .expect("BigDecimal class must exist");
21
+ register_mark_object(cls);
22
+ cls
23
+ });
24
+
25
+ const RECURSION_LIMIT: u16 = 255;
26
+
27
+ #[inline]
28
+ pub fn to_value(ruby: &Ruby, value: Value) -> Result<JsonValue, Error> {
29
+ to_value_recursive(ruby, value, 0)
30
+ }
31
+
32
+ /// Convert a Ruby value in schema position to a `serde_json::Value`.
33
+ ///
34
+ /// If the value is a String, attempt to parse it as JSON first.
35
+ /// This allows passing JSON strings as schemas (e.g. `'{"type":"integer"}'`).
36
+ /// If parsing fails, falls back to treating it as a plain string value.
37
+ #[inline]
38
+ pub fn to_schema_value(ruby: &Ruby, value: Value) -> Result<JsonValue, Error> {
39
+ // SAFETY: We're reading the type tag of a valid Ruby value
40
+ #[allow(unsafe_code)]
41
+ let value_type = unsafe { RB_TYPE(value.as_raw()) };
42
+ if value_type == ruby_value_type::RUBY_T_STRING {
43
+ if let Some(rstring) = RString::from_value(value) {
44
+ // SAFETY: rstring is valid and we're in Ruby VM context
45
+ #[allow(unsafe_code)]
46
+ let bytes = unsafe { rstring.as_slice() };
47
+ if let Ok(parsed) = serde_json::from_slice(bytes) {
48
+ return Ok(parsed);
49
+ }
50
+ }
51
+ }
52
+ to_value_typed(ruby, value, value_type, 0)
53
+ }
54
+
55
+ fn to_value_recursive(ruby: &Ruby, value: Value, depth: u16) -> Result<JsonValue, Error> {
56
+ if value.is_nil() {
57
+ return Ok(JsonValue::Null);
58
+ }
59
+
60
+ // SAFETY: We're reading the type tag of a valid Ruby value
61
+ #[allow(unsafe_code)]
62
+ let value_type = unsafe { RB_TYPE(value.as_raw()) };
63
+
64
+ to_value_typed(ruby, value, value_type, depth)
65
+ }
66
+
67
+ fn to_value_typed(
68
+ ruby: &Ruby,
69
+ value: Value,
70
+ value_type: ruby_value_type,
71
+ depth: u16,
72
+ ) -> Result<JsonValue, Error> {
73
+ match value_type {
74
+ ruby_value_type::RUBY_T_TRUE => Ok(JsonValue::Bool(true)),
75
+ ruby_value_type::RUBY_T_FALSE => Ok(JsonValue::Bool(false)),
76
+ ruby_value_type::RUBY_T_FIXNUM | ruby_value_type::RUBY_T_BIGNUM => {
77
+ convert_integer(ruby, value)
78
+ }
79
+ ruby_value_type::RUBY_T_FLOAT => {
80
+ let f = f64::try_convert(value)?;
81
+ Number::from_f64(f).map(JsonValue::Number).ok_or_else(|| {
82
+ Error::new(
83
+ ruby.exception_arg_error(),
84
+ "Cannot convert NaN or Infinity to JSON",
85
+ )
86
+ })
87
+ }
88
+ ruby_value_type::RUBY_T_STRING => {
89
+ let Some(rstring) = RString::from_value(value) else {
90
+ unreachable!("We checked the type tag")
91
+ };
92
+ // SAFETY: rstring is valid and we're in Ruby VM context
93
+ #[allow(unsafe_code)]
94
+ let bytes = unsafe { rstring.as_slice() };
95
+ match std::str::from_utf8(bytes) {
96
+ Ok(s) => Ok(JsonValue::String(s.to_owned())),
97
+ Err(_) => Err(Error::new(
98
+ ruby.exception_encoding_error(),
99
+ "String is not valid UTF-8",
100
+ )),
101
+ }
102
+ }
103
+ ruby_value_type::RUBY_T_SYMBOL => {
104
+ let Some(sym) = Symbol::from_value(value) else {
105
+ unreachable!("We checked the type tag")
106
+ };
107
+ let name = sym.name()?;
108
+ Ok(JsonValue::String(name.to_string()))
109
+ }
110
+ ruby_value_type::RUBY_T_ARRAY => {
111
+ if depth >= RECURSION_LIMIT {
112
+ return Err(Error::new(
113
+ ruby.exception_arg_error(),
114
+ format!("Exceeded maximum nesting depth ({RECURSION_LIMIT})"),
115
+ ));
116
+ }
117
+ let Some(arr) = RArray::from_value(value) else {
118
+ unreachable!("We checked the type tag")
119
+ };
120
+ let len = arr.len();
121
+ let mut json_arr = Vec::with_capacity(len);
122
+ // Do not use `RArray::as_slice` here: recursive conversion may call
123
+ // Ruby APIs for nested values, and `as_slice` borrows Ruby-managed
124
+ // memory that must not be held across Ruby calls/GC.
125
+ for idx in 0..len {
126
+ let idx = isize::try_from(idx).map_err(|_| {
127
+ Error::new(
128
+ ruby.exception_arg_error(),
129
+ "Array index exceeds supported range",
130
+ )
131
+ })?;
132
+ let item: Value = arr.entry(idx)?;
133
+ json_arr.push(to_value_recursive(ruby, item, depth + 1)?);
134
+ }
135
+ Ok(JsonValue::Array(json_arr))
136
+ }
137
+ ruby_value_type::RUBY_T_HASH => {
138
+ if depth >= RECURSION_LIMIT {
139
+ return Err(Error::new(
140
+ ruby.exception_arg_error(),
141
+ format!("Exceeded maximum nesting depth ({RECURSION_LIMIT})"),
142
+ ));
143
+ }
144
+ let Some(hash) = RHash::from_value(value) else {
145
+ unreachable!("We checked the type tag")
146
+ };
147
+ let mut map = Map::with_capacity(hash.len());
148
+ hash.foreach(|key: Value, val: Value| {
149
+ let key_str = hash_key_to_string(ruby, key)?;
150
+ let json_val = to_value_recursive(ruby, val, depth + 1)?;
151
+ map.insert(key_str, json_val);
152
+ Ok(magnus::r_hash::ForEach::Continue)
153
+ })?;
154
+ Ok(JsonValue::Object(map))
155
+ }
156
+ ruby_value_type::RUBY_T_DATA if value.is_kind_of(ruby.get_inner(&BIG_DECIMAL_CLASS)) => {
157
+ convert_big_decimal(ruby, value)
158
+ }
159
+ _ => {
160
+ let class = value.class();
161
+ #[allow(unsafe_code)]
162
+ let class_name = unsafe { class.name() };
163
+ Err(Error::new(
164
+ ruby.exception_type_error(),
165
+ format!("Unsupported type: '{class_name}'"),
166
+ ))
167
+ }
168
+ }
169
+ }
170
+
171
+ /// Convert Ruby BigDecimal to JSON Number while preserving precision.
172
+ #[inline]
173
+ fn convert_big_decimal(ruby: &Ruby, value: Value) -> Result<JsonValue, Error> {
174
+ let decimal_text: String = value.funcall("to_s", ("F",))?;
175
+ if let Ok(JsonValue::Number(n)) = serde_json::from_str::<JsonValue>(&decimal_text) {
176
+ return Ok(JsonValue::Number(n));
177
+ }
178
+ Err(Error::new(
179
+ ruby.exception_arg_error(),
180
+ "Cannot convert BigDecimal NaN or Infinity to JSON",
181
+ ))
182
+ }
183
+
184
+ /// Convert Ruby Integer to JSON Number
185
+ /// Handles Fixnum, Bignum, and arbitrary precision
186
+ #[inline]
187
+ fn convert_integer(ruby: &Ruby, value: Value) -> Result<JsonValue, Error> {
188
+ // Try i64 first (handles most integers including negative fixnums)
189
+ if let Ok(i) = i64::try_convert(value) {
190
+ return Ok(JsonValue::Number(Number::from(i)));
191
+ }
192
+
193
+ // For bignums, try Integer methods
194
+ if let Some(int) = Integer::from_value(value) {
195
+ // Try u64 for large positive integers
196
+ if let Ok(u) = int.to_u64() {
197
+ return Ok(JsonValue::Number(Number::from(u)));
198
+ }
199
+ // Arbitrary precision via string parsing
200
+ let s: String = int.funcall("to_s", ())?;
201
+ if let Ok(JsonValue::Number(n)) = serde_json::from_str::<JsonValue>(&s) {
202
+ return Ok(JsonValue::Number(n));
203
+ }
204
+ }
205
+
206
+ Err(Error::new(
207
+ ruby.exception_type_error(),
208
+ "Cannot convert Integer to JSON",
209
+ ))
210
+ }
211
+
212
+ #[inline]
213
+ fn hash_key_to_string(ruby: &Ruby, key: Value) -> Result<String, Error> {
214
+ #[allow(unsafe_code)]
215
+ let key_type = unsafe { RB_TYPE(key.as_raw()) };
216
+
217
+ match key_type {
218
+ ruby_value_type::RUBY_T_STRING => {
219
+ if let Some(rstring) = RString::from_value(key) {
220
+ // SAFETY: rstring is valid
221
+ #[allow(unsafe_code)]
222
+ let bytes = unsafe { rstring.as_slice() };
223
+ return std::str::from_utf8(bytes)
224
+ .map(std::borrow::ToOwned::to_owned)
225
+ .map_err(|_| {
226
+ Error::new(
227
+ ruby.exception_encoding_error(),
228
+ "Hash key is not valid UTF-8",
229
+ )
230
+ });
231
+ }
232
+ }
233
+ ruby_value_type::RUBY_T_SYMBOL => {
234
+ if let Some(sym) = Symbol::from_value(key) {
235
+ return Ok(sym.name()?.to_string());
236
+ }
237
+ }
238
+ _ => {}
239
+ }
240
+
241
+ Err(Error::new(
242
+ ruby.exception_type_error(),
243
+ "Hash keys must be strings or symbols",
244
+ ))
245
+ }
246
+
247
+ #[inline]
248
+ pub fn map_to_ruby(ruby: &Ruby, map: &Map<String, JsonValue>) -> Result<Value, Error> {
249
+ let rb_hash = ruby.hash_new_capa(map.len());
250
+ for (k, v) in map {
251
+ rb_hash.aset(k.as_str(), value_to_ruby(ruby, v)?)?;
252
+ }
253
+ Ok(rb_hash.as_value())
254
+ }
255
+
256
+ #[inline]
257
+ pub fn value_to_ruby(ruby: &Ruby, value: &JsonValue) -> Result<Value, Error> {
258
+ match value {
259
+ JsonValue::Null => Ok(ruby.qnil().as_value()),
260
+ JsonValue::Bool(b) => Ok(ruby.into_value(*b)),
261
+ JsonValue::Number(n) => number_to_ruby(ruby, n),
262
+ JsonValue::String(s) => Ok(ruby.into_value(s.as_str())),
263
+ JsonValue::Array(arr) => {
264
+ let rb_arr = ruby.ary_new_capa(arr.len());
265
+ for item in arr {
266
+ rb_arr.push(value_to_ruby(ruby, item)?)?;
267
+ }
268
+ Ok(rb_arr.as_value())
269
+ }
270
+ JsonValue::Object(obj) => {
271
+ let rb_hash = ruby.hash_new_capa(obj.len());
272
+ for (k, v) in obj {
273
+ rb_hash.aset(k.as_str(), value_to_ruby(ruby, v)?)?;
274
+ }
275
+ Ok(rb_hash.as_value())
276
+ }
277
+ }
278
+ }
279
+
280
+ #[inline]
281
+ fn number_to_ruby(ruby: &Ruby, number: &Number) -> Result<Value, Error> {
282
+ if let Some(i) = number.as_i64() {
283
+ return Ok(ruby.into_value(i));
284
+ }
285
+ if let Some(u) = number.as_u64() {
286
+ return Ok(ruby.integer_from_u64(u).as_value());
287
+ }
288
+ number_string_to_ruby(ruby, &number.to_string())
289
+ }
290
+
291
+ #[inline]
292
+ fn number_string_to_ruby(ruby: &Ruby, number: &str) -> Result<Value, Error> {
293
+ if !number.contains(['.', 'e', 'E']) {
294
+ return ruby.module_kernel().funcall("Integer", (number,));
295
+ }
296
+
297
+ if let Ok(f) = number.parse::<f64>() {
298
+ if f.is_finite()
299
+ && Number::from_f64(f).is_some_and(|roundtrip| roundtrip.to_string() == number)
300
+ {
301
+ return Ok(ruby.into_value(f));
302
+ }
303
+ }
304
+
305
+ let _ = ruby.get_inner(&BIG_DECIMAL_CLASS);
306
+ ruby.module_kernel().funcall("BigDecimal", (number,))
307
+ }
308
+
309
+ /// Token used by serde_json with the `arbitrary_precision` feature.
310
+ const SERDE_JSON_NUMBER_TOKEN: &str = "$serde_json::private::Number";
311
+
312
+ #[derive(Debug)]
313
+ struct RubySerError(String);
314
+
315
+ impl fmt::Display for RubySerError {
316
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317
+ f.write_str(&self.0)
318
+ }
319
+ }
320
+
321
+ impl std::error::Error for RubySerError {}
322
+
323
+ impl serde::ser::Error for RubySerError {
324
+ fn custom<T: fmt::Display>(msg: T) -> Self {
325
+ RubySerError(msg.to_string())
326
+ }
327
+ }
328
+
329
+ /// A [`serde::Serializer`] that directly produces Ruby [`Value`] objects.
330
+ #[derive(Clone, Copy)]
331
+ struct RubySerializer<'a> {
332
+ ruby: &'a Ruby,
333
+ }
334
+
335
+ impl<'a> RubySerializer<'a> {
336
+ fn new(ruby: &'a Ruby) -> Self {
337
+ RubySerializer { ruby }
338
+ }
339
+
340
+ /// Parse a raw number string into a Ruby Integer, Float, or BigDecimal.
341
+ fn parse_number(&self, s: &str) -> Result<Value, RubySerError> {
342
+ number_string_to_ruby(self.ruby, s)
343
+ .map_err(|e| RubySerError(format!("number conversion failed: {e}")))
344
+ }
345
+ }
346
+
347
+ impl<'a> serde::Serializer for RubySerializer<'a> {
348
+ type Ok = Value;
349
+ type Error = RubySerError;
350
+
351
+ type SerializeSeq = RubySeqSerializer<'a>;
352
+ type SerializeTuple = RubySeqSerializer<'a>;
353
+ type SerializeTupleStruct = RubySeqSerializer<'a>;
354
+ type SerializeTupleVariant = RubySeqSerializer<'a>;
355
+ type SerializeMap = RubyMapSerializer<'a>;
356
+ type SerializeStruct = RubyStructSerializer<'a>;
357
+ type SerializeStructVariant = RubyStructSerializer<'a>;
358
+
359
+ #[inline]
360
+ fn serialize_bool(self, v: bool) -> Result<Value, RubySerError> {
361
+ Ok(self.ruby.into_value(v))
362
+ }
363
+
364
+ #[inline]
365
+ fn serialize_i8(self, v: i8) -> Result<Value, RubySerError> {
366
+ self.serialize_i64(i64::from(v))
367
+ }
368
+
369
+ #[inline]
370
+ fn serialize_i16(self, v: i16) -> Result<Value, RubySerError> {
371
+ self.serialize_i64(i64::from(v))
372
+ }
373
+
374
+ #[inline]
375
+ fn serialize_i32(self, v: i32) -> Result<Value, RubySerError> {
376
+ self.serialize_i64(i64::from(v))
377
+ }
378
+
379
+ #[inline]
380
+ fn serialize_i64(self, v: i64) -> Result<Value, RubySerError> {
381
+ Ok(self.ruby.into_value(v))
382
+ }
383
+
384
+ #[inline]
385
+ fn serialize_u8(self, v: u8) -> Result<Value, RubySerError> {
386
+ self.serialize_u64(u64::from(v))
387
+ }
388
+
389
+ #[inline]
390
+ fn serialize_u16(self, v: u16) -> Result<Value, RubySerError> {
391
+ self.serialize_u64(u64::from(v))
392
+ }
393
+
394
+ #[inline]
395
+ fn serialize_u32(self, v: u32) -> Result<Value, RubySerError> {
396
+ self.serialize_u64(u64::from(v))
397
+ }
398
+
399
+ #[inline]
400
+ fn serialize_u64(self, v: u64) -> Result<Value, RubySerError> {
401
+ Ok(self.ruby.integer_from_u64(v).as_value())
402
+ }
403
+
404
+ #[inline]
405
+ fn serialize_f32(self, v: f32) -> Result<Value, RubySerError> {
406
+ self.serialize_f64(f64::from(v))
407
+ }
408
+
409
+ #[inline]
410
+ fn serialize_f64(self, v: f64) -> Result<Value, RubySerError> {
411
+ Ok(self.ruby.into_value(v))
412
+ }
413
+
414
+ #[inline]
415
+ fn serialize_char(self, v: char) -> Result<Value, RubySerError> {
416
+ let mut buf = [0u8; 4];
417
+ Ok(self.ruby.into_value(v.encode_utf8(&mut buf) as &str))
418
+ }
419
+
420
+ #[inline]
421
+ fn serialize_str(self, v: &str) -> Result<Value, RubySerError> {
422
+ Ok(self.ruby.into_value(v))
423
+ }
424
+
425
+ fn serialize_bytes(self, v: &[u8]) -> Result<Value, RubySerError> {
426
+ Ok(self.ruby.str_from_slice(v).as_value())
427
+ }
428
+
429
+ #[inline]
430
+ fn serialize_none(self) -> Result<Value, RubySerError> {
431
+ Ok(self.ruby.qnil().as_value())
432
+ }
433
+
434
+ #[inline]
435
+ fn serialize_some<T: ?Sized + serde::Serialize>(
436
+ self,
437
+ value: &T,
438
+ ) -> Result<Value, RubySerError> {
439
+ value.serialize(self)
440
+ }
441
+
442
+ #[inline]
443
+ fn serialize_unit(self) -> Result<Value, RubySerError> {
444
+ Ok(self.ruby.qnil().as_value())
445
+ }
446
+
447
+ #[inline]
448
+ fn serialize_unit_struct(self, _name: &'static str) -> Result<Value, RubySerError> {
449
+ Ok(self.ruby.qnil().as_value())
450
+ }
451
+
452
+ fn serialize_unit_variant(
453
+ self,
454
+ _name: &'static str,
455
+ _variant_index: u32,
456
+ variant: &'static str,
457
+ ) -> Result<Value, RubySerError> {
458
+ Ok(self.ruby.into_value(variant))
459
+ }
460
+
461
+ fn serialize_newtype_struct<T: ?Sized + serde::Serialize>(
462
+ self,
463
+ name: &'static str,
464
+ value: &T,
465
+ ) -> Result<Value, RubySerError> {
466
+ if name == SERDE_JSON_NUMBER_TOKEN {
467
+ // inner serializes as a raw number string.
468
+ // Serialize to Ruby String first, then parse as number.
469
+ let rb_str = value.serialize(self)?;
470
+ if let Some(rstring) = RString::from_value(rb_str) {
471
+ let number = {
472
+ // Copy bytes into an owned String before calling Ruby:
473
+ // `parse_number` may invoke `Kernel.Integer` / `BigDecimal`,
474
+ // so keeping an `as_slice` borrow alive would be unsound.
475
+ // SAFETY: `rstring` is valid and we're in Ruby VM context.
476
+ #[allow(unsafe_code)]
477
+ let bytes = unsafe { rstring.as_slice() };
478
+ std::str::from_utf8(bytes)
479
+ .map(std::borrow::ToOwned::to_owned)
480
+ .map_err(|_| {
481
+ serde::ser::Error::custom("invalid arbitrary precision number")
482
+ })?
483
+ };
484
+ return self.parse_number(&number);
485
+ }
486
+ return Err(serde::ser::Error::custom(
487
+ "invalid arbitrary precision number",
488
+ ));
489
+ }
490
+ value.serialize(self)
491
+ }
492
+
493
+ fn serialize_newtype_variant<T: ?Sized + serde::Serialize>(
494
+ self,
495
+ _name: &'static str,
496
+ _variant_index: u32,
497
+ _variant: &'static str,
498
+ value: &T,
499
+ ) -> Result<Value, RubySerError> {
500
+ value.serialize(self)
501
+ }
502
+
503
+ fn serialize_seq(self, len: Option<usize>) -> Result<RubySeqSerializer<'a>, RubySerError> {
504
+ let arr = match len {
505
+ Some(n) => self.ruby.ary_new_capa(n),
506
+ None => self.ruby.ary_new(),
507
+ };
508
+ Ok(RubySeqSerializer {
509
+ ruby: self.ruby,
510
+ arr,
511
+ })
512
+ }
513
+
514
+ fn serialize_tuple(self, len: usize) -> Result<RubySeqSerializer<'a>, RubySerError> {
515
+ self.serialize_seq(Some(len))
516
+ }
517
+
518
+ fn serialize_tuple_struct(
519
+ self,
520
+ _name: &'static str,
521
+ len: usize,
522
+ ) -> Result<RubySeqSerializer<'a>, RubySerError> {
523
+ self.serialize_seq(Some(len))
524
+ }
525
+
526
+ fn serialize_tuple_variant(
527
+ self,
528
+ _name: &'static str,
529
+ _variant_index: u32,
530
+ _variant: &'static str,
531
+ len: usize,
532
+ ) -> Result<RubySeqSerializer<'a>, RubySerError> {
533
+ self.serialize_seq(Some(len))
534
+ }
535
+
536
+ fn serialize_map(self, len: Option<usize>) -> Result<RubyMapSerializer<'a>, RubySerError> {
537
+ let hash = match len {
538
+ Some(n) => self.ruby.hash_new_capa(n),
539
+ None => self.ruby.hash_new(),
540
+ };
541
+ Ok(RubyMapSerializer {
542
+ ruby: self.ruby,
543
+ hash,
544
+ next_key: None,
545
+ })
546
+ }
547
+
548
+ fn serialize_struct(
549
+ self,
550
+ _name: &'static str,
551
+ len: usize,
552
+ ) -> Result<RubyStructSerializer<'a>, RubySerError> {
553
+ Ok(RubyStructSerializer {
554
+ ruby: self.ruby,
555
+ hash: self.ruby.hash_new_capa(len),
556
+ })
557
+ }
558
+
559
+ fn serialize_struct_variant(
560
+ self,
561
+ _name: &'static str,
562
+ _variant_index: u32,
563
+ _variant: &'static str,
564
+ len: usize,
565
+ ) -> Result<RubyStructSerializer<'a>, RubySerError> {
566
+ Ok(RubyStructSerializer {
567
+ ruby: self.ruby,
568
+ hash: self.ruby.hash_new_capa(len),
569
+ })
570
+ }
571
+ }
572
+
573
+ /// Sequence serializer producing Ruby Arrays.
574
+ struct RubySeqSerializer<'a> {
575
+ ruby: &'a Ruby,
576
+ arr: RArray,
577
+ }
578
+
579
+ impl serde::ser::SerializeSeq for RubySeqSerializer<'_> {
580
+ type Ok = Value;
581
+ type Error = RubySerError;
582
+
583
+ fn serialize_element<T: ?Sized + serde::Serialize>(
584
+ &mut self,
585
+ value: &T,
586
+ ) -> Result<(), RubySerError> {
587
+ let v = value.serialize(RubySerializer::new(self.ruby))?;
588
+ self.arr.push(v).map_err(serde::ser::Error::custom)
589
+ }
590
+
591
+ fn end(self) -> Result<Value, RubySerError> {
592
+ Ok(self.arr.as_value())
593
+ }
594
+ }
595
+
596
+ impl serde::ser::SerializeTuple for RubySeqSerializer<'_> {
597
+ type Ok = Value;
598
+ type Error = RubySerError;
599
+
600
+ fn serialize_element<T: ?Sized + serde::Serialize>(
601
+ &mut self,
602
+ value: &T,
603
+ ) -> Result<(), RubySerError> {
604
+ serde::ser::SerializeSeq::serialize_element(self, value)
605
+ }
606
+
607
+ fn end(self) -> Result<Value, RubySerError> {
608
+ serde::ser::SerializeSeq::end(self)
609
+ }
610
+ }
611
+
612
+ impl serde::ser::SerializeTupleStruct for RubySeqSerializer<'_> {
613
+ type Ok = Value;
614
+ type Error = RubySerError;
615
+
616
+ fn serialize_field<T: ?Sized + serde::Serialize>(
617
+ &mut self,
618
+ value: &T,
619
+ ) -> Result<(), RubySerError> {
620
+ serde::ser::SerializeSeq::serialize_element(self, value)
621
+ }
622
+
623
+ fn end(self) -> Result<Value, RubySerError> {
624
+ serde::ser::SerializeSeq::end(self)
625
+ }
626
+ }
627
+
628
+ impl serde::ser::SerializeTupleVariant for RubySeqSerializer<'_> {
629
+ type Ok = Value;
630
+ type Error = RubySerError;
631
+
632
+ fn serialize_field<T: ?Sized + serde::Serialize>(
633
+ &mut self,
634
+ value: &T,
635
+ ) -> Result<(), RubySerError> {
636
+ serde::ser::SerializeSeq::serialize_element(self, value)
637
+ }
638
+
639
+ fn end(self) -> Result<Value, RubySerError> {
640
+ serde::ser::SerializeSeq::end(self)
641
+ }
642
+ }
643
+
644
+ /// Map serializer producing Ruby Hashes.
645
+ struct RubyMapSerializer<'a> {
646
+ ruby: &'a Ruby,
647
+ hash: RHash,
648
+ next_key: Option<Value>,
649
+ }
650
+
651
+ impl serde::ser::SerializeMap for RubyMapSerializer<'_> {
652
+ type Ok = Value;
653
+ type Error = RubySerError;
654
+
655
+ fn serialize_key<T: ?Sized + serde::Serialize>(&mut self, key: &T) -> Result<(), RubySerError> {
656
+ self.next_key = Some(key.serialize(RubySerializer::new(self.ruby))?);
657
+ Ok(())
658
+ }
659
+
660
+ fn serialize_value<T: ?Sized + serde::Serialize>(
661
+ &mut self,
662
+ value: &T,
663
+ ) -> Result<(), RubySerError> {
664
+ let key = self
665
+ .next_key
666
+ .take()
667
+ .expect("serialize_value called without serialize_key");
668
+ let val = value.serialize(RubySerializer::new(self.ruby))?;
669
+ self.hash.aset(key, val).map_err(serde::ser::Error::custom)
670
+ }
671
+
672
+ fn end(self) -> Result<Value, RubySerError> {
673
+ Ok(self.hash.as_value())
674
+ }
675
+ }
676
+
677
+ /// Struct serializer producing Ruby Hashes with symbol keys.
678
+ struct RubyStructSerializer<'a> {
679
+ ruby: &'a Ruby,
680
+ hash: RHash,
681
+ }
682
+
683
+ impl serde::ser::SerializeStruct for RubyStructSerializer<'_> {
684
+ type Ok = Value;
685
+ type Error = RubySerError;
686
+
687
+ fn serialize_field<T: ?Sized + serde::Serialize>(
688
+ &mut self,
689
+ key: &'static str,
690
+ value: &T,
691
+ ) -> Result<(), RubySerError> {
692
+ let val = value.serialize(RubySerializer::new(self.ruby))?;
693
+ let sym = self.ruby.sym_new(key);
694
+ self.hash.aset(sym, val).map_err(serde::ser::Error::custom)
695
+ }
696
+
697
+ fn end(self) -> Result<Value, RubySerError> {
698
+ Ok(self.hash.as_value())
699
+ }
700
+ }
701
+
702
+ impl serde::ser::SerializeStructVariant for RubyStructSerializer<'_> {
703
+ type Ok = Value;
704
+ type Error = RubySerError;
705
+
706
+ fn serialize_field<T: ?Sized + serde::Serialize>(
707
+ &mut self,
708
+ key: &'static str,
709
+ value: &T,
710
+ ) -> Result<(), RubySerError> {
711
+ serde::ser::SerializeStruct::serialize_field(self, key, value)
712
+ }
713
+
714
+ fn end(self) -> Result<Value, RubySerError> {
715
+ serde::ser::SerializeStruct::end(self)
716
+ }
717
+ }
718
+
719
+ /// Serialize any [`serde::Serialize`] type directly to a Ruby [`Value`].
720
+ pub fn serialize_to_ruby<T: serde::Serialize>(ruby: &Ruby, value: &T) -> Result<Value, Error> {
721
+ value
722
+ .serialize(RubySerializer::new(ruby))
723
+ .map_err(|err| Error::new(ruby.exception_runtime_error(), err.to_string()))
724
+ }