Usable object wrapper.
Improve DukValue enum.
This commit is contained in:
parent
5b90fd87e9
commit
39eeb57ac3
2 changed files with 196 additions and 90 deletions
|
@ -11,4 +11,3 @@ description = "Safe(er) rust wrapper for dukbind."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dukbind = "0.0.4"
|
dukbind = "0.0.4"
|
||||||
serde_json = "1.0.37"
|
|
283
src/lib.rs
283
src/lib.rs
|
@ -1,12 +1,10 @@
|
||||||
extern crate dukbind;
|
extern crate dukbind;
|
||||||
extern crate serde_json;
|
|
||||||
|
|
||||||
use serde_json::*;
|
|
||||||
|
|
||||||
use dukbind::*;
|
use dukbind::*;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::f64;
|
use std::f64;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
@ -21,79 +19,160 @@ pub enum DukErrorCode {
|
||||||
URI = DUK_ERR_URI_ERROR
|
URI = DUK_ERR_URI_ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Compatibility type enum providing NaN, Infinity, and None alongside Serde JSON types as Val.
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum DukValue {
|
pub enum DukNumber {
|
||||||
None,
|
|
||||||
NaN,
|
NaN,
|
||||||
Infinity,
|
Infinity,
|
||||||
Val(Value)
|
Float(f64),
|
||||||
|
Int(i64)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DukValue {
|
impl DukNumber {
|
||||||
pub fn as_str(&self) -> Option<&str> {
|
pub fn as_str(&self) -> String {
|
||||||
match self {
|
match self.clone() {
|
||||||
DukValue::None => None,
|
DukNumber::NaN => "NaN".to_string(),
|
||||||
DukValue::NaN => Some("NaN"),
|
DukNumber::Infinity => "Infinity".to_string(),
|
||||||
DukValue::Infinity => Some("Infinity"),
|
DukNumber::Float(v) => v.clone().to_string(),
|
||||||
DukValue::Val(ref v) => v.as_str()
|
DukNumber::Int(v) => v.clone().to_string()
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn as_f64(&self) -> Option<f64> {
|
|
||||||
match self {
|
|
||||||
DukValue::NaN => Some(f64::NAN),
|
|
||||||
DukValue::Infinity => Some(f64::INFINITY),
|
|
||||||
DukValue::Val(ref v) => v.as_f64(),
|
|
||||||
DukValue::None => None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_f64(&self) -> bool {
|
pub fn is_f64(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
DukValue::None => false,
|
DukNumber::Int(_v) => false,
|
||||||
DukValue::NaN => true,
|
_ => true
|
||||||
DukValue::Infinity => true,
|
|
||||||
DukValue::Val(ref v) => v.is_f64()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_i64(&self) -> bool {
|
pub fn is_i64(&self) -> bool {
|
||||||
match self {
|
self.is_f64() == false
|
||||||
DukValue::Val(ref v) => v.is_i64(),
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn as_i64(&self) -> Option<i64> {
|
|
||||||
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<Value> {
|
|
||||||
match self {
|
|
||||||
DukValue::Val(ref v) => Some(v.clone()),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn is_nan(&self) -> bool {
|
pub fn is_nan(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
DukValue::NaN => true,
|
DukNumber::NaN => true,
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_infinity(&self) -> bool {
|
pub fn is_infinity(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
DukValue::Infinity => true,
|
DukNumber::Infinity => true,
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_value(&self) -> bool {
|
pub fn as_f64(&self) -> f64 {
|
||||||
match self {
|
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<DukValue> {
|
||||||
|
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<String> {
|
||||||
|
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<bool> {
|
||||||
|
match self {
|
||||||
|
DukValue::Boolean(b) => Some(*b),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn as_number(&self) -> Option<DukNumber> {
|
||||||
|
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<f64> {
|
||||||
|
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
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn is_i64(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
DukValue::Number(ref n) => n.is_i64(),
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn as_i64(&self) -> Option<i64> {
|
||||||
|
match self {
|
||||||
|
DukValue::Number(ref n) => Some(n.as_i64()),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
@ -145,84 +224,77 @@ impl fmt::Display for DukError {
|
||||||
|
|
||||||
pub type DukResult<T> = std::result::Result<T, DukError>;
|
pub type DukResult<T> = std::result::Result<T, DukError>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct DukContext {
|
pub struct DukContext {
|
||||||
ctx: *mut duk_context,
|
pub ctx: Option<*mut duk_context>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DukContext {
|
impl DukContext {
|
||||||
fn new() -> DukContext {
|
fn new() -> DukContext {
|
||||||
unsafe {
|
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 {
|
fn get_value(&mut self) -> DukValue {
|
||||||
unsafe {
|
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 {
|
match t as u32 {
|
||||||
DUK_TYPE_NONE => DukValue::Val(Value::default()),
|
DUK_TYPE_NONE => DukValue::Null,
|
||||||
DUK_TYPE_UNDEFINED => DukValue::Val(Value::default()),
|
DUK_TYPE_UNDEFINED => DukValue::Undefined,
|
||||||
DUK_TYPE_NULL => DukValue::Val(Value::Null),
|
DUK_TYPE_NULL => DukValue::Null,
|
||||||
DUK_TYPE_BOOLEAN => DukValue::Val(Value::Bool(duk_get_boolean(self.ctx, -1) == 0)),
|
DUK_TYPE_BOOLEAN => DukValue::Boolean(duk_get_boolean(self.ctx.expect("Invalid context pointer"), -1) == 1),
|
||||||
DUK_TYPE_NUMBER => {
|
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 {
|
if v.fract() > 0_f64 {
|
||||||
match Number::from_f64(v) {
|
DukValue::Number(DukNumber::Float(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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if v.is_nan() {
|
if v.is_nan() {
|
||||||
DukValue::NaN
|
DukValue::Number(DukNumber::NaN)
|
||||||
} else if v.is_infinite() {
|
} else if v.is_infinite() {
|
||||||
DukValue::Infinity
|
DukValue::Number(DukNumber::Infinity)
|
||||||
} else {
|
} else {
|
||||||
DukValue::Val(Value::Number(Number::from(v as i32)))
|
DukValue::Number(DukNumber::Int(v as i64))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DUK_TYPE_STRING => {
|
DUK_TYPE_STRING => {
|
||||||
use std::ffi::CStr;
|
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 t = CStr::from_ptr(v);
|
||||||
let cow = t.to_string_lossy();
|
let cow = t.to_string_lossy();
|
||||||
DukValue::Val(Value::String(String::from(cow)))
|
DukValue::String(String::from(cow))
|
||||||
},
|
},
|
||||||
DUK_TYPE_OBJECT => {
|
DUK_TYPE_OBJECT => {
|
||||||
use std::ffi::CStr;
|
let obj = DukObject::new(self.clone());
|
||||||
let istr = duk_json_encode(self.ctx, -1);
|
DukValue::Object(obj)
|
||||||
let t = CStr::from_ptr(istr);
|
|
||||||
let cow = t.to_string_lossy();
|
|
||||||
DukValue::Val(serde_json::from_str(&String::from(cow)).unwrap())
|
|
||||||
},
|
},
|
||||||
_ => DukValue::None
|
_ => DukValue::Undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn eval_string(&mut self, code: &str) -> DukResult<DukValue> {
|
fn eval_string(&mut self, code: &str) -> DukResult<DukValue> {
|
||||||
unsafe {
|
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();
|
let result = self.get_value();
|
||||||
duk_pop(self.ctx);
|
duk_pop_2(self.ctx.expect("Invalid context pointer"));
|
||||||
Ok(result)
|
Ok(result)
|
||||||
} else {
|
} 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";
|
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();
|
let val = self.get_value();
|
||||||
duk_pop(self.ctx);
|
duk_pop(self.ctx.expect("Invalid context pointer"));
|
||||||
match val.as_str() {
|
match val.as_str() {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
use std::mem;
|
use std::mem;
|
||||||
let c: DukErrorCode = mem::transmute(code);
|
let c: DukErrorCode = mem::transmute(code);
|
||||||
Err(DukError::from(c, v))
|
Err(DukError::from(c, v.as_ref()))
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
Err(DukError::from_code(DukErrorCode::Error))
|
Err(DukError::from_code(DukErrorCode::Error))
|
||||||
|
@ -239,7 +311,42 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_eval_ret() {
|
fn test_eval_ret() {
|
||||||
let mut ctx = DukContext::new();
|
let mut ctx = DukContext::new();
|
||||||
let res = ctx.eval_string("5*5").expect("Eval error!");
|
let mut res = ctx.eval_string("({'ok': true})").expect("Eval error!");
|
||||||
assert_eq!(25, res.as_i64().expect("Value was not an integer!"))
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue