use std::fmt;
use std::io::{self, read_to_string};
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::io::stdin;
pub(crate) static STDIN_HAS_BEEN_USED: AtomicBool = AtomicBool::new(false);
#[derive(thiserror::Error, Debug)]
pub enum StdinError {
#[error("stdin argument used more than once")]
StdInRepeatedUse,
#[error(transparent)]
StdIn(#[from] io::Error),
#[error("{0}")]
FromStr(String),
}
#[derive(Debug, Clone)]
pub enum Source {
Stdin(String),
Arg(String),
}
impl FromStr for Source {
type Err = StdinError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"-" => {
if STDIN_HAS_BEEN_USED.load(Ordering::Acquire) {
return Err(StdinError::StdInRepeatedUse);
}
let mut stdin = stdin();
let input = read_to_string(&mut stdin)?;
STDIN_HAS_BEEN_USED.store(true, Ordering::SeqCst);
Ok(Self::Stdin(input))
}
arg => Ok(Self::Arg(arg.to_owned())),
}
}
}
#[derive(Clone)]
pub struct MaybeStdin<T> {
pub source: Source,
inner: T,
}
impl<T> FromStr for MaybeStdin<T>
where
T: FromStr,
T::Err: fmt::Display,
{
type Err = StdinError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let source = Source::from_str(s)?;
match &source {
Source::Stdin(value) => Ok(T::from_str(value.trim_end())
.map_err(|e| StdinError::FromStr(format!("{e}")))
.map(|val| Self { source, inner: val })?),
Source::Arg(value) => Ok(T::from_str(value)
.map_err(|e| StdinError::FromStr(format!("{e}")))
.map(|val| Self { source, inner: val })?),
}
}
}
impl<T> MaybeStdin<T> {
pub fn into_inner(self) -> T {
self.inner
}
}
impl<T> fmt::Display for MaybeStdin<T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl<T> fmt::Debug for MaybeStdin<T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl<T> Deref for MaybeStdin<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for MaybeStdin<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
#[derive(Clone)]
pub struct MaybeStdinVec<T> {
pub source: Source,
inner: Vec<T>,
}
impl<T> FromStr for MaybeStdinVec<T>
where
T: FromStr,
T::Err: fmt::Display,
{
type Err = StdinError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let source = Source::from_str(s)?;
match &source {
Source::Stdin(value) => {
let mut inner = vec![];
for arg in value.lines() {
let val = T::from_str(arg.trim_end())
.map_err(|e| StdinError::FromStr(format!("{e}")))?;
inner.push(val);
}
Ok(Self { source, inner })
}
Source::Arg(value) => Ok(T::from_str(value)
.map_err(|e| StdinError::FromStr(format!("{e}")))
.map(|val| Self { source, inner: vec![val] })?),
}
}
}
impl<T> MaybeStdinVec<T> {
pub fn into_inner(self) -> Vec<T> {
self.inner
}
}
impl<T> fmt::Debug for MaybeStdinVec<T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl<T> Deref for MaybeStdinVec<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for MaybeStdinVec<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T> IntoIterator for MaybeStdinVec<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<'a, T> IntoIterator for &'a MaybeStdinVec<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.inner.iter()
}
}
#[cfg(test)]
mod tests {
use crate::test::assert_ordered_eq;
use super::*;
#[test]
fn repeated_use() {
let r: Result<MaybeStdin<String>, StdinError> = "-".parse();
assert!(r.is_ok());
let r: Result<MaybeStdin<String>, StdinError> = "-".parse();
assert!(matches!(r, Err(StdinError::StdInRepeatedUse)));
}
#[test]
fn maybe_stdin() {
for use_stdin in [false, true] {
let mut value: MaybeStdin<String> = if use_stdin {
let mut stdin = stdin();
stdin.inject("test\n").unwrap();
"-".parse().unwrap()
} else {
"test".parse().unwrap()
};
assert_eq!(value.to_string(), "test");
assert!(format!("{value:?}").contains("test"));
assert_eq!(value.len(), 4);
value.push_str("test");
assert_eq!(value.into_inner(), "testtest");
}
}
#[test]
fn maybe_stdin_vec() {
for use_stdin in [false, true] {
let mut values: MaybeStdinVec<usize> = if use_stdin {
let mut stdin = stdin();
stdin.inject("12\n").unwrap();
"-".parse().unwrap()
} else {
"12".parse().unwrap()
};
assert!(format!("{values:?}").contains("12"));
assert_eq!(values.len(), 1);
values.push(13);
assert_ordered_eq!(values.clone().into_iter(), [12, 13]);
assert_ordered_eq!((&values).into_iter(), [&12, &13]);
assert_eq!(values.into_inner(), [12, 13]);
}
}
}