From 39eeb57ac325f95f80e84bfc373a8ea699881b5f Mon Sep 17 00:00:00 2001 From: envis10n Date: Wed, 30 Jan 2019 19:03:50 -0600 Subject: [PATCH] Usable object wrapper. Improve DukValue enum. --- Cargo.toml | 3 +- src/lib.rs | 283 ++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 196 insertions(+), 90 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cf82b2a..d8c6bd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,5 +10,4 @@ license = "Unlicense" description = "Safe(er) rust wrapper for dukbind." [dependencies] -dukbind = "0.0.4" -serde_json = "1.0.37" \ No newline at end of file +dukbind = "0.0.4" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 2e656fd..d093e5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,10 @@ extern crate dukbind; -extern crate serde_json; - -use serde_json::*; use dukbind::*; use std::error::Error; use std::fmt; use std::f64; +use std::os::raw::c_void; #[allow(missing_docs)] #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -21,79 +19,160 @@ pub enum DukErrorCode { URI = DUK_ERR_URI_ERROR } - -/// Compatibility type enum providing NaN, Infinity, and None alongside Serde JSON types as Val. #[derive(Clone, Debug)] -pub enum DukValue { - None, +pub enum DukNumber { NaN, Infinity, - Val(Value) + Float(f64), + Int(i64) } -impl DukValue { - pub fn as_str(&self) -> Option<&str> { - match self { - DukValue::None => None, - DukValue::NaN => Some("NaN"), - DukValue::Infinity => Some("Infinity"), - DukValue::Val(ref v) => v.as_str() - } - } - pub fn as_f64(&self) -> Option { - match self { - DukValue::NaN => Some(f64::NAN), - DukValue::Infinity => Some(f64::INFINITY), - DukValue::Val(ref v) => v.as_f64(), - DukValue::None => None +impl DukNumber { + pub fn as_str(&self) -> String { + match self.clone() { + DukNumber::NaN => "NaN".to_string(), + DukNumber::Infinity => "Infinity".to_string(), + DukNumber::Float(v) => v.clone().to_string(), + DukNumber::Int(v) => v.clone().to_string() } } pub fn is_f64(&self) -> bool { match self { - DukValue::None => false, - DukValue::NaN => true, - DukValue::Infinity => true, - DukValue::Val(ref v) => v.is_f64() + DukNumber::Int(_v) => false, + _ => true } } pub fn is_i64(&self) -> bool { - match self { - DukValue::Val(ref v) => v.is_i64(), - _ => false - } - } - pub fn as_i64(&self) -> Option { - match self { - DukValue::NaN => Some(f64::NAN as i64), - DukValue::Infinity => Some(f64::INFINITY as i64), - DukValue::Val(ref v) => v.as_i64(), - DukValue::None => None - } - } - pub fn as_value(&self) -> Option { - match self { - DukValue::Val(ref v) => Some(v.clone()), - _ => None - } + self.is_f64() == false } pub fn is_nan(&self) -> bool { match self { - DukValue::NaN => true, + DukNumber::NaN => true, _ => false } } pub fn is_infinity(&self) -> bool { match self { - DukValue::Infinity => true, + DukNumber::Infinity => true, _ => false } } - pub fn is_value(&self) -> bool { + pub fn as_f64(&self) -> f64 { match self { - DukValue::Val(ref v) => true, + DukNumber::NaN => f64::NAN, + DukNumber::Infinity => f64::INFINITY, + DukNumber::Float(v) => *v, + DukNumber::Int(v) => *v as f64 + } + } + pub fn as_i64(&self) -> i64 { + match self { + DukNumber::NaN => f64::NAN as i64, + DukNumber::Infinity => f64::INFINITY as i64, + DukNumber::Float(v) => *v as i64, + DukNumber::Int(v) => *v + } + } +} + +#[derive(Clone, Debug)] +pub struct DukObject { + context: DukContext, + heap: *mut c_void +} + +impl DukObject { + pub fn get_prop(&mut self, name: &str) -> DukResult { + unsafe { + let ctx = self.context.ctx.expect("Invalid context pointer."); + let idx = duk_push_heapptr(ctx, self.heap); + if duk_get_prop_lstring(ctx, idx, name.as_ptr() as *const i8, name.len() as duk_size_t) == 1 { + let result = self.context.get_value(); + duk_pop(ctx); + Ok(result) + } else { + Err(DukError{ code: DukErrorCode::Error, message: Some("Could not get property.".to_string())}) + } + } + } + pub fn new(context: DukContext) -> DukObject { + unsafe { + let ctx = context.ctx.expect("Invalid context pointer."); + let ptr = duk_get_heapptr(ctx, -1); + duk_push_heap_stash(ctx); + duk_dup(ctx, -2); + duk_put_prop_heapptr(ctx, -2, ptr); + duk_pop(ctx); + DukObject { heap: ptr, context: context } + } + } +} + +#[derive(Clone, Debug)] +pub enum DukValue { + Undefined, + Null, + Number(DukNumber), + Boolean(bool), + String(String), + Object(DukObject) +} + +impl DukValue { + pub fn as_str(&self) -> Option { + match self { + DukValue::Undefined => Some(String::from("undefined")), + DukValue::Null => Some(String::from("null")), + DukValue::Number(ref n) => Some(String::from(n.as_str())), + DukValue::Boolean(b) => Some(b.to_string()), + DukValue::String(s) => Some(s.clone()), + DukValue::Object(ref _o) => Some(String::from("[object]")) + } + } + pub fn as_bool(&self) -> Option { + match self { + DukValue::Boolean(b) => Some(*b), + _ => None + } + } + pub fn as_number(&self) -> Option { + match self { + DukValue::Number(ref n) => Some(n.clone()), + _ => None + } + } + pub fn as_object(&mut self) -> Option<&mut DukObject> { + match self { + DukValue::Object(ref mut o) => { + Some(o) + }, + _ => None + } + } + pub fn as_f64(&self) -> Option { + match self { + DukValue::Number(ref n) => Some(n.as_f64()), + _ => None + } + } + pub fn is_f64(&self) -> bool { + match self { + DukValue::Number(ref n) => n.is_f64(), _ => false } } + pub fn is_i64(&self) -> bool { + match self { + DukValue::Number(ref n) => n.is_i64(), + _ => false + } + } + pub fn as_i64(&self) -> Option { + match self { + DukValue::Number(ref n) => Some(n.as_i64()), + _ => None + } + } } #[derive(PartialEq, Eq, Debug)] @@ -145,84 +224,77 @@ impl fmt::Display for DukError { pub type DukResult = std::result::Result; +#[derive(Clone, Debug)] pub struct DukContext { - ctx: *mut duk_context, + pub ctx: Option<*mut duk_context>, } impl DukContext { fn new() -> DukContext { unsafe { - DukContext { ctx: duk_create_heap_default() } + DukContext { ctx: Some(duk_create_heap_default()) } + } + } + fn destroy(&mut self) { + unsafe { + duk_destroy_heap(self.ctx.expect("Invalid context pointer.")); + self.ctx = None; } } fn get_value(&mut self) -> DukValue { unsafe { - let t = duk_get_type(self.ctx, -1); + let t = duk_get_type(self.ctx.expect("Invalid context pointer"), -1); match t as u32 { - DUK_TYPE_NONE => DukValue::Val(Value::default()), - DUK_TYPE_UNDEFINED => DukValue::Val(Value::default()), - DUK_TYPE_NULL => DukValue::Val(Value::Null), - DUK_TYPE_BOOLEAN => DukValue::Val(Value::Bool(duk_get_boolean(self.ctx, -1) == 0)), + DUK_TYPE_NONE => DukValue::Null, + DUK_TYPE_UNDEFINED => DukValue::Undefined, + DUK_TYPE_NULL => DukValue::Null, + DUK_TYPE_BOOLEAN => DukValue::Boolean(duk_get_boolean(self.ctx.expect("Invalid context pointer"), -1) == 1), DUK_TYPE_NUMBER => { - let v = duk_get_number(self.ctx, -1); + let v = duk_get_number(self.ctx.expect("Invalid context pointer"), -1); if v.fract() > 0_f64 { - match Number::from_f64(v) { - Some(n) => DukValue::Val(Value::Number(n)), - None => { - if v.is_nan() { - DukValue::NaN - } else if v.is_infinite() { - DukValue::Infinity - } else { - DukValue::None - } - } - } + DukValue::Number(DukNumber::Float(v)) } else { if v.is_nan() { - DukValue::NaN + DukValue::Number(DukNumber::NaN) } else if v.is_infinite() { - DukValue::Infinity + DukValue::Number(DukNumber::Infinity) } else { - DukValue::Val(Value::Number(Number::from(v as i32))) + DukValue::Number(DukNumber::Int(v as i64)) } } }, DUK_TYPE_STRING => { use std::ffi::CStr; - let v = duk_get_string(self.ctx, -1); + let v = duk_get_string(self.ctx.expect("Invalid context pointer"), -1); let t = CStr::from_ptr(v); let cow = t.to_string_lossy(); - DukValue::Val(Value::String(String::from(cow))) + DukValue::String(String::from(cow)) }, DUK_TYPE_OBJECT => { - use std::ffi::CStr; - let istr = duk_json_encode(self.ctx, -1); - let t = CStr::from_ptr(istr); - let cow = t.to_string_lossy(); - DukValue::Val(serde_json::from_str(&String::from(cow)).unwrap()) + let obj = DukObject::new(self.clone()); + DukValue::Object(obj) }, - _ => DukValue::None + _ => DukValue::Undefined } } } fn eval_string(&mut self, code: &str) -> DukResult { unsafe { - if duk_eval_string(self.ctx, code) == 0 { + if duk_eval_string(self.ctx.expect("Invalid context pointer"), code) == 0 { let result = self.get_value(); - duk_pop(self.ctx); + duk_pop_2(self.ctx.expect("Invalid context pointer")); Ok(result) } else { - let code = duk_get_error_code(self.ctx, -1) as u32; + let code = duk_get_error_code(self.ctx.expect("Invalid context pointer"), -1) as u32; let name = "stack"; - duk_get_prop_lstring(self.ctx, -1, name.as_ptr() as *const i8, name.len() as duk_size_t); + duk_get_prop_lstring(self.ctx.expect("Invalid context pointer"), -1, name.as_ptr() as *const i8, name.len() as duk_size_t); let val = self.get_value(); - duk_pop(self.ctx); + duk_pop(self.ctx.expect("Invalid context pointer")); match val.as_str() { Some(v) => { use std::mem; let c: DukErrorCode = mem::transmute(code); - Err(DukError::from(c, v)) + Err(DukError::from(c, v.as_ref())) }, None => { Err(DukError::from_code(DukErrorCode::Error)) @@ -239,7 +311,42 @@ mod tests { #[test] fn test_eval_ret() { let mut ctx = DukContext::new(); - let res = ctx.eval_string("5*5").expect("Eval error!"); - assert_eq!(25, res.as_i64().expect("Value was not an integer!")) + let mut res = ctx.eval_string("({'ok': true})").expect("Eval error!"); + let o: &mut DukObject = res.as_object().expect("Should be an object here."); + match o.get_prop("ok") { + Ok(r) => { + println!("Test OK {}", r.as_bool().expect("NOT A BOOL")); + ctx.destroy(); + }, + Err(e) => { + println!("Error: {:?}", e); + ctx.destroy(); + } + } + } + #[test] + fn test_eval_ret_multi() { + let mut ctx = DukContext::new(); + let mut res = ctx.eval_string("({derp:{ok: true}})").expect("Eval error!"); + let o: &mut DukObject = res.as_object().expect("Should be an object here."); + match o.get_prop("derp") { + Ok(mut r) => { + let o2: &mut DukObject = r.as_object().expect("Should be another object here!"); + match o2.get_prop("ok") { + Ok(r2) => { + println!("Test OK: {}", r2.as_bool().expect("Should be a bool here.")); + ctx.destroy(); + }, + Err(e) => { + ctx.destroy(); + panic!("{:?}", e); + } + } + }, + Err(e) => { + ctx.destroy(); + panic!("Error: {:?}", e); + } + } } }