use crate::prolog_parser::ast::*; use crate::prolog::read::readline::*; use std::cell::RefCell; use std::error::Error; use std::fmt; use std::fs::File; use std::io::{stdin, stdout, Cursor, ErrorKind, Read, Write}; use std::hash::{Hash, Hasher}; use std::net::TcpStream; use std::rc::Rc; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum StreamType { Binary, Text, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum EOFAction { EOFCode, Error, Reset, } /* all these streams are closed automatically when the instance is * dropped. */ pub enum StreamInstance { Bytes(Cursor>), DynReadSource(Box), File(File), Null, ReadlineStream(ReadlineStream), Stdin, Stdout, TcpStream(TcpStream), } impl fmt::Debug for StreamInstance { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self { &StreamInstance::Bytes(ref bytes) => write!(fmt, "Bytes({:?})", bytes), &StreamInstance::DynReadSource(_) => write!(fmt, "DynReadSource(_)"), // Hacky solution. &StreamInstance::File(ref file) => write!(fmt, "File({:?})", file), &StreamInstance::Null => write!(fmt, "Null"), &StreamInstance::ReadlineStream(ref readline_stream) => write!(fmt, "ReadlineStream({:?})", readline_stream), &StreamInstance::Stdin => write!(fmt, "Stdin"), &StreamInstance::Stdout => write!(fmt, "Stdout"), &StreamInstance::TcpStream(ref tcp_stream) => write!(fmt, "TcpStream({:?})", tcp_stream), } } } #[derive(Debug, Clone)] struct WrappedStreamInstance(Rc>); impl WrappedStreamInstance { #[inline] fn new(stream_inst: StreamInstance) -> Self { WrappedStreamInstance(Rc::new(RefCell::new(stream_inst))) } } impl PartialEq for WrappedStreamInstance { #[inline] fn eq(&self, other: &Self) -> bool { Rc::ptr_eq(&self.0, &other.0) } } impl Eq for WrappedStreamInstance {} impl Hash for WrappedStreamInstance { fn hash(&self, state: &mut H) { let rc = &self.0; let ptr = Rc::into_raw(rc.clone()); state.write_usize(ptr as usize); unsafe { // necessary to avoid memory leak. let _ = Rc::from_raw(ptr); }; } } #[derive(Debug)] enum StreamError { ReadFromOutputStream, WriteToInputStream, FlushToInputStream, } impl fmt::Display for StreamError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { StreamError::ReadFromOutputStream => { write!(f, "attempted to read from a write-only stream") } StreamError::WriteToInputStream => { write!(f, "attempted to write to a read-only stream") } StreamError::FlushToInputStream => { write!(f, "attempted to flush a read-only stream") } } } } impl Error for StreamError {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StreamOptions { pub stream_type: StreamType, pub reposition: bool, pub alias: Option, pub eof_action: EOFAction, } impl Default for StreamOptions { #[inline] fn default() -> Self { StreamOptions { stream_type: StreamType::Text, reposition: false, alias: None, eof_action: EOFAction::EOFCode, } } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Stream { pub options: StreamOptions, stream_inst: WrappedStreamInstance, } impl From for Stream { fn from(string: String) -> Self { Stream { options: StreamOptions::default(), stream_inst: WrappedStreamInstance::new( StreamInstance::Bytes(Cursor::new(string.into_bytes())) ) } } } impl From for Stream { fn from(rl_stream: ReadlineStream) -> Self { Stream { options: StreamOptions::default(), stream_inst: WrappedStreamInstance::new( StreamInstance::ReadlineStream(rl_stream) ), } } } impl From<&'static str> for Stream { fn from(src: &'static str) -> Stream { Stream { options: StreamOptions::default(), stream_inst: WrappedStreamInstance::new( StreamInstance::DynReadSource(Box::new(src.as_bytes())) ), } } } impl From for Stream { fn from(file: File) -> Stream { Stream { options: StreamOptions::default(), stream_inst: WrappedStreamInstance::new( StreamInstance::File(file) ), } } } impl Stream { #[inline] pub(crate) fn as_ptr(&self) -> *const RefCell { let rc = self.stream_inst.0.clone(); let ptr = Rc::into_raw(rc); unsafe { // must be done to avoid memory leak. let _ = Rc::from_raw(ptr); } ptr } #[inline] pub(crate) fn stdout() -> Self { Stream { options: StreamOptions::default(), stream_inst: WrappedStreamInstance::new( StreamInstance::Stdout ), } } #[inline] pub(crate) fn stdin() -> Self { Stream { options: StreamOptions::default(), stream_inst: WrappedStreamInstance::new( StreamInstance::Stdin ), } } #[inline] pub(crate) fn null_stream() -> Self { Stream { options: StreamOptions::default(), // TODO: null_options? stream_inst: WrappedStreamInstance::new( StreamInstance::Null ), } } #[inline] pub(crate) fn is_stdout(&self) -> bool { match *self.stream_inst.0.borrow() { StreamInstance::Stdout => { true } _ => { false } } } #[inline] pub(crate) fn is_stdin(&self) -> bool { match *self.stream_inst.0.borrow() { StreamInstance::Stdin | StreamInstance::ReadlineStream(_) => { true } _ => { false } } } #[inline] pub(crate) fn is_input_stream(&self) -> bool { match *self.stream_inst.0.borrow() { StreamInstance::Stdin | StreamInstance::TcpStream(_) | StreamInstance::Bytes(_) | StreamInstance::ReadlineStream(_) | StreamInstance::DynReadSource(_) | StreamInstance::File(_) => { true } _ => { false } } } #[inline] pub(crate) fn is_output_stream(&self) -> bool { match *self.stream_inst.0.borrow() { StreamInstance::Stdout | StreamInstance::TcpStream(_) | StreamInstance::Bytes(_) | StreamInstance::File(_) => { true } _ => { false } } } } impl Read for Stream { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { match *self.stream_inst.0.borrow_mut() { StreamInstance::File(ref mut file) => { file.read(buf) } StreamInstance::TcpStream(ref mut tcp_stream) => { tcp_stream.read(buf) } StreamInstance::ReadlineStream(ref mut rl_stream) => { rl_stream.read(buf) } StreamInstance::DynReadSource(ref mut src) => { src.read(buf) } StreamInstance::Bytes(ref mut cursor) => { cursor.read(buf) } StreamInstance::Stdin => { stdin().read(buf) } StreamInstance::Stdout | StreamInstance::Null => { Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, )) } } } } impl Write for Stream { fn write(&mut self, buf: &[u8]) -> std::io::Result { match *self.stream_inst.0.borrow_mut() { StreamInstance::File(ref mut file) => { file.write(buf) } StreamInstance::TcpStream(ref mut tcp_stream) => { tcp_stream.write(buf) } StreamInstance::Bytes(ref mut cursor) => { cursor.write(buf) } StreamInstance::Stdout => { stdout().write(buf) } _ => { Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::WriteToInputStream, )) } } } fn flush(&mut self) -> std::io::Result<()> { match *self.stream_inst.0.borrow_mut() { StreamInstance::File(ref mut file) => { file.flush() } StreamInstance::TcpStream(ref mut tcp_stream) => { tcp_stream.flush() } StreamInstance::Bytes(ref mut cursor) => { cursor.flush() } StreamInstance::Stdout => { stdout().flush() } _ => { Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::FlushToInputStream, )) } } } } //TODO: write a Seek instance.