Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions mruby-serde-json/src/json_value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ 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>>,
Expand Down Expand Up @@ -91,3 +92,70 @@ pub(crate) fn mrb_json_dump(vm: &mut VM, obj: Rc<RObject>) -> Result<Rc<RObject>
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())
}
42 changes: 41 additions & 1 deletion mruby-serde-json/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,31 @@ use mrubyedge::{

pub fn init_json(vm: &mut VM) {
let json_class = vm.define_class("JSON", None, None);
mrb_define_class_cmethod(vm, json_class, "dump", Box::new(mrb_json_class_dump));

mrb_define_class_cmethod(
vm,
json_class.clone(),
"dump",
Box::new(mrb_json_class_dump),
);
mrb_define_class_cmethod(
vm,
json_class.clone(),
"generate",
Box::new(mrb_json_class_dump),
);
mrb_define_class_cmethod(
vm,
json_class.clone(),
"load",
Box::new(mrb_json_class_load),
);
mrb_define_class_cmethod(
vm,
json_class.clone(),
"parse",
Box::new(mrb_json_class_load),
);
}

pub fn mrb_json_class_dump(vm: &mut VM, args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
Expand All @@ -26,3 +50,19 @@ pub fn mrb_json_class_dump(vm: &mut VM, args: &[Rc<RObject>]) -> Result<Rc<RObje
let result = json_value::mrb_json_dump(vm, args[0].clone())?;
Ok(result)
}

pub fn mrb_json_class_load(vm: &mut VM, args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
let args = if args[args.len() - 1].is_nil() {
&args[0..args.len() - 1]
} else {
args
};
if args.len() != 1 {
return Err(Error::ArgumentError(
"wrong number of arguments".to_string(),
));
}
let json_str: String = args[0].as_ref().try_into()?;
let result = json_value::mrb_json_load(vm, json_str)?;
Ok(result.get_inner())
}
3 changes: 1 addition & 2 deletions mruby-serde-json/tests/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,5 @@ pub fn mrbc_compile(fname: &'static str, code: &'static str) -> Vec<u8> {
mec_mrbc_sys::mrbc_main(args.len() as i32, args.as_ptr() as *mut *mut i8);
}

let binary = std::fs::read(dest).unwrap();
binary
std::fs::read(dest).unwrap()
}
224 changes: 224 additions & 0 deletions mruby-serde-json/tests/load.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
extern crate mruby_serde_json;
extern crate mrubyedge;

mod helpers;
use helpers::*;

#[test]
fn test_json_load_integer() {
let code = r#"
JSON.load("42")
"#;
let binary = mrbc_compile("json_load_integer", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
mruby_serde_json::init_json(&mut vm);

let result = vm.run().unwrap();
let value: i64 = result.as_ref().try_into().unwrap();
assert_eq!(value, 42);
}

#[test]
fn test_json_load_string() {
let code = r#"
JSON.load('"hello"')
"#;
let binary = mrbc_compile("json_load_string", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
mruby_serde_json::init_json(&mut vm);

let result = vm.run().unwrap();
let value: String = result.as_ref().try_into().unwrap();
assert_eq!(value, "hello");
}

#[test]
fn test_json_load_array() {
let code = r#"
result = JSON.load('[
1,
2,
3
]')
result
"#;
let binary = mrbc_compile("json_load_array", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
mruby_serde_json::init_json(&mut vm);

let result = vm.run().unwrap();
if let mrubyedge::yamrb::value::RValue::Array(arr) = &result.value {
assert_eq!(arr.borrow().len(), 3);
let v0: i64 = arr.borrow()[0].as_ref().try_into().unwrap();
let v1: i64 = arr.borrow()[1].as_ref().try_into().unwrap();
let v2: i64 = arr.borrow()[2].as_ref().try_into().unwrap();
assert_eq!(v0, 1);
assert_eq!(v1, 2);
assert_eq!(v2, 3);
} else {
panic!("Expected array result");
}
}

#[test]
fn test_json_load_hash() {
let code = r#"
result = JSON.load('{
"name": "Alice",
"age": 30
}')
result["name"]
"#;
let binary = mrbc_compile("json_load_hash_name", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
mruby_serde_json::init_json(&mut vm);
let result = vm.run().unwrap();
let name: String = result.as_ref().try_into().unwrap();
assert_eq!(name, "Alice");

let code2 = r#"
result = JSON.load('{
"name": "Alice",
"age": 30
}')
result["age"]
"#;
let binary2 = mrbc_compile("json_load_hash_age", code2);
let mut rite2 = mrubyedge::rite::load(&binary2).unwrap();
let mut vm2 = mrubyedge::yamrb::vm::VM::open(&mut rite2);
mruby_serde_json::init_json(&mut vm2);
let result2 = vm2.run().unwrap();
let age: i64 = result2.as_ref().try_into().unwrap();
assert_eq!(age, 30);
}

#[test]
fn test_json_load_nested_structure() {
let code = r#"
result = JSON.load('{
"users": [
{
"name": "Bob",
"age": 25
},
{
"name": "Carol",
"age": 28
}
]
}')
result["users"][0]["name"]
"#;
let binary = mrbc_compile("json_load_nested", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
mruby_serde_json::init_json(&mut vm);

let result = vm.run().unwrap();
let name: String = result.as_ref().try_into().unwrap();
assert_eq!(name, "Bob");

let code2 = r#"
result = JSON.load('{
"users": [
{
"name": "Bob",
"age": 25
},
{
"name": "Carol",
"age": 28
}
]
}')
result["users"][1]["name"]
"#;
let binary2 = mrbc_compile("json_load_nested2", code2);
let mut rite2 = mrubyedge::rite::load(&binary2).unwrap();
let mut vm2 = mrubyedge::yamrb::vm::VM::open(&mut rite2);
mruby_serde_json::init_json(&mut vm2);
let result2 = vm2.run().unwrap();
let name2: String = result2.as_ref().try_into().unwrap();
assert_eq!(name2, "Carol");
}

#[test]
fn test_json_load_boolean() {
let code = r#"
JSON.load("true")
"#;
let binary = mrbc_compile("json_load_bool", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
mruby_serde_json::init_json(&mut vm);

let result = vm.run().unwrap();
let value: bool = result.as_ref().try_into().unwrap();
assert_eq!(value, true);
}

#[test]
fn test_json_load_boolean_2() {
let code = r#"
JSON.load("false")
"#;
let binary = mrbc_compile("json_load_bool_2", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
mruby_serde_json::init_json(&mut vm);

let result = vm.run().unwrap();
let value: bool = result.as_ref().try_into().unwrap();
assert_eq!(value, false);
}

#[test]
fn test_json_load_nil() {
let code = r#"
JSON.load("null")
"#;
let binary = mrbc_compile("json_load_nil", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
mruby_serde_json::init_json(&mut vm);

let result = vm.run().unwrap();
assert!(result.is_nil());
}

#[test]
fn test_json_load_float() {
let code = r#"
JSON.load("3.14")
"#;
let binary = mrbc_compile("json_load_float", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
mruby_serde_json::init_json(&mut vm);

let result = vm.run().unwrap();
let value: f64 = result.as_ref().try_into().unwrap();
assert_eq!(value, 3.14);
}

#[test]
fn test_json_load_key() {
let code = r#"
result = JSON.load('{
"status": "ok"
}')
result["status"]
"#;
let binary = mrbc_compile("json_load_key", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
mruby_serde_json::init_json(&mut vm);

let result = vm.run().unwrap();
let value: String = result.as_ref().try_into().unwrap();
assert_eq!(value, "ok");
}