use std::io;
use std::os::windows::io::{IntoRawHandle, OwnedHandle};
use std::sync::Arc;
use ::tokio::net::windows::named_pipe::NamedPipeServer;
use crate::backend::ConPtyBackend;
use crate::core::options::PtyOptions;
use crate::core::pipes::{create_overlapped_pipes, OverlappedPipes};
use crate::core::pseudocon::PseudoConsole;
use crate::core::session::Session as SessionCore;
use crate::error::{Error, Result};
use crate::size::Size;
use super::pty::Pty;
use super::pty::{ConinWriter, ConoutReader};
#[derive(Debug, Clone, Default)]
pub(crate) struct PtyBuilder {
options: PtyOptions,
}
impl PtyBuilder {
#[must_use]
pub(crate) const fn size(mut self, size: Size) -> Self {
self.options.size = size;
self
}
#[must_use]
pub(crate) fn backend(mut self, backend: ConPtyBackend) -> Self {
self.options.backend = Some(backend);
self
}
#[must_use]
#[cfg(test)]
pub(crate) const fn inherit_cursor(mut self, inherit: bool) -> Self {
self.options.inherit_cursor = inherit;
self
}
#[must_use]
#[cfg(test)]
pub(crate) const fn eof_on_root_exit(mut self, eof: bool) -> Self {
self.options.eof_on_root_exit = eof;
self
}
pub(crate) fn build(self) -> Result<Pty> {
let backend = match self.options.backend {
Some(backend) => backend,
None => ConPtyBackend::resolve_default()?,
};
if ::tokio::runtime::Handle::try_current().is_err() {
return Err(Error::create_console(io::Error::other(
"an async Pty must be built from within a Tokio runtime: its \
pipes are registered with the runtime's I/O driver",
)));
}
let OverlappedPipes {
conout_server,
conout_client,
conin_server,
conin_client,
} = create_overlapped_pipes().map_err(Error::create_console)?;
let conout = register(conout_server).map_err(Error::create_console)?;
let conin = register(conin_server).map_err(Error::create_console)?;
let console = PseudoConsole::new(
backend,
self.options.size,
conin_client,
conout_client,
self.options.inherit_cursor,
)
.map_err(Error::create_console)?;
let shared = Arc::clone(console.shared());
let inner = Arc::new(SessionCore::new(console, self.options.eof_on_root_exit));
Ok(Pty {
reader: ConoutReader::new(conout, shared),
writer: ConinWriter::new(conin, Arc::clone(&inner)),
inner,
})
}
}
fn register(handle: OwnedHandle) -> io::Result<NamedPipeServer> {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
let raw = handle.into_raw_handle();
unsafe { NamedPipeServer::from_raw_handle(raw) }
}))
.unwrap_or_else(|_| {
Err(io::Error::other(
"the Tokio runtime's I/O driver is disabled; enable it before \
building an async Pty",
))
})
}