use std::env;
use std::fmt::Arguments;
use std::io::{
self,
IsTerminal,
Write,
Result as IoResult
};
use std::ops::{
Deref,
DerefMut
};
use crate::term::EmitEscapes;
use crate::style::Paint;
use crate::push::{
Inline,
Pushable
};
use super::Conciliator;
pub struct Stream {
inner: StdStream,
should_color: bool,
}
enum StdStream {
Out(io::Stdout),
Err(io::Stderr)
}
pub struct Buffer {
inner: Vec<u8>,
should_color: bool
}
pub struct Line<'s> {
buffer: Option<Buffer>,
stream: &'s Stream
}
pub struct NoNewLine<'s> {
buffer: Option<Buffer>,
stream: &'s Stream
}
pub trait InitialContent {
fn init_buffer(&self, buffer: &mut Buffer);
}
pub trait GetLine<'l> {
type Line: 'l + Deref<Target = Buffer> + DerefMut;
fn get_line(&'l self) -> Self::Line;
}
impl StdStream {
pub(crate) fn clone(&self) -> Self {
match self {
Self::Out(_) => Self::Out(io::stdout()),
Self::Err(_) => Self::Err(io::stderr()),
}
}
fn is_terminal(&self) -> bool {
match self {
Self::Out(out) => out.is_terminal(),
Self::Err(err) => err.is_terminal()
}
}
}
impl Stream {
pub(crate) fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
..*self
}
}
pub(crate) fn colors_enabled(&self) -> bool {self.should_color}
pub fn stdout(color_override: Option<bool>) -> Self {
let inner = StdStream::Out(io::stdout());
let should_color = color_override.unwrap_or_else(
|| inner.is_terminal() && Self::should_color()
);
Self {inner, should_color}
}
pub fn stderr(color_override: Option<bool>) -> Self {
let inner = StdStream::Err(io::stderr());
let should_color = color_override.unwrap_or_else(
|| inner.is_terminal() && Self::should_color()
);
Self {inner, should_color}
}
pub fn buffer(&self) -> Buffer {
Buffer::new(self.should_color)
}
pub fn line(&'_ self) -> Line<'_> {
Line::new(self)
}
pub fn print_buffer(&self, buffer: &Buffer) -> io::Result<()> {
#[cfg(not(any(test, feature = "test_capture")))] {
match &self.inner {
StdStream::Out(out) => out.lock().write_all(&buffer.inner),
StdStream::Err(err) => err.lock().write_all(&buffer.inner)
}
}
#[cfg(any(test, feature = "test_capture"))] {
let s = String::from_utf8_lossy(&buffer.inner);
match &self.inner {
StdStream::Out(_out) => print!("{s}"),
StdStream::Err(_err) => eprint!("{s}")
}
Ok(())
}
}
pub fn print_bytes(&self, buffer: &[u8]) -> io::Result<()> {
#[cfg(not(any(test, feature = "test_capture")))] {
match &self.inner {
StdStream::Out(out) => out.lock().write_all(buffer),
StdStream::Err(err) => err.lock().write_all(buffer)
}
}
#[cfg(any(test, feature = "test_capture"))] {
let s = String::from_utf8_lossy(buffer);
match &self.inner {
StdStream::Out(_out) => print!("{s}"),
StdStream::Err(_err) => eprint!("{s}")
}
Ok(())
}
}
pub fn flush(&self) -> io::Result<()> {
match &self.inner {
StdStream::Out(out) => out.lock().flush(),
StdStream::Err(err) => err.lock().flush()
}
}
#[cfg(not(any(test, feature = "test_capture")))]
pub(crate) fn print_buffer_and_flush(&self, buffer: &Buffer)
-> io::Result<()>
{
match &self.inner {
StdStream::Out(out) => {
let mut out = out.lock();
out.write_all(&buffer.inner)?;
out.flush()
},
StdStream::Err(err) => {
let mut err = err.lock();
err.write_all(&buffer.inner)?;
err.flush()
}
}
}
#[cfg(any(test, feature = "test_capture"))]
pub(crate) fn print_buffer_and_flush(&self, buffer: &Buffer)
-> io::Result<()>
{
let s = String::from_utf8_lossy(&buffer.inner);
match &self.inner {
StdStream::Out(out) => {
let mut out = out.lock();
print!("{s}");
out.flush()
},
StdStream::Err(err) => {
let mut err = err.lock();
eprint!("{s}");
err.flush()
}
}
}
fn should_color() -> bool {
match env::var_os("TERM") {
Some(var) if var == "dumb" => false,
None => false,
Some(_) => env::var_os("NO_COLOR").is_none()
}
}
}
impl<'l> GetLine<'l> for Stream {
type Line = Line<'l>;
fn get_line(&'l self) -> Self::Line {self.line()}
}
impl Conciliator for Stream {}
impl Buffer {
pub fn new(should_color: bool) -> Self {
Self {
inner: Vec::new(),
should_color
}
}
pub fn push_inline<T: Inline>(&mut self, thing: &T) -> &mut Self {
thing.inline(self);
self
}
pub fn push<M, T: Pushable<M>>(&mut self, thing: T) -> &mut Self {
thing.push_into(self);
self
}
pub(crate) fn clear(&mut self) {self.inner.clear()}
}
impl Write for Buffer {
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> IoResult<()> {
self.inner.flush()
}
}
impl EmitEscapes for Buffer {
fn escapes_recognized(&self) -> bool {self.should_color}
}
impl<'s> Line<'s> {
pub fn new(stream: &'s Stream) -> Self {
let buffer = Some(Buffer::new(stream.should_color));
Self {buffer, stream}
}
pub fn print(self) {}
pub fn discard(mut self) {self.buffer.take();}
pub fn no_newline(mut self) -> NoNewLine<'s> {
let buffer = self.buffer.take().unwrap();
NoNewLine {stream: self.stream, buffer: Some(buffer)}
}
}
impl<'s> Drop for Line<'s> {
fn drop(&mut self) {
if let Some(buffer) = self.buffer.as_mut() {
writeln!(buffer, ).unwrap();
self.stream.print_buffer(buffer).unwrap();
}
}
}
impl<'s> Deref for Line<'s> {
type Target = Buffer;
fn deref(&self) -> &Self::Target {self.buffer.as_ref().unwrap()}
}
impl<'s> DerefMut for Line<'s> {
fn deref_mut(&mut self) -> &mut Self::Target {self.buffer.as_mut().unwrap()}
}
impl<'s> Write for Line<'s> {
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> IoResult<()> {
self.inner.flush()
}
}
impl<'s> EmitEscapes for Line<'s> {
fn escapes_recognized(&self) -> bool {self.should_color}
}
impl<'s> NoNewLine<'s> {
pub fn print(self) {}
pub fn discard(mut self) {self.buffer.take();}
}
impl<'s> Drop for NoNewLine<'s> {
fn drop(&mut self) {
if let Some(buffer) = self.buffer.as_mut() {
self.stream.print_buffer_and_flush(buffer).unwrap();
}
}
}
impl<'s> Deref for NoNewLine<'s> {
type Target = Buffer;
fn deref(&self) -> &Self::Target {self.buffer.as_ref().unwrap()}
}
impl<'s> DerefMut for NoNewLine<'s> {
fn deref_mut(&mut self) -> &mut Self::Target {self.buffer.as_mut().unwrap()}
}
impl InitialContent for std::ops::RangeFull {
fn init_buffer(&self, _buf: &mut Buffer) {}
}
impl InitialContent for () {
fn init_buffer(&self, _buf: &mut Buffer) {}
}
impl InitialContent for Option<()> {
fn init_buffer(&self, _buf: &mut Buffer) {}
}
impl InitialContent for &str {
fn init_buffer(&self, buf: &mut Buffer) {
buf.push_plain(self);
}
}
impl InitialContent for String {
fn init_buffer(&self, buf: &mut Buffer) {
buf.push_plain(self);
}
}
impl InitialContent for Arguments<'_> {
fn init_buffer(&self, buf: &mut Buffer) {
buf.push_plain(self);
}
}
impl<T: Inline + ?Sized> InitialContent for T {
fn init_buffer(&self, buf: &mut Buffer) {self.inline(buf)}
}