//! 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, } 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, 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; fn next(&mut self) -> Option { 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 { // 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()); } }