use std::borrow::Cow;
use std::fmt;
use std::fs::File;
use std::io::{self, BufRead, BufReader, BufWriter, Write as IoWrite};
use std::path::Path;
use std::sync::{Arc, Mutex, MutexGuard};
use std::time::Duration;
use crate::command::Command;
use crate::complete::{Completer};
use crate::function::Function;
use crate::inputrc::Directive;
use crate::reader::{Read, Reader, ReadLock, ReadResult};
use crate::terminal::{DefaultTerminal, Signal, Terminal};
use crate::variables::Variable;
use crate::writer::{Write, Writer, WriteLock};
pub struct Interface<Term: Terminal> {
term: Term,
write: Mutex<Write>,
read: Mutex<Read<Term>>,
}
impl Interface<DefaultTerminal> {
pub fn new<T>(application: T) -> io::Result<Interface<DefaultTerminal>>
where T: Into<Cow<'static, str>> {
let term = DefaultTerminal::new()?;
Interface::with_term(application, term)
}
}
impl<Term: Terminal> Interface<Term> {
pub fn with_term<T>(application: T, term: Term) -> io::Result<Interface<Term>>
where T: Into<Cow<'static, str>> {
let size = term.lock_write().size()?;
let read = Read::new(&term, application.into());
Ok(Interface{
term: term,
write: Mutex::new(Write::new(size)),
read: Mutex::new(read),
})
}
pub fn lock_reader(&self) -> Reader<Term> {
Reader::new(self, self.lock_read())
}
pub fn lock_writer_append(&self) -> io::Result<Writer<Term>> {
Writer::with_lock(self.lock_write(), false)
}
pub fn lock_writer_erase(&self) -> io::Result<Writer<Term>> {
Writer::with_lock(self.lock_write(), true)
}
fn lock_read(&self) -> ReadLock<Term> {
ReadLock::new(
self.term.lock_read(),
self.read.lock().expect("Interface::lock_read"))
}
pub(crate) fn lock_write(&self) -> WriteLock<Term> {
WriteLock::new(
self.term.lock_write(),
self.write.lock().expect("Interface::lock_write"))
}
pub(crate) fn lock_write_data(&self) -> MutexGuard<Write> {
self.write.lock().expect("Interface::lock_write_data")
}
}
impl<Term: Terminal> Interface<Term> {
pub fn read_line(&self) -> io::Result<ReadResult> {
self.lock_reader().read_line()
}
pub fn read_line_step(&self, timeout: Option<Duration>)
-> io::Result<Option<ReadResult>> {
self.lock_reader().read_line_step(timeout)
}
pub fn cancel_read_line(&self) -> io::Result<()> {
self.lock_reader().cancel_read_line()
}
pub fn completer(&self) -> Arc<Completer<Term>> {
self.lock_reader().completer().clone()
}
pub fn set_completer(&self, completer: Arc<Completer<Term>>)
-> Arc<Completer<Term>> {
self.lock_reader().set_completer(completer)
}
pub fn get_variable(&self, name: &str) -> Option<Variable> {
self.lock_reader().get_variable(name)
}
pub fn set_variable(&self, name: &str, value: &str) -> Option<Variable> {
self.lock_reader().set_variable(name, value)
}
pub fn ignore_signal(&self, signal: Signal) -> bool {
self.lock_reader().ignore_signal(signal)
}
pub fn set_ignore_signal(&self, signal: Signal, set: bool) {
self.lock_reader().set_ignore_signal(signal, set)
}
pub fn report_signal(&self, signal: Signal) -> bool {
self.lock_reader().report_signal(signal)
}
pub fn set_report_signal(&self, signal: Signal, set: bool) {
self.lock_reader().set_report_signal(signal, set)
}
pub fn bind_sequence<T>(&self, seq: T, cmd: Command) -> Option<Command>
where T: Into<Cow<'static, str>> {
self.lock_reader().bind_sequence(seq, cmd)
}
pub fn bind_sequence_if_unbound<T>(&self, seq: T, cmd: Command) -> bool
where T: Into<Cow<'static, str>> {
self.lock_reader().bind_sequence_if_unbound(seq, cmd)
}
pub fn unbind_sequence(&self, seq: &str) -> Option<Command> {
self.lock_reader().unbind_sequence(seq)
}
pub fn define_function<T>(&self, name: T, cmd: Arc<Function<Term>>)
-> Option<Arc<Function<Term>>> where T: Into<Cow<'static, str>> {
self.lock_reader().define_function(name, cmd)
}
pub fn remove_function(&self, name: &str) -> Option<Arc<Function<Term>>> {
self.lock_reader().remove_function(name)
}
pub fn evaluate_directives(&self, dirs: Vec<Directive>) {
self.lock_reader().evaluate_directives(&self.term, dirs)
}
pub fn evaluate_directive(&self, dir: Directive) {
self.lock_reader().evaluate_directive(&self.term, dir)
}
}
impl<Term: Terminal> Interface<Term> {
pub fn buffer(&self) -> String {
self.lock_write().buffer.to_owned()
}
pub fn history_len(&self) -> usize {
self.lock_write().history_len()
}
pub fn history_size(&self) -> usize {
self.lock_write().history_size()
}
pub fn save_history<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
let file = File::create(path)?;
let mut wtr = BufWriter::new(file);
for entry in self.lock_write().history() {
wtr.write_all(entry.as_bytes())?;
wtr.write_all(b"\n")?;
}
wtr.flush()
}
pub fn load_history<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
let mut writer = self.lock_write();
let file = File::open(&path)?;
let rdr = BufReader::new(file);
for line in rdr.lines() {
writer.add_history(line?);
}
Ok(())
}
pub fn write_fmt(&self, args: fmt::Arguments) -> io::Result<()> {
let s = args.to_string();
self.write_str(&s)
}
fn write_str(&self, line: &str) -> io::Result<()> {
self.lock_writer_erase()?.write_str(line)
}
}
impl<Term: Terminal> Interface<Term> {
pub fn set_prompt(&self, prompt: &str) -> io::Result<()> {
self.lock_reader().set_prompt(prompt)
}
pub fn set_buffer(&self, buf: &str) -> io::Result<()> {
self.lock_reader().set_buffer(buf)
}
pub fn set_cursor(&self, pos: usize) -> io::Result<()> {
self.lock_reader().set_cursor(pos)
}
pub fn add_history(&self, line: String) {
self.lock_reader().add_history(line);
}
pub fn add_history_unique(&self, line: String) {
self.lock_reader().add_history_unique(line);
}
pub fn clear_history(&self) {
self.lock_reader().clear_history();
}
pub fn remove_history(&self, idx: usize) {
self.lock_reader().remove_history(idx);
}
pub fn set_history_size(&self, n: usize) {
self.lock_reader().set_history_size(n);
}
pub fn truncate_history(&self, n: usize) {
self.lock_reader().truncate_history(n);
}
}