1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright (c) 2020-2021 Jan Haller. zlib/libpng license.

// Note: the crate documentation is copied to ReadMe.md using cargo-readme:  cargo readme --no-license > ReadMe.md
// Alternatives:
// * once stable: #![feature(external_doc)] #![doc(include = "../ReadMe.md")]
// * doc_comment crate + doctest!("../ReadMe.md");  -- works for running doc-tests, but not for doc on crate level

//! `js-sandbox` is a Rust library for executing JavaScript code from Rust in a secure sandbox. It is based on the [Deno] project and uses [serde_json]
//! for serialization.
//!
//!
//! This library's primary focus is **embedding JS as a scripting language into Rust**. It does not provide all possible integrations between the two
//! languages, and is not tailored to JS's biggest domain as a client/server side language of the web.
//!
//! Instead, `js-sandbox` focuses on calling standalone JS code from Rust, and tries to remain as simple as possible in doing so.
//! The typical use case is a core Rust application that integrates with scripts from external users, for example a plugin system or a game that runs
//! external mods.
//!
//! This library is in early development, with a basic but powerful API. The API may still evolve considerably.
//!
//! # Examples
//!
//! ## Print from JavaScript
//!
//! The _Hello World_ example -- print something using JavaScript -- is one line, as it should be:
//! ```rust
//! fn main() {
//! 	js_sandbox::eval_json("console.log('Hello Rust from JS')").expect("JS runs");
//! }
//! ```
//!
//! ## Call a JS function
//!
//! A very basic application calls a JavaScript function `triple()` from Rust. It passes an argument and accepts a return value, both serialized via JSON:
//!
//! ```rust
//! use js_sandbox::{Script, AnyError};
//!
//! fn main() -> Result<(), AnyError> {
//! 	let js_code = "function triple(a) { return 3 * a; }";
//! 	let mut script = Script::from_string(js_code)?;
//!
//! 	let arg = 7;
//! 	let result: i32 = script.call("triple", &arg)?;
//!
//! 	assert_eq!(result, 21);
//! 	Ok(())
//! }
//! ```
//!
//! An example that serializes a JSON object (Rust -> JS) and formats a string (JS -> Rust):
//!
//! ```rust
//! use js_sandbox::{Script, AnyError};
//! use serde::Serialize;
//!
//! #[derive(Serialize, PartialEq)]
//! struct Person {
//! 	name: String,
//! 	age: u8,
//! }
//!
//! fn main() -> Result<(), AnyError> {
//! 	let src = r#"
//! 		function toString(person) {
//! 			return "A person named " + person.name + " of age " + person.age;
//! 		}"#;
//!
//! 	let mut script = Script::from_string(src)
//! 		.expect("Initialization succeeds");
//!
//! 	let person = Person { name: "Roger".to_string(), age: 42 };
//! 	let result: String = script.call("toString", &person).unwrap();
//!
//! 	assert_eq!(result, "A person named Roger of age 42");
//! 	Ok(())
//! }
//! ```
//!
//! ## Maintain state in JavaScript
//!
//! It is possible to initialize a stateful JS script, and then use functions to modify that state over time.
//! This example appends a string in two calls, and then gets the result in a third call:
//!
//! ```rust
//! use js_sandbox::{Script, AnyError};
//!
//! fn main() -> Result<(), AnyError> {
//! 	let src = r#"
//! 		var total = '';
//! 		function append(str) { total += str; }
//! 		function get()       { return total; }"#;
//!
//! 	let mut script = Script::from_string(src)
//! 		.expect("Initialization succeeds");
//!
//! 	let _: () = script.call("append", &"hello").unwrap();
//! 	let _: () = script.call("append", &" world").unwrap();
//! 	let result: String = script.call("get", &()).unwrap();
//!
//! 	assert_eq!(result, "hello world");
//! 	Ok(())
//! }
//! ```
//!
//! [Deno]: https://deno.land/
//! [serde_json]: https://docs.serde.rs/serde_json


pub use script::Script;
pub use util::eval_json;

/// Represents a value passed to or from JavaScript.
///
/// Currently aliased as serde_json's Value type.
pub type JsValue = serde_json::Value;

/// Polymorphic error type able to represent different error domains.
///
/// Currently reusing [anyhow::Error](../anyhow/enum.Error.html), this type may change slightly in the future depending on js-sandbox's needs.
// use through deno_core, to make sure same version of anyhow crate is used
pub type AnyError = deno_core::error::AnyError;


mod script;
mod util;