use std::fs;
use std::io::{self, Read};
use std::str::FromStr;
use super::{Source, StdinError};
#[derive(Clone)]
pub struct FileOrStdin<T = String> {
pub source: Source,
inner: T,
}
impl<T> FromStr for FileOrStdin<T>
where
T: FromStr,
<T as FromStr>::Err: std::fmt::Display,
{
type Err = StdinError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let source = Source::from_str(s)?;
match &source {
Source::Stdin => {
let stdin = io::stdin();
let mut input = String::new();
stdin.lock().read_to_string(&mut input)?;
Ok(T::from_str(input.trim_end())
.map_err(|e| StdinError::FromStr(format!("{e}")))
.map(|val| Self { source, inner: val })?)
}
Source::Arg(filepath) => Ok(T::from_str(&fs::read_to_string(filepath)?)
.map_err(|e| StdinError::FromStr(format!("{e}")))
.map(|val| FileOrStdin { source, inner: val })?),
}
}
}
impl<T> FileOrStdin<T> {
pub fn into_inner(self) -> T {
self.inner
}
}
impl<T> std::fmt::Display for FileOrStdin<T>
where
T: std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl<T> std::fmt::Debug for FileOrStdin<T>
where
T: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl<T> std::ops::Deref for FileOrStdin<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl std::ops::DerefMut for FileOrStdin {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}