use core::cell::RefCell;
use std::io::{BufRead, BufWriter, Error, ErrorKind, Read, Result, Write};
use std::sync::{Mutex, MutexGuard};
use crate::opt::{Options, Volume};
use crate::read::{DoggedReader, VerboseReader};
use crate::scan::Scanner;
use crate::sys::{RawConfig, RawConnection, RawOutput};
use crate::{Command, Scan};
#[derive(Debug)]
struct DeferredWriter {
writer: BufWriter<RawOutput>,
deferred: RefCell<Vec<Box<dyn Command + Send>>>,
}
impl DeferredWriter {
pub fn new(writer: RawOutput, options: &Options) -> Self {
Self {
writer: BufWriter::with_capacity(options.write_buffer_size(), writer),
deferred: RefCell::new(Vec::new()),
}
}
pub fn defer<C>(&self, cmd: C)
where
C: Command + Send + 'static,
{
self.deferred.borrow_mut().push(Box::new(cmd));
}
pub fn take(&self) -> Vec<Box<dyn Command + Send>> {
self.deferred.take()
}
}
impl Write for DeferredWriter {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.writer.write(buf)
}
fn flush(&mut self) -> Result<()> {
self.writer.flush()
}
}
#[derive(Debug)]
pub struct Connection {
options: Options,
stamp: u32,
config: Option<RawConfig>,
scanner: Mutex<Scanner<Box<dyn Read + Send>>>,
writer: Mutex<DeferredWriter>,
connection: RawConnection,
}
fn _assert_connection_is_send_sync() {
fn is_send_sync<T: Send + Sync>() {}
is_send_sync::<Connection>();
}
impl Connection {
pub fn open() -> Result<Self> {
Self::with_options(Options::default())
}
#[allow(clippy::print_stdout)]
pub fn with_options(options: Options) -> Result<Self> {
let connection = RawConnection::open(&options)
.map_err(|e| Error::new(ErrorKind::ConnectionRefused, e))?;
let config = RawConfig::read(&connection)?;
let verbose = !matches!(options.volume(), Volume::Silent);
if verbose {
println!("terminal::config {:?}", &config);
}
let config = config.apply(&options).map_or_else(
|| Ok::<Option<RawConfig>, Error>(None),
|reconfig| {
if verbose {
println!("terminal::reconfig {:?}", &reconfig);
}
reconfig.write(&connection)?;
if verbose {
print!("terminal::reconfigured\r\n")
}
Ok(Some(config))
},
)?;
let reader: Box<dyn Read + Send> = if matches!(options.volume(), Volume::Detailed) {
Box::new(VerboseReader::new(connection.input(), options.timeout()))
} else {
Box::new(DoggedReader::new(connection.input()))
};
let scanner = Mutex::new(Scanner::with_options(&options, reader));
let writer = Mutex::new(DeferredWriter::new(connection.output(), &options));
let stamp = if verbose {
std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.map(|d| d.subsec_micros())
.unwrap_or(0)
} else {
0
};
let this = Self {
options,
stamp,
config,
scanner,
writer,
connection,
};
this.log("terminal::connect")?;
Ok(this)
}
#[inline]
pub fn options(&self) -> &Options {
&self.options
}
#[inline]
pub fn io(&self) -> (Input<'_>, Output<'_>) {
(self.input(), self.output())
}
#[inline]
pub fn input(&self) -> Input<'_> {
Input {
scanner: self.scanner.lock().expect("can't lock poisoned mutex"),
}
}
#[inline]
pub fn output(&self) -> Output<'_> {
Output {
writer: self.writer.lock().expect("can't lock poisoned mutex"),
}
}
fn log(&self, message: impl AsRef<str>) -> Result<()> {
if !matches!(self.options.volume(), Volume::Silent) {
let mut writer = self
.writer
.try_lock()
.map_err(|_| Error::from(ErrorKind::WouldBlock))?;
write!(
writer,
"{} pid={} group={} stamp={}\r\n",
message.as_ref(),
std::process::id(),
self.connection.group().unwrap_or(0),
self.stamp
)?;
writer.flush()
} else {
Ok(())
}
}
}
impl Drop for Connection {
fn drop(&mut self) {
let _ = self.log("terminal::disconnect");
let _ = self.writer.lock().map(|mut writer| {
for cmd in writer.take().into_iter().rev() {
let _ = write!(writer, "{}", cmd);
}
let _ = writer.flush();
});
if let Some(ref cfg) = self.config {
let _ = cfg.write(&self.connection);
}
}
}
#[derive(Debug)]
pub struct Input<'a> {
pub scanner: MutexGuard<'a, Scanner<Box<dyn Read + Send>>>,
}
impl Input<'_> {
#[must_use = "the only reason to invoke method is to access the returned value"]
pub fn is_readable(&self) -> bool {
self.scanner.is_readable()
}
}
impl Scan for Input<'_> {
#[inline]
fn in_flight(&self) -> bool {
self.scanner.in_flight()
}
#[inline]
fn read_token(&mut self) -> Result<crate::Token<'_>> {
self.scanner.read_token().map_err(core::convert::Into::into)
}
}
impl Read for Input<'_> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let mut source = self.scanner.fill_buf()?;
let count = source.read(buf)?;
self.scanner.consume(count)?;
Ok(count)
}
}
impl BufRead for Input<'_> {
#[inline]
fn fill_buf(&mut self) -> Result<&[u8]> {
self.scanner.fill_buf().map_err(core::convert::Into::into)
}
#[inline]
fn consume(&mut self, amt: usize) {
let _ = self.scanner.consume(amt);
}
}
#[derive(Debug)]
pub struct Output<'a> {
writer: MutexGuard<'a, DeferredWriter>,
}
impl Output<'_> {
#[inline]
#[must_use = "method returns result that may indicate an error"]
pub fn print<T: AsRef<str>>(&mut self, text: T) -> Result<()> {
self.writer.write_all(text.as_ref().as_bytes())?;
self.writer.flush()
}
#[inline]
#[must_use = "method returns result that may indicate an error"]
pub fn println<T: AsRef<str>>(&mut self, text: T) -> Result<()> {
self.writer.write_all(text.as_ref().as_bytes())?;
self.writer.write_all(b"\r\n")?;
self.writer.flush()
}
#[inline]
#[must_use = "method returns result that may indicate an error"]
pub fn exec<C: Command>(&mut self, cmd: C) -> Result<()> {
write!(self.writer, "{}", cmd)?;
self.writer.flush()
}
#[must_use = "method returns result that may indicate an error"]
pub fn exec_defer<C1, C2>(&mut self, cmd1: C1, cmd2: C2) -> Result<()>
where
C1: Command,
C2: Command + Send + 'static,
{
write!(self.writer, "{}", cmd1)?;
self.writer.defer(cmd2);
self.writer.flush()
}
}
impl Write for Output<'_> {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.writer.write(buf)
}
#[inline]
fn flush(&mut self) -> Result<()> {
self.writer.flush()
}
}