use crate::{is_fifo, Result};
use std::convert::TryFrom;
use std::ffi::{OsStr, OsString};
use std::fmt::{self, Debug, Display};
use std::fs::{File, OpenOptions};
use std::io::{self, Result as IoResult, Stdout, Write};
#[cfg(feature = "http")]
use crate::http::{is_http, try_to_url, HttpWriter};
#[derive(Debug)]
pub enum Output {
Stdout(Stdout),
Pipe(OsString, File),
File(OsString, File),
#[cfg(feature = "http")]
Http(String, Box<HttpWriter>),
}
pub enum SizedOutput {
Stdout(Stdout),
Pipe(OsString, File),
File(OsString, File),
#[cfg(feature = "http")]
Http(String),
}
impl Output {
pub fn new<S: AsRef<OsStr>>(path: S) -> Result<Self> {
SizedOutput::new(path)?.without_len()
}
pub fn try_from_os_str(path: &OsStr) -> std::result::Result<Self, std::ffi::OsString> {
TryFrom::try_from(path)
}
pub fn finish(mut self) -> Result<()> {
self.flush()?;
match self {
Output::Stdout(_) => Ok(()),
Output::Pipe(_, _) => Ok(()),
Output::File(_, file) => Ok(file.sync_data()?),
#[cfg(feature = "http")]
Output::Http(_, http) => Ok(http.finish()?),
}
}
pub fn lock<'a>(&'a mut self) -> Box<dyn Write + 'a> {
match self {
Output::Stdout(stdout) => Box::new(stdout.lock()),
Output::Pipe(_, pipe) => Box::new(pipe),
Output::File(_, file) => Box::new(file),
#[cfg(feature = "http")]
Output::Http(_, http) => Box::new(http),
}
}
}
impl Write for Output {
fn flush(&mut self) -> IoResult<()> {
match self {
Output::Stdout(stdout) => stdout.flush(),
Output::Pipe(_, pipe) => pipe.flush(),
Output::File(_, file) => file.flush(),
#[cfg(feature = "http")]
Output::Http(_, http) => http.flush(),
}
}
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
match self {
Output::Stdout(stdout) => stdout.write(buf),
Output::Pipe(_, pipe) => pipe.write(buf),
Output::File(_, file) => file.write(buf),
#[cfg(feature = "http")]
Output::Http(_, http) => http.write(buf),
}
}
}
impl TryFrom<&OsStr> for Output {
type Error = std::ffi::OsString;
fn try_from(file_name: &OsStr) -> std::result::Result<Self, std::ffi::OsString> {
Output::new(file_name).map_err(|e| e.to_os_string(file_name))
}
}
impl SizedOutput {
pub fn new<S: AsRef<OsStr>>(path: S) -> Result<Self> {
let path = path.as_ref();
if path == "-" {
Ok(SizedOutput::Stdout(io::stdout()))
} else {
#[cfg(feature = "http")]
if is_http(path) {
return Ok(SizedOutput::Http(try_to_url(path)?));
}
let file = open_rw(path)?;
if is_fifo(&file)? {
Ok(SizedOutput::Pipe(path.to_os_string(), file))
} else {
Ok(SizedOutput::File(path.to_os_string(), file))
}
}
}
pub fn try_from_os_str(path: &OsStr) -> std::result::Result<Self, std::ffi::OsString> {
TryFrom::try_from(path)
}
pub fn with_len(self, size: u64) -> Result<Output> {
self.maybe_with_len(Some(size))
}
pub fn without_len(self) -> Result<Output> {
self.maybe_with_len(None)
}
pub fn maybe_with_len(self, size: Option<u64>) -> Result<Output> {
Ok(match self {
SizedOutput::Stdout(stdout) => Output::Stdout(stdout),
SizedOutput::Pipe(path, pipe) => Output::Pipe(path, pipe),
SizedOutput::File(path, file) => {
if let Some(size) = size {
file.set_len(size)?;
}
Output::File(path, file)
}
#[cfg(feature = "http")]
SizedOutput::Http(path) => {
let writer = HttpWriter::new(&path, size)?;
Output::Http(path, Box::new(writer))
}
})
}
}
impl TryFrom<&OsStr> for SizedOutput {
type Error = std::ffi::OsString;
fn try_from(file_name: &OsStr) -> std::result::Result<Self, std::ffi::OsString> {
SizedOutput::new(file_name).map_err(|e| e.to_os_string(file_name))
}
}
fn open_rw(path: &OsStr) -> io::Result<File> {
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path)
}
impl Display for Output {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Output::Stdout(_) => write!(fmt, "-"),
Output::Pipe(path, _) => write!(fmt, "{:?}", path),
Output::File(path, _) => write!(fmt, "{:?}", path),
#[cfg(feature = "http")]
Output::Http(url, _) => write!(fmt, "{}", url),
}
}
}
impl Display for SizedOutput {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SizedOutput::Stdout(_) => write!(fmt, "-"),
SizedOutput::Pipe(path, _) => write!(fmt, "{:?}", path),
SizedOutput::File(path, _) => write!(fmt, "{:?}", path),
#[cfg(feature = "http")]
SizedOutput::Http(url) => write!(fmt, "{}", url),
}
}
}