use std::{
io::Write,
marker::Sized,
sync::atomic::{AtomicBool, Ordering::Relaxed},
};
use crate::pipe_out_recovered_iterator::PipeOutRecoveredIterator;
#[must_use = "this may be an error variant (`BrokenPipe`, `OtherError`, \
`PreviousError`), which should be handled"]
pub enum WriteLineResult {
BrokenPipe(std::io::Error),
OtherError(std::io::Error),
PreviousError,
StdOutLock,
Ok,
}
impl WriteLineResult {
#[must_use]
pub fn same_variant_as(&self, variant: &Self) -> bool {
self.variant_to_usize() == variant.variant_to_usize()
}
fn variant_to_usize(&self) -> usize {
match self {
Self::BrokenPipe(_) => 1,
Self::OtherError(_) => 2,
Self::PreviousError => 3,
Self::StdOutLock => 4,
Self::Ok => 5,
}
}
}
impl From<std::io::Error> for WriteLineResult {
fn from(e: std::io::Error) -> Self {
if e.kind() == std::io::ErrorKind::BrokenPipe {
Self::BrokenPipe(e)
} else {
Self::OtherError(e)
}
}
}
impl From<WriteLineResult> for std::io::Error {
fn from(e: WriteLineResult) -> Self {
match e {
WriteLineResult::OtherError(e) | WriteLineResult::BrokenPipe(e) => {
e
}
_ => panic!(
"1656622268 - Can't unwrap io::Error from non-std::io::Error \
variant"
),
}
}
}
impl From<WriteLineResult> for Result<(), std::io::Error> {
fn from(value: WriteLineResult) -> Self {
match value {
WriteLineResult::Ok => Ok(()),
WriteLineResult::OtherError(e) | WriteLineResult::BrokenPipe(e) => {
Err(e)
}
WriteLineResult::PreviousError => Err(std::io::Error::other(
"1669225745 - Other error occurred in last [Self::pipe_out]",
)),
WriteLineResult::StdOutLock => Err(std::io::Error::other(
"1669225823 - STDOUT is already locked",
)),
}
}
}
pub trait PipeOut<D: std::fmt::Display>: Iterator<Item = D> {
#[doc = include_str!("examples/pipe_out.rs")]
fn pipe_out(mut self) -> Result<(), PipeOutRecoveredIterator<D, Self>>
where
Self: Sized,
{
if check_error_state() || set_lock_state().is_err() {
return Err(PipeOutRecoveredIterator {
iter: self,
recovered_datum: None,
result: WriteLineResult::StdOutLock,
});
}
let stdout = std::io::stdout();
let mut lock = stdout.lock();
for datum in self.by_ref() {
if check_error_state() {
release_lock_state();
return Err(PipeOutRecoveredIterator {
iter: self,
recovered_datum: Some(datum),
result: WriteLineResult::PreviousError,
});
}
if let Err(error) = lock.write_all(format!("{datum}\n").as_bytes())
{
set_error_state();
release_lock_state();
return Err(PipeOutRecoveredIterator {
iter: self,
recovered_datum: Some(datum),
result: error.into(),
});
}
}
release_lock_state();
Ok(())
}
}
impl<D: std::fmt::Display, I: Iterator<Item = D>> PipeOut<D> for I {}
static ERROR: AtomicBool = AtomicBool::new(false);
fn check_error_state() -> bool {
ERROR.load(Relaxed)
}
fn set_error_state() {
ERROR.store(true, Relaxed);
}
static LOCKED: AtomicBool = AtomicBool::new(false);
fn set_lock_state() -> Result<bool, bool> {
LOCKED.compare_exchange(false, true, Relaxed, Relaxed)
}
fn release_lock_state() {
LOCKED.store(false, Relaxed);
}
#[cfg(test)]
mod test {
use super::*;
fn check_lock_state() -> bool {
LOCKED.load(Relaxed)
}
#[test]
fn test1656794821() {
assert!(!check_error_state());
assert!(!check_error_state());
set_error_state();
assert!(check_error_state());
assert!(check_error_state());
}
#[test]
fn test1656795696() {
assert!(!check_lock_state());
assert!(!check_lock_state());
set_lock_state().unwrap();
assert!(check_lock_state());
assert!(check_lock_state());
release_lock_state();
assert!(!check_lock_state());
assert!(!check_lock_state());
}
}