js-promises 0.1.0

Bindings for JS promises
Documentation
//! Functions for passing arbitrary Rust values across the JS boundary and back to Rust, ex. as a return value from a promise.

use stdweb::{
	self,
	js,
	unstable::TryFrom,
};

/// Boxes an object, returning a JS wrapper that `js_unbox` can consume to turn back into the object.
/// 
/// The contents of the return value should be untouched, both by rust code and by JS code, for
/// safe usage with `js_unbox`.
pub fn js_box<T>(v: T) -> stdweb::Value {
	let ptr = Box::into_raw(Box::new(v)) as usize;
	let ptr_js = stdweb::Value::try_from(ptr).expect("Could not convert ptr to js number");
	let v = js!{
		return {"_ptr": @{ptr_js}};
	};
	v
}

/// Unboxes a value created by `js_box`, returning the original object.
/// 
/// Returns `Some(v)` if the unboxing succeeded, or `None` if the value was not a valid box object
/// or it was already unboxed.
/// 
/// This mutates the object, so that calling `js_unbox` on the same object will return `None`.
/// 
/// Safety
/// ------
/// 
/// Assumes that the object has been untouched since being returned from `js_box` or `js_box_from_std_box`,
/// and that the type argument is the same as the one used to box it.
/// 
/// JS or Rust code could modify the object or construct a JS object with an invalid internal pointer,
/// which this function can't check for. In such a case, the result is undefined.
/// 
/// If the value is sent across a thread, the caller needs to ensure that `T` implements `Send`
/// (TODO: is this even possible with wasm?).
/// 
/// The lifetime of `T` must still be valid. If in doubt, require `'static`.
pub unsafe fn js_unbox<T>(v: stdweb::Value) -> Option<T> {
	let v_num_obj = v
		.into_reference()?
		.downcast::<stdweb::Object>()?
		.to_iter()
		.filter(|&(ref k, ref _v)| k == "_ptr")
		.next()?
		.1
	;
	
	let v_num = match v_num_obj {
		stdweb::Value::Number(n) => n,
		_ => { return None; }
	};
	
	let v_ptr = usize::try_from(v_num).ok()? as *mut T;
	Some(*Box::from_raw(v_ptr))
}