std_io_iterators 1.0.0

An iterator for `STDIN` and a wrapper for `STDOUT`. Allows easy piping, and graceful closing of application if pipe breaks
Documentation
//! Abstraction of `STDIN` into iterator of [`String`] wrapped in [`rc::Rc`]

use std::{
    io, rc,
    sync::atomic::{AtomicBool, Ordering::Relaxed},
};

/// Errors possible during [`PipeInIterator::try_new`]
#[derive(Debug)]
pub enum Error {
    /// A [`PipeInIterator`] has already been constructed
    AlreadyConstructed,
}

/// Abstraction of `STDIN` into iterator of [`String`] wrapped in [`rc::Rc`]
///
/// Example
/// -------
///
/// ```no_run
#[doc = include_str!("examples/pipe_in.rs")]
/// ```
pub struct PipeInIterator<'a> {
    _stdin: *mut io::Stdin,
    lock: io::StdinLock<'a>,
    string_buffer: rc::Rc<String>,
}

impl<'a> PipeInIterator<'a> {
    /// Initialize [`PipeInIterator`]
    ///
    /// # Warning
    ///
    /// If `STDIN` is already locked the thread may block until the previous
    /// lock is released.
    ///
    /// # Errors
    ///
    /// This function should error if the lock is already held by the current
    /// thread
    ///
    /// # Panics
    ///
    /// This function might panic when called if the lock is already held by
    /// the current thread.
    ///
    /// (See [`std::sync::Mutex`])
    pub fn try_new() -> Result<PipeInIterator<'a>, Error> {
        match set_lock_state() {
            Ok(false) => (), // If old value was `false` and able to be changed to `true` then the function can run
            Err(true) => return Err(Error::AlreadyConstructed), // If the value couldn't be written because the value was already true
            Ok(true) | Err(false) => {
                unreachable!("1669400008 - compare_exchange(false, true, ...)")
            }
        }

        // Ignore the borrow checker by hiding STDIN behind a pointer
        let buffer = String::new();

        let stdin = Box::into_raw(Box::new(io::stdin()));

        Ok(Self {
            lock: unsafe { &*stdin }.lock(),
            _stdin: stdin,
            string_buffer: rc::Rc::new(buffer),
        })
    }
}

impl<'a> Iterator for PipeInIterator<'a> {
    type Item = rc::Rc<String>;

    fn next(&mut self) -> Option<Self::Item> {
        use io::BufRead;

        let data = rc::Rc::make_mut(&mut self.string_buffer);
        data.clear();
        let read_line_res = self.lock.read_line(data);
        match read_line_res {
            Ok(0) => None,
            Ok(_) => {
                data.truncate(data.trim_end().len());
                Some(self.string_buffer.clone())
            }
            Err(error) => {
                println!("error: {error}");
                None
            }
        }
    }
}

impl<'a> Drop for PipeInIterator<'a> {
    fn drop(&mut self) {
        release_lock_state();
    }
}

static mut LOCKED: AtomicBool = AtomicBool::new(false);

fn set_lock_state() -> Result<bool, bool> {
    // https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html#method.compare_exchange
    unsafe { LOCKED.compare_exchange(false, true, Relaxed, Relaxed) }
}

fn release_lock_state() {
    unsafe { LOCKED.store(false, Relaxed) }
}

#[cfg(test)]
mod test {
    use super::*;

    fn check_lock_state() -> bool {
        unsafe { LOCKED.load(Relaxed) }
    }

    #[test]
    fn test1656796720() {
        assert!(!check_lock_state());
        assert!(!check_lock_state());
        set_lock_state().expect("1669403390");
        assert!(check_lock_state());
        assert!(check_lock_state());
        release_lock_state();
        assert!(!check_lock_state());
        assert!(!check_lock_state());
    }
}