js_sandbox/lib.rs
1// Copyright (c) 2020-2021 Jan Haller. zlib/libpng license.
2
3// Note: the crate documentation is copied to ReadMe.md using cargo-readme: cargo readme --no-license > ReadMe.md
4// Alternatives:
5// * once stable: #![feature(external_doc)] #![doc(include = "../ReadMe.md")]
6// * doc_comment crate + doctest!("../ReadMe.md"); -- works for running doc-tests, but not for doc on crate level
7
8//! `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]
9//! for serialization.
10//!
11//!
12//! This library's primary focus is **embedding JS as a scripting language into Rust**. It does not provide all possible integrations between the two
13//! languages, and is not tailored to JS's biggest domain as a client/server side language of the web.
14//!
15//! Instead, `js-sandbox` focuses on calling standalone JS code from Rust, and tries to remain as simple as possible in doing so.
16//! 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
17//! external mods.
18//!
19//! This library is in early development, with a basic but powerful API. The API may still evolve considerably.
20//!
21//! # Examples
22//!
23//! ## Print from JavaScript
24//!
25//! The _Hello World_ example -- print something using JavaScript -- is one line, as it should be:
26//! ```rust
27//! fn main() {
28//! js_sandbox::eval_json("console.log('Hello Rust from JS')").expect("JS runs");
29//! }
30//! ```
31//!
32//! ## Call a JS function
33//!
34//! A very basic application calls a JavaScript function `triple()` from Rust. It passes an argument and accepts a return value, both serialized via JSON:
35//!
36//! ```rust
37//! use js_sandbox::{Script, AnyError};
38//!
39//! fn main() -> Result<(), AnyError> {
40//! let js_code = "function triple(a) { return 3 * a; }";
41//! let mut script = Script::from_string(js_code)?;
42//!
43//! let arg = 7;
44//! let result: i32 = script.call("triple", &arg)?;
45//!
46//! assert_eq!(result, 21);
47//! Ok(())
48//! }
49//! ```
50//!
51//! An example that serializes a JSON object (Rust -> JS) and formats a string (JS -> Rust):
52//!
53//! ```rust
54//! use js_sandbox::{Script, AnyError};
55//! use serde::Serialize;
56//!
57//! #[derive(Serialize, PartialEq)]
58//! struct Person {
59//! name: String,
60//! age: u8,
61//! }
62//!
63//! fn main() -> Result<(), AnyError> {
64//! let src = r#"
65//! function toString(person) {
66//! return "A person named " + person.name + " of age " + person.age;
67//! }"#;
68//!
69//! let mut script = Script::from_string(src)
70//! .expect("Initialization succeeds");
71//!
72//! let person = Person { name: "Roger".to_string(), age: 42 };
73//! let result: String = script.call("toString", &person).unwrap();
74//!
75//! assert_eq!(result, "A person named Roger of age 42");
76//! Ok(())
77//! }
78//! ```
79//!
80//! ## Maintain state in JavaScript
81//!
82//! It is possible to initialize a stateful JS script, and then use functions to modify that state over time.
83//! This example appends a string in two calls, and then gets the result in a third call:
84//!
85//! ```rust
86//! use js_sandbox::{Script, AnyError};
87//!
88//! fn main() -> Result<(), AnyError> {
89//! let src = r#"
90//! var total = '';
91//! function append(str) { total += str; }
92//! function get() { return total; }"#;
93//!
94//! let mut script = Script::from_string(src)
95//! .expect("Initialization succeeds");
96//!
97//! let _: () = script.call("append", &"hello").unwrap();
98//! let _: () = script.call("append", &" world").unwrap();
99//! let result: String = script.call("get", &()).unwrap();
100//!
101//! assert_eq!(result, "hello world");
102//! Ok(())
103//! }
104//! ```
105//!
106//! [Deno]: https://deno.land/
107//! [serde_json]: https://docs.serde.rs/serde_json
108
109
110pub use script::Script;
111pub use util::eval_json;
112
113/// Represents a value passed to or from JavaScript.
114///
115/// Currently aliased as serde_json's Value type.
116pub type JsValue = serde_json::Value;
117
118/// Polymorphic error type able to represent different error domains.
119///
120/// Currently reusing [anyhow::Error](../anyhow/enum.Error.html), this type may change slightly in the future depending on js-sandbox's needs.
121// use through deno_core, to make sure same version of anyhow crate is used
122pub type AnyError = deno_core::error::AnyError;
123
124
125mod script;
126mod util;