More wrapping.
OBJECTS! Update readme and version.
This commit is contained in:
parent
39eeb57ac3
commit
a0b5afe1eb
3 changed files with 162 additions and 44 deletions
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "duktape-rs"
|
name = "duktape-rs"
|
||||||
version = "0.0.1"
|
version = "0.0.2"
|
||||||
authors = ["envis10n <envis10n@protonmail.com>"]
|
authors = ["envis10n <envis10n@protonmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
repository = "https://gitlab.com/envis10n/duktape-rs"
|
repository = "https://gitlab.com/envis10n/duktape-rs"
|
||||||
|
|
50
README.md
50
README.md
|
@ -1,7 +1,51 @@
|
||||||
# duktape-rs
|
# duktape-rs
|
||||||
|
|
||||||
Safe(er) rust wrapper for dukbind.
|
*Safe(er) rust wrapper for dukbind.*
|
||||||
|
|
||||||
This is currently a work in progress.
|
## Work In Progress
|
||||||
|
This library is a work in progress and is currently limited in features.
|
||||||
|
|
||||||
Returned values from eval are a wrapper for serde_json::Value (as DukValue::Val(serde_json::Value)) that also provide None, NaN, and Infinity.
|
## What can it do?
|
||||||
|
At the moment, duktape-rs
|
||||||
|
|
||||||
|
- [x] Provides a safe* wrapper around dukbind (raw FFI bindings for duktape).
|
||||||
|
- [x] Provides manageable value returns that can be modified and passed back to the duktape context.
|
||||||
|
- [x] Supports heap pointers (for objects), including setting and getting properties of an object (as DukValue).
|
||||||
|
- [x] Can eval a &str and return the result (DukResult<DukValue, DukError>)
|
||||||
|
- [x] Supports handling (what I assume to be) most JS errors that crop up during eval *minimally tested*
|
||||||
|
|
||||||
|
*Safety not guaranteed
|
||||||
|
## Where are the docs?
|
||||||
|
For some reason docs.rs has a problem with compiling dukbind and won't generate them :/
|
||||||
|
Check back another time for documentation *Coming Soon™*
|
||||||
|
|
||||||
|
## Basics
|
||||||
|
|
||||||
|
extern crate duktape-rs;
|
||||||
|
|
||||||
|
use duktape-rs::DukContext;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Create a new context
|
||||||
|
let mut ctx = DukContext::new();
|
||||||
|
|
||||||
|
// Eval 5+5
|
||||||
|
let val = ctx.eval_string("5+5").unwrap();
|
||||||
|
|
||||||
|
// Destroy the heap (do this when you are done using the context)
|
||||||
|
ctx.destroy();
|
||||||
|
|
||||||
|
// Compare the value as an i64 against 10
|
||||||
|
assert_eq!(val.as_i64().expect("Not an i64"), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
## Objects
|
||||||
|
Objects in duktape are returned as heap pointers that have to be stored and returned as a wrapper around that pointer.
|
||||||
|
|
||||||
|
let mut ctx = DukContext::new();
|
||||||
|
|
||||||
|
let obj = ctx.eval_string("({ok: true})").unwrap().as_object().expect("Not an object");
|
||||||
|
|
||||||
|
let val = obj.get_prop("ok").unwrap().as_bool().expect("Not a bool");
|
||||||
|
|
||||||
|
println!("Value: {}", val); //-> Value: true
|
||||||
|
|
150
src/lib.rs
150
src/lib.rs
|
@ -16,7 +16,8 @@ pub enum DukErrorCode {
|
||||||
Range = DUK_ERR_RANGE_ERROR,
|
Range = DUK_ERR_RANGE_ERROR,
|
||||||
Syntax = DUK_ERR_SYNTAX_ERROR,
|
Syntax = DUK_ERR_SYNTAX_ERROR,
|
||||||
Type = DUK_ERR_TYPE_ERROR,
|
Type = DUK_ERR_TYPE_ERROR,
|
||||||
URI = DUK_ERR_URI_ERROR
|
URI = DUK_ERR_URI_ERROR,
|
||||||
|
NullPtr
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -82,6 +83,29 @@ pub struct DukObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DukObject {
|
impl DukObject {
|
||||||
|
/// Encode to JSON string.
|
||||||
|
pub fn encode(&mut self) -> Option<String> {
|
||||||
|
unsafe {
|
||||||
|
match self.context.ctx {
|
||||||
|
Some(ctx) => {
|
||||||
|
let idx = duk_push_heapptr(ctx, self.heap);
|
||||||
|
if duk_is_undefined(ctx, idx) == 0 {
|
||||||
|
duk_dup(ctx, idx);
|
||||||
|
let raw = duk_json_encode(ctx, -1);
|
||||||
|
use std::ffi::CStr;
|
||||||
|
let t = CStr::from_ptr(raw);
|
||||||
|
let cow = t.to_string_lossy();
|
||||||
|
duk_pop_2(ctx);
|
||||||
|
Some(String::from(cow))
|
||||||
|
} else {
|
||||||
|
duk_pop(ctx);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn get_prop(&mut self, name: &str) -> DukResult<DukValue> {
|
pub fn get_prop(&mut self, name: &str) -> DukResult<DukValue> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ctx = self.context.ctx.expect("Invalid context pointer.");
|
let ctx = self.context.ctx.expect("Invalid context pointer.");
|
||||||
|
@ -95,6 +119,59 @@ impl DukObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn set_prop(&mut self, name: &str, value: DukValue) -> DukResult<()> {
|
||||||
|
match self.context.ctx {
|
||||||
|
Some(ctx) => {
|
||||||
|
unsafe {
|
||||||
|
duk_push_heapptr(ctx, self.heap);
|
||||||
|
if duk_is_undefined(ctx, -1) == 0 {
|
||||||
|
let mut ok: bool = true;
|
||||||
|
match value {
|
||||||
|
DukValue::Undefined => duk_push_undefined(ctx),
|
||||||
|
DukValue::Null => duk_push_null(ctx),
|
||||||
|
DukValue::Number(ref n) => {
|
||||||
|
if n.is_nan() {
|
||||||
|
duk_push_nan(ctx);
|
||||||
|
} else if n.is_infinity() {
|
||||||
|
duk_push_lstring(ctx, "Infinity".as_ptr() as *const i8, "Infinity".len() as duk_size_t);
|
||||||
|
} else {
|
||||||
|
duk_push_number(ctx, n.as_f64());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DukValue::Boolean(b) => duk_push_boolean(ctx, value.as_duk_bool().expect("Not a boolean!")),
|
||||||
|
DukValue::String(s) => {
|
||||||
|
let t = &s;
|
||||||
|
duk_push_lstring(ctx, t.as_ptr() as *const i8, t.len() as duk_size_t);
|
||||||
|
},
|
||||||
|
DukValue::Object(ref o) => {
|
||||||
|
duk_push_heapptr(ctx, o.heap);
|
||||||
|
if duk_is_undefined(ctx, -1) == 1 {
|
||||||
|
duk_pop(ctx);
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if ok {
|
||||||
|
if duk_put_prop_lstring(ctx, -2, name.as_ptr() as *const i8, name.len() as duk_size_t) == 1 {
|
||||||
|
duk_pop_2(ctx);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
duk_pop_2(ctx);
|
||||||
|
Err(DukError::from(DukErrorCode::Error, "Failed to set prop."))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
duk_pop(ctx);
|
||||||
|
Err(DukError::from(DukErrorCode::Error, "Error setting prop."))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
duk_pop(ctx);
|
||||||
|
Err(DukError::from(DukErrorCode::NullPtr, "Invalid heap pointer."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => Err(DukError::from(DukErrorCode::NullPtr, "Invalid context pointer."))
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn new(context: DukContext) -> DukObject {
|
pub fn new(context: DukContext) -> DukObject {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ctx = context.ctx.expect("Invalid context pointer.");
|
let ctx = context.ctx.expect("Invalid context pointer.");
|
||||||
|
@ -129,6 +206,18 @@ impl DukValue {
|
||||||
DukValue::Object(ref _o) => Some(String::from("[object]"))
|
DukValue::Object(ref _o) => Some(String::from("[object]"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn as_duk_bool(&self) -> Option<duk_bool_t> {
|
||||||
|
match self {
|
||||||
|
DukValue::Boolean(b) => {
|
||||||
|
if *b {
|
||||||
|
Some(1)
|
||||||
|
} else {
|
||||||
|
Some(0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn as_bool(&self) -> Option<bool> {
|
pub fn as_bool(&self) -> Option<bool> {
|
||||||
match self {
|
match self {
|
||||||
DukValue::Boolean(b) => Some(*b),
|
DukValue::Boolean(b) => Some(*b),
|
||||||
|
@ -167,6 +256,12 @@ impl DukValue {
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn is_bool(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
DukValue::Boolean(_b) => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn as_i64(&self) -> Option<i64> {
|
pub fn as_i64(&self) -> Option<i64> {
|
||||||
match self {
|
match self {
|
||||||
DukValue::Number(ref n) => Some(n.as_i64()),
|
DukValue::Number(ref n) => Some(n.as_i64()),
|
||||||
|
@ -241,6 +336,18 @@ impl DukContext {
|
||||||
self.ctx = None;
|
self.ctx = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn decode_json(&mut self, json: &str) -> DukValue {
|
||||||
|
match self.ctx {
|
||||||
|
Some(ctx) => {
|
||||||
|
unsafe {
|
||||||
|
duk_push_lstring(ctx, json.as_ptr() as *const i8, json.len() as duk_size_t);
|
||||||
|
duk_json_decode(ctx, -1);
|
||||||
|
self.get_value()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => DukValue::Undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
fn get_value(&mut self) -> DukValue {
|
fn get_value(&mut self) -> DukValue {
|
||||||
unsafe {
|
unsafe {
|
||||||
let t = duk_get_type(self.ctx.expect("Invalid context pointer"), -1);
|
let t = duk_get_type(self.ctx.expect("Invalid context pointer"), -1);
|
||||||
|
@ -310,43 +417,10 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_eval_ret() {
|
fn test_eval_ret() {
|
||||||
|
// Create a new context
|
||||||
let mut ctx = DukContext::new();
|
let mut ctx = DukContext::new();
|
||||||
let mut res = ctx.eval_string("({'ok': true})").expect("Eval error!");
|
// Eval 5+5
|
||||||
let o: &mut DukObject = res.as_object().expect("Should be an object here.");
|
let val = ctx.eval_string("5+5").unwrap();
|
||||||
match o.get_prop("ok") {
|
assert_eq!(val.as_i64().expect("Not an i64"), 10)
|
||||||
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