I want to start a Rust terminal user interface I’ve made using crossterm which needs to handle keyboard input as a NodeJS child process. This works fine when I directly run the Rust program, but results in the runtime error “Failed to initialize input reader” when spawned as a child process from Node.
Minimal reproducible example:
index.js
const { spawn } = require('child_process');
const runMinimalCrossterm = () => {
const rustProcess = spawn('cargo', ['run', '--bin', 'poll'], {
cwd: '/project/path',
stdio: ['pipe', 'pipe', 'pipe'],
});
rustProcess.stdout.on('data', (data) => {
console.log(`Rust says: ${data}`);
});
rustProcess.stderr.on('data', (data) => {
console.error(`Rust error: ${data}`);
});
rustProcess.on('close', (code) => {
console.log(`Rust process exited with code ${code}`);
});
if (rustProcess.stdin) {
rustProcess.stdin.write('qn');
rustProcess.stdin.end();
} else {
console.error("Failed to initialize stdin for the Rust process");
}
};
runMinimalCrossterm();
main.rs
use crossterm::event::{self, Event, KeyCode};
use crossterm::terminal::{enable_raw_mode, disable_raw_mode};
use std::io::{self, Write};
use std::time::Duration;
fn main() -> io::Result<()> {
enable_raw_mode().expect("Failed to enable raw mode");
println!("Polling for events. Press 'q' to exit.");
loop {
match event::poll(Duration::from_secs(10)) {
Ok(true) => {
match event::read() {
Ok(Event::Key(key_event)) => {
println!("Event: {:?}", key_event);
if key_event.code == KeyCode::Char('q') {
println!("Exiting...");
break;
}
}
Ok(event) => {
println!("Event: {:?}", event);
}
Err(e) => {
eprintln!("Error reading event: {}", e);
}
}
}
Ok(false) => {
println!("No events polled within timeout.");
}
Err(e) => {
eprintln!("Failed to poll event: {}", e);
break;
}
}
}
disable_raw_mode().expect("Failed to disable raw mode");
Ok(())
}
Running index.js will result in this output:
Rust error: Running `target/debug/poll`
Rust says: Polling for events. Press 'q' to exit.
Rust error: Failed to poll event: Failed to initialize input reader
Rust process exited with code 0
While running main.rs directly results in output:
Polling for events. Press 'q' to exit.
q
Event: KeyEvent { code: Char('q'), modifiers: KeyModifiers(0x0), kind: Press, state: KeyEventState(0x0) }
Exiting...