-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d68fb81
commit 71c2497
Showing
1 changed file
with
125 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. | ||
//! This example shows you how to define ops in Rust and then call them from | ||
//! JavaScript. | ||
use anyhow::anyhow; | ||
use deno_core::json_op_sync; | ||
use deno_core::JsRuntime; | ||
use deno_core::Op; | ||
use serde_json::Value; | ||
use std::io::Write; | ||
|
||
fn main() { | ||
// Initialize a runtime instance | ||
let mut runtime = JsRuntime::new(Default::default()); | ||
|
||
// The first thing we do is define two ops. They will be used to show how to | ||
// pass data to Rust and back to JavaScript. | ||
// | ||
// The first one is used to print data to stdout, because by default the | ||
// JavaScript console functions are just stubs (they don't do anything). | ||
// | ||
// The second one just transforms some input and returns it to JavaScript. | ||
|
||
// Register the op for outputting bytes to stdout. | ||
// It can be invoked with Deno.core.dispatch and the id this method returns | ||
// or Deno.core.dispatchByName and the name provided. | ||
runtime.register_op( | ||
"op_print", | ||
// The op_fn callback takes a state object OpState | ||
// and a vector of ZeroCopyBuf's, which are mutable references | ||
// to ArrayBuffer's in JavaScript. | ||
|_state, zero_copy| { | ||
let mut out = std::io::stdout(); | ||
|
||
// Write the contents of every buffer to stdout | ||
for buf in zero_copy { | ||
out.write_all(&buf).unwrap(); | ||
} | ||
|
||
Op::Sync(Box::new([])) // No meaningful result | ||
}, | ||
); | ||
|
||
// Register the JSON op for summing a number array. | ||
// A JSON op is just an op where the first ZeroCopyBuf is a serialized JSON | ||
// value, the return value is also a serialized JSON value. It can be invoked | ||
// with Deno.core.jsonOpSync and the name. | ||
runtime.register_op( | ||
"op_sum", | ||
// The json_op_sync function automatically deserializes | ||
// the first ZeroCopyBuf and serializes the return value | ||
// to reduce boilerplate | ||
json_op_sync(|_state, json, zero_copy| { | ||
// We check that we only got the JSON value, | ||
// and that it's of the right type. | ||
if !zero_copy.is_empty() { | ||
Err(anyhow!("Expected exactly one argument")) | ||
} else if !json.is_array() { | ||
Err(anyhow!("Argument is not of type array")) | ||
} else if !json | ||
.as_array() | ||
.unwrap() | ||
.iter() | ||
.all(|value| value.is_number()) | ||
{ | ||
Err(anyhow!("Argument is not array of numbers")) | ||
} else { | ||
// And if everything checks out we do our actual task | ||
let sum = json | ||
.as_array() | ||
.unwrap() | ||
.iter() | ||
.fold(0.0, |a, v| a + v.as_f64().unwrap()); | ||
|
||
// Finally we return a JSON value | ||
Ok(Value::from(sum)) | ||
} | ||
}), | ||
); | ||
|
||
// Now we see how to invoke the ops we just defined. The runtime automatically | ||
// contains a Deno.core object with several functions for interacting with it. | ||
// You can find its definition in core.js. | ||
runtime.execute( | ||
"<init>", | ||
r#" | ||
// First we initialize the ops cache. | ||
// This maps op names to their id's. | ||
Deno.core.ops(); | ||
// Then we define a print function that uses | ||
// our op_print op to display the stringified argument. | ||
const _newline = new Uint8Array([10]); | ||
function print(value) { | ||
Deno.core.dispatchByName('op_print', Deno.core.encode(value.toString()), _newline); | ||
} | ||
// Finally we register the error class used by op_sum | ||
// so that it throws the correct class. | ||
Deno.core.registerErrorClass('Error', Error); | ||
"#, | ||
).unwrap(); | ||
|
||
// Now we can finally use this in an example. | ||
runtime | ||
.execute( | ||
"<usage>", | ||
r#" | ||
const arr = [1, 2, 3]; | ||
print("The sum of"); | ||
print(arr); | ||
print("is"); | ||
print(Deno.core.jsonOpSync('op_sum', arr)); | ||
// And incorrect usage | ||
try { | ||
print(Deno.core.jsonOpSync('op_sum', 0)); | ||
} catch(e) { | ||
print('Exception:'); | ||
print(e); | ||
} | ||
"#, | ||
) | ||
.unwrap(); | ||
} |