1use magnus::prelude::*;
7use magnus::{Error, RArray, RHash, Ruby, TryConvert, Value};
8use serde_json::json;
9
10pub fn rb_value_to_json(ruby: &Ruby, value: Value) -> Result<serde_json::Value, Error> {
25 if value.is_nil() {
26 Ok(serde_json::Value::Null)
27 } else if value.is_kind_of(ruby.class_true_class())
28 || value.is_kind_of(ruby.class_false_class())
29 {
30 let b: bool = TryConvert::try_convert(value).map_err(|e| {
31 Error::new(
32 ruby.exception_type_error(),
33 format!("Failed to convert boolean: {e}"),
34 )
35 })?;
36 Ok(serde_json::Value::Bool(b))
37 } else if value.is_kind_of(ruby.class_integer()) {
38 let i: i64 = TryConvert::try_convert(value).map_err(|e| {
39 Error::new(
40 ruby.exception_type_error(),
41 format!("Failed to convert integer: {e}"),
42 )
43 })?;
44 Ok(serde_json::Value::from(i))
45 } else if value.is_kind_of(ruby.class_float()) {
46 let f: f64 = TryConvert::try_convert(value).map_err(|e| {
47 Error::new(
48 ruby.exception_type_error(),
49 format!("Failed to convert float: {e}"),
50 )
51 })?;
52 Ok(json!(f))
53 } else if value.is_kind_of(ruby.class_string()) || value.is_kind_of(ruby.class_symbol()) {
54 let s: String = TryConvert::try_convert(value).map_err(|e| {
55 Error::new(
56 ruby.exception_type_error(),
57 format!("Failed to convert string: {e}"),
58 )
59 })?;
60 Ok(serde_json::Value::String(s))
61 } else if let Ok(arr) = RArray::try_convert(value) {
62 rb_array_to_json(ruby, arr)
63 } else if let Ok(hash) = RHash::try_convert(value) {
64 rb_hash_to_json(ruby, hash)
65 } else {
66 Err(Error::new(
67 ruby.exception_type_error(),
68 format!("Unsupported Ruby object type: {}", unsafe {
69 value.classname()
70 }),
71 ))
72 }
73}
74
75fn rb_array_to_json(ruby: &Ruby, array: RArray) -> Result<serde_json::Value, Error> {
86 let mut vec = Vec::new();
87 for item in array.into_iter() {
88 vec.push(rb_value_to_json(ruby, item)?);
89 }
90 Ok(serde_json::Value::Array(vec))
91}
92
93pub fn rb_hash_to_json(ruby: &Ruby, hash: RHash) -> Result<serde_json::Value, Error> {
104 let mut map = serde_json::Map::new();
105 hash.foreach(|key: String, value: Value| {
106 let json_value = rb_value_to_json(ruby, value)?;
107 map.insert(key, json_value);
108 Ok(magnus::r_hash::ForEach::Continue)
109 })?;
110 Ok(serde_json::Value::Object(map))
111}
112
113pub fn json_to_rb_value(ruby: &Ruby, value: &serde_json::Value) -> Result<Value, Error> {
124 match value {
125 serde_json::Value::Null => Ok(ruby.qnil().as_value()),
126 serde_json::Value::Bool(b) => Ok(if *b {
127 ruby.qtrue().as_value()
128 } else {
129 ruby.qfalse().as_value()
130 }),
131 serde_json::Value::Number(n) => {
132 if let Some(i) = n.as_i64() {
133 Ok(ruby.integer_from_i64(i).as_value())
134 } else if let Some(f) = n.as_f64() {
135 Ok(ruby.float_from_f64(f).as_value())
136 } else {
137 Err(Error::new(
138 ruby.exception_type_error(),
139 "Unsupported number type",
140 ))
141 }
142 }
143 serde_json::Value::String(s) => Ok(ruby.str_new(s).as_value()),
144 serde_json::Value::Array(arr) => {
145 let rb_arr = ruby.ary_new_capa(arr.len());
146 for item in arr {
147 rb_arr.push(json_to_rb_value(ruby, item)?)?;
148 }
149 Ok(rb_arr.as_value())
150 }
151 serde_json::Value::Object(obj) => {
152 let rb_hash = ruby.hash_new();
153 for (key, val) in obj {
154 rb_hash.aset(ruby.str_new(key), json_to_rb_value(ruby, val)?)?;
155 }
156 Ok(rb_hash.as_value())
157 }
158 }
159}