use std::marker::PhantomData;
use std::str::FromStr;
#[cfg(feature = "tokio")]
use tokio::io::AsyncReadExt;
use super::{Source, StdinError};
#[derive(Debug, Clone)]
pub struct FileOrStdin<T = String> {
source: Source,
_type: PhantomData<T>,
}
impl<T> FileOrStdin<T> {
pub fn is_stdin(&self) -> bool {
matches!(self.source, Source::Stdin)
}
pub fn is_file(&self) -> bool {
!self.is_stdin()
}
pub fn filename(&self) -> &str {
match &self.source {
Source::Stdin => "-",
Source::Arg(path) => path,
}
}
pub fn contents(self) -> Result<T, StdinError>
where
T: FromStr,
<T as FromStr>::Err: std::fmt::Display,
{
use std::io::Read;
let mut reader = self.into_reader()?;
let mut input = String::new();
let _ = reader.read_to_string(&mut input)?;
T::from_str(input.trim_end()).map_err(|e| StdinError::FromStr(format!("{e}")))
}
pub fn into_reader(self) -> Result<impl std::io::Read, StdinError> {
self.source.into_reader()
}
#[cfg(feature = "tokio")]
pub async fn contents_async(self) -> Result<T, StdinError>
where
T: FromStr,
<T as FromStr>::Err: std::fmt::Display,
{
let mut reader = self.into_async_reader().await?;
let mut input = String::new();
let _ = reader.read_to_string(&mut input).await?;
T::from_str(input.trim_end()).map_err(|e| StdinError::FromStr(format!("{e}")))
}
#[cfg(feature = "tokio")]
pub async fn into_async_reader(&self) -> Result<impl tokio::io::AsyncRead, StdinError> {
let input: std::pin::Pin<Box<dyn tokio::io::AsyncRead + 'static>> = match &self.source {
Source::Stdin => Box::pin(tokio::io::stdin()),
Source::Arg(filepath) => {
let f = tokio::fs::File::open(filepath).await?;
Box::pin(f)
}
};
Ok(input)
}
}
impl<T> FromStr for FileOrStdin<T> {
type Err = StdinError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let source = Source::from_str(s)?;
Ok(Self {
source,
_type: PhantomData,
})
}
}
#[test]
fn test_source_methods() {
let val: FileOrStdin<String> = "-".parse().unwrap();
assert!(val.is_stdin());
assert!(!val.is_file());
assert_eq!(val.filename(), "-");
let val: FileOrStdin<String> = "/path/to/something".parse().unwrap();
assert!(val.is_file());
assert!(!val.is_stdin());
assert_eq!(val.filename(), "/path/to/something");
}