use std::io::{self, Read, Write};
use std::thread;
use conpty_oxide::tokio::Command;
use conpty_oxide::Result;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
const DEFAULT_SHELL: &str = "powershell.exe";
#[tokio::main]
async fn main() -> Result<()> {
let shell = std::env::args().nth(1).unwrap_or_else(|| {
DEFAULT_SHELL.to_string()
});
let parts = Command::new(&shell).spawn()?.into_parts();
let mut child = parts.child;
let mut reader = parts.output;
let mut writer = parts.input;
let _controller = parts.controller;
let output = tokio::spawn(async move {
let mut buf = vec![0u8; 8 * 1024];
let mut stdout = io::stdout();
loop {
let read = reader.read(&mut buf).await?;
if read == 0 {
return io::Result::Ok(());
}
stdout.write_all(&buf[..read])?;
stdout.flush()?;
}
});
let (input_tx, mut input_rx) = tokio::sync::mpsc::channel::<Vec<u8>>(16);
thread::Builder::new()
.name("stdin-relay".into())
.spawn(move || {
let mut stdin = io::stdin();
let mut buf = vec![0u8; 4 * 1024];
while let Ok(read) = stdin.read(&mut buf) {
if read == 0 || input_tx.blocking_send(buf[..read].to_vec()).is_err() {
return;
}
}
})?;
let input = tokio::spawn(async move {
while let Some(chunk) = input_rx.recv().await {
if writer.write_all(&chunk).await.is_err() {
break;
}
}
std::future::pending::<()>().await;
});
let status = child.wait().await?;
output.await.map_err(io::Error::other)??;
input.abort();
match input.await {
Err(error) if error.is_cancelled() => {},
Err(error) => return Err(io::Error::other(error).into()),
Ok(()) => {},
}
println!("{shell} exited: {status}");
Ok(())
}