mruby-serde-json 0.1.0

mruby-serde-json provides JSON serialization/deserialization for mruby/edge using serde_json
Documentation
use std::cell::RefCell;
use std::rc::Rc;

use mrubyedge::Error;
use mrubyedge::yamrb::helpers::mrb_funcall;
use mrubyedge::yamrb::value::RObject;
use mrubyedge::yamrb::value::RValue;
use mrubyedge::yamrb::vm::VM;
use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
use serde_json::Value;

pub struct Json<'a> {
    mrb: Rc<RefCell<&'a mut VM>>,
    inner: Rc<RObject>,
}

impl<'a> Json<'a> {
    pub fn get_inner(&self) -> Rc<RObject> {
        self.inner.clone()
    }

    pub fn from_robject(mrb: Rc<RefCell<&'a mut VM>>, inner: Rc<RObject>) -> Self {
        Self { mrb, inner }
    }
}

impl<'a> From<Json<'a>> for Rc<RObject> {
    fn from(value: Json<'a>) -> Self {
        value.get_inner()
    }
}

impl<'a> Serialize for Json<'a> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self.get_inner().value {
            RValue::Nil => serializer.serialize_none(),
            RValue::Bool(b) => serializer.serialize_bool(b),
            RValue::Integer(i) => serializer.serialize_i64(i),
            RValue::Float(f) => serializer.serialize_f64(f),
            RValue::String(ref s, _) => {
                serializer.serialize_str(&String::from_utf8_lossy(&s.borrow()))
            }
            RValue::Symbol(ref s) => serializer.serialize_str(&s.name),
            RValue::Array(ref arr) => {
                let arr = arr.borrow();
                let mut seq = serializer.serialize_seq(Some(arr.len()))?;
                for item in arr.iter() {
                    let json_item = Json::from_robject(self.mrb.clone(), item.clone());
                    seq.serialize_element(&json_item)?;
                }
                seq.end()
            }
            RValue::Hash(ref hash) => {
                let hash = hash.borrow();
                let mut map = serializer.serialize_map(Some(hash.len()))?;
                for (_, (key, value)) in hash.iter() {
                    let key_str = match key.value {
                        RValue::String(ref s, _) => {
                            String::from_utf8_lossy(&s.borrow()).to_string()
                        }
                        RValue::Symbol(ref s) => s.name.to_string(),
                        _ => {
                            return Err(serde::ser::Error::custom("Non-string key in JSON object"));
                        }
                    };
                    let json_value = Json::from_robject(self.mrb.clone(), value.clone());
                    map.serialize_entry(&key_str, &json_value)?;
                }
                map.end()
            }
            _ => {
                let obj = self.get_inner();
                let vm = &mut self.mrb.borrow_mut();
                let serializable = mrb_funcall(vm, Some(obj), "to_json", &[]);
                let json_obj = Json::from_robject(
                    self.mrb.clone(),
                    serializable.expect("to_json not defined for instance"),
                );
                json_obj.serialize(serializer)
            }
        }
    }
}

pub(crate) fn mrb_json_dump(vm: &mut VM, obj: Rc<RObject>) -> Result<Rc<RObject>, Error> {
    let vm = Rc::new(RefCell::new(vm));
    let json_value = Json::from_robject(vm, obj);
    let serialized =
        serde_json::to_string(&json_value).expect("Failed to serialize JSON value to string");
    Ok(RObject::string(serialized).to_refcount_assigned())
}

pub struct JsonValue {
    inner: Rc<RObject>,
}

impl JsonValue {
    pub fn new(inner: Rc<RObject>) -> Self {
        Self { inner }
    }

    pub fn get_inner(&self) -> Rc<RObject> {
        self.inner.clone()
    }
}

impl From<JsonValue> for Rc<RObject> {
    fn from(value: JsonValue) -> Self {
        value.get_inner()
    }
}

impl From<Value> for JsonValue {
    fn from(value: Value) -> Self {
        let obj = match value {
            Value::Null => RObject::nil().to_refcount_assigned(),
            Value::Bool(b) => RObject::boolean(b).to_refcount_assigned(),
            Value::Number(n) => {
                if let Some(i) = n.as_i64() {
                    RObject::integer(i).to_refcount_assigned()
                } else if let Some(u) = n.as_u64() {
                    RObject::integer(u as i64).to_refcount_assigned()
                } else if let Some(f) = n.as_f64() {
                    RObject::float(f).to_refcount_assigned()
                } else {
                    panic!("Invalid range of numeric value");
                }
            }
            Value::String(s) => RObject::string(s).to_refcount_assigned(),
            Value::Array(arr) => {
                let vec = arr.into_iter().map(JsonValue::from).collect::<Vec<_>>();
                let arr = RObject::array(vec.into_iter().map(|j| j.get_inner()).collect());
                arr.to_refcount_assigned()
            }
            Value::Object(obj) => {
                let map = obj
                    .into_iter()
                    .map(|(k, v)| {
                        let key = RObject::string(k).to_refcount_assigned();
                        (
                            key.as_hash_key().expect("object cannot use for hashed key"),
                            (key.clone(), JsonValue::from(v).get_inner()),
                        )
                    })
                    .collect();
                let hash = RObject::hash(map);
                hash.to_refcount_assigned()
            }
        };
        JsonValue::new(obj)
    }
}

pub(crate) fn mrb_json_load(_vm: &mut VM, json_str: impl Into<String>) -> Result<JsonValue, Error> {
    let value = serde_json::from_str::<serde_json::Value>(&json_str.into())
        .map_err(|e| Error::RuntimeError(format!("Failed to parse JSON string: {}", e)))?;
    Ok(value.into())
}