From 2c24249a1d9dc37400b7c98e0488119f5e846b11 Mon Sep 17 00:00:00 2001 From: Berzan Date: Sun, 28 Apr 2024 19:37:42 +0000 Subject: [PATCH] create: `nacho-js-process` crate --- Cargo.lock | 9 ++++ Cargo.toml | 2 + js-process/Cargo.toml | 10 ++++ js-process/resources/echo.mjs | 5 ++ js-process/src/error.rs | 12 +++++ js-process/src/js_process.rs | 96 +++++++++++++++++++++++++++++++++++ js-process/src/lib.rs | 5 ++ 7 files changed, 139 insertions(+) create mode 100644 js-process/Cargo.toml create mode 100644 js-process/resources/echo.mjs create mode 100644 js-process/src/error.rs create mode 100644 js-process/src/js_process.rs create mode 100644 js-process/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4cd386a..9e2375e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -926,6 +926,14 @@ dependencies = [ "thiserror", ] +[[package]] +name = "nacho-js-process" +version = "0.1.0" +dependencies = [ + "thiserror", + "tokio", +] + [[package]] name = "nacho-liquidities-db" version = "0.1.0" @@ -981,6 +989,7 @@ dependencies = [ "nacho-burns-db", "nacho-data-structures", "nacho-executor", + "nacho-js-process", "nacho-liquidities-db", "nacho-mempool", "nacho-pools-db", diff --git a/Cargo.toml b/Cargo.toml index da6915a..c6c7862 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ members = [ "schnorr-signature", "proofs-db", "macros", + "js-process", ] [workspace.dependencies] @@ -60,6 +61,7 @@ nacho-processes.path = "processes" nacho-proofpool.path = "proofpool" nacho-proofs-db.path = "proofs-db" nacho-prover.path = "prover" +nacho-js-process.path = "js-process" nacho-rpc-server.path = "rpc-server" nacho-schnorr-signature.path = "schnorr-signature" nacho-withdrawals-db.path = "withdrawals-db" diff --git a/js-process/Cargo.toml b/js-process/Cargo.toml new file mode 100644 index 0000000..87ea0fc --- /dev/null +++ b/js-process/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "nacho-js-process" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tokio.workspace = true +thiserror.workspace = true diff --git a/js-process/resources/echo.mjs b/js-process/resources/echo.mjs new file mode 100644 index 0000000..c1b5722 --- /dev/null +++ b/js-process/resources/echo.mjs @@ -0,0 +1,5 @@ +import { stdin, stdout } from "node:process" + +stdin.on("data", async (chunk) => { + stdout.write(chunk) +}) diff --git a/js-process/src/error.rs b/js-process/src/error.rs new file mode 100644 index 0000000..aac1f61 --- /dev/null +++ b/js-process/src/error.rs @@ -0,0 +1,12 @@ +use thiserror::Error; + +/// The errors that can occur during spawning and interacting with Node.js processes. +#[derive(Error, Debug)] +pub enum JsProcessError { + #[error("data store disconnected")] + Io(#[from] std::io::Error), + #[error("stdout couldn't be taken from the js process")] + Stdout, + #[error("stdin couldn't be taken from the js process")] + Stdin, +} diff --git a/js-process/src/js_process.rs b/js-process/src/js_process.rs new file mode 100644 index 0000000..9908c80 --- /dev/null +++ b/js-process/src/js_process.rs @@ -0,0 +1,96 @@ +use std::process::Stdio; + +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + process::{ChildStdin, ChildStdout, Command}, +}; + +use crate::error::JsProcessError; + +/// Spawns a Node.js process using the given arguments and returns a the standard input and output streams of the process. +/// +/// # Examples +/// +/// Spawn a process: +/// +/// ```rs +/// let (stdin, stdout) = nacho_js_process::spawn(&["echo.js"])?; +/// ``` +/// +pub fn spawn( + args: &[&str], +) -> Result<(&'static mut ChildStdin, &'static mut ChildStdout), JsProcessError> { + let mut process = Command::new("node") + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .kill_on_drop(false) + .spawn()?; + + let stdin = process.stdin.take().ok_or(JsProcessError::Stdin)?; + let stdout = process.stdout.take().ok_or(JsProcessError::Stdout)?; + + Ok((Box::leak(Box::new(stdin)), Box::leak(Box::new(stdout)))) +} + +/// Writes the given input to the standard input stream of the process. +/// +/// And reads the standart output of the process to the given output. +/// +/// # Examples +/// +/// Spawn a process: +/// +/// ```rs +/// let (stdin, stdout) = nacho_js_process::spawn(&["greeting.js"])?; +/// ``` +/// +/// Interact with the process: +/// +/// ```rs +/// let input = b"Berzan"; +/// let mut output = [0u8; 11]; +/// nacho_js_process::interact(stdin, stdout, &input, &mut output).await?; +/// assert_eq!(output, b"Hi, Berzan!"); +/// ``` +/// +pub async fn interact( + stdin: &mut ChildStdin, + stdout: &mut ChildStdout, + input: &[u8], + output: &mut [u8], +) -> Result<(), JsProcessError> { + stdin.write_all(input).await?; + stdout.read_exact(output).await?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + pub async fn test_echo_js_process() { + let js_file_path = concat!(env!("CARGO_MANIFEST_DIR"), "/resources/echo.mjs"); + + let (stdin, stdout) = spawn(&[js_file_path]).unwrap(); + + // First try: + let input = [111u8; 5]; + let mut output = [0u8; 5]; + + interact(stdin, stdout, &input, &mut output).await.unwrap(); + + assert_eq!(output, input); + + // Second try: + let input = [222u8; 40]; + let mut output = [0u8; 40]; + + interact(stdin, stdout, &input, &mut output).await.unwrap(); + + assert_eq!(output, input); + } +} diff --git a/js-process/src/lib.rs b/js-process/src/lib.rs new file mode 100644 index 0000000..85b1e60 --- /dev/null +++ b/js-process/src/lib.rs @@ -0,0 +1,5 @@ +mod error; +mod js_process; + +pub use error::JsProcessError; +pub use js_process::{interact, spawn};