use std::{
fmt::Arguments,
io::Result,
ops::{Deref, DerefMut},
sync::{Mutex, MutexGuard},
};
#[macro_export]
macro_rules! echo {
( -n, $dst:expr, $($tt:tt)+) => {{
#[cfg( all( feature="altio", debug_assertions ))]
eprint!( $($tt)+ );
write!( $dst, $($tt)+).unwrap()
}};
( $dst:expr, $($tt:tt)+) => {{
#[cfg( all( feature="altio", debug_assertions ))]
eprintln!( $($tt)+ );
writeln!( $dst, $($tt)+).unwrap()
}};
}
pub struct AltinLock<'a> {
inner: MutexGuard<'a, String>,
}
impl<'a> AltinLock<'a> {
pub fn read_line( &mut self, buf: &mut String ) -> Result<usize> {
if let Some( offset ) = self.inner.find( '\n' ) {
buf.extend( self.inner.drain( ..=offset ));
Ok( buf.len() )
} else {
Ok( 0 )
}
}
pub fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
if !self.inner.is_empty() {
let len = self.inner.len();
buf.extend( self.inner.drain(..) );
Ok( len )
} else {
Ok(0)
}
}
pub fn is_terminal( &self ) -> bool { false }
}
pub struct Lines<'a> {
inner: MutexGuard<'a, String>,
}
impl<'a> Iterator for Lines<'a> {
type Item = String;
fn next( &mut self ) -> Option<String> {
self.inner
.find( '\n' )
.map( |offset| String::from_iter( self.inner.drain( ..=offset )))
}
}
#[derive( Debug, Default )]
pub struct Altin( Mutex<String> );
impl Altin {
pub fn lock( &self ) -> AltinLock<'_> {
loop {
if let Ok( lock ) = self.0.lock() {
break AltinLock{ inner: lock };
}
}
}
pub fn lines( &self ) -> Lines<'_> {
loop {
if let Ok( lock ) = self.0.lock() {
break Lines{ inner: lock };
}
}
}
pub fn read_line( &self, buf: &mut String ) -> Result<usize> {
loop {
if let Ok( ref mut input ) = self.0.lock() {
if let Some( offset ) = input.find( '\n' ) {
buf.extend( input.drain( ..=offset ));
return Ok( buf.len() );
}
}
}
}
pub fn read_to_string(&self, buf: &mut String) -> Result<usize> {
loop {
if let Ok( ref mut input ) = self.0.lock() {
if !input.is_empty() {
let len = input.len();
buf.extend( input.drain(..) );
return Ok( len );
}
}
}
}
pub fn is_terminal( &self ) -> bool { false }
}
pub struct AltoutLock<'a> {
inner: MutexGuard<'a, String>,
}
impl<'a> AltoutLock<'a> {
pub fn write_fmt( &mut self, args: Arguments<'_> ) -> Result<()> {
use std::fmt::Write;
self.inner.write_fmt( args ).map_err( |_| unreachable!() )
}
}
impl<'a> Deref for AltoutLock<'a> {
type Target = String;
fn deref( &self ) -> &String {
self.inner.deref()
}
}
impl<'a> DerefMut for AltoutLock<'a> {
fn deref_mut( &mut self ) -> &mut String {
self.inner.deref_mut()
}
}
#[derive( Debug, Default )]
pub struct Altout( Mutex<String> );
impl Altout {
pub fn lock( &self ) -> AltoutLock<'_> {
loop {
if let Ok( lock ) = self.0.lock() {
return AltoutLock{ inner: lock };
}
}
}
pub fn write_fmt( &mut self, args: Arguments<'_> ) -> Result<()> {
use std::fmt::Write;
self.lock().inner.write_fmt( args ).map_err( |_| unreachable!() )
}
pub fn flush( &mut self ) -> Result<()> {
Ok(())
}
pub fn is_terminal( &self ) -> bool { false }
}
#[inline]
fn get_lines<'a>( buf: &mut MutexGuard<'a,String>, mut cnt: usize, peek_only: bool ) -> Option<String> {
let mut offset = 0;
while let Some( mut off ) = buf[offset..].find( '\n' ) {
off += 1;
offset += off;
cnt -= 1;
if cnt == 0 {
break;
}
}
if cnt != 0 {
None
} else if peek_only {
Some( buf[ ..offset ].to_owned() )
} else {
Some( String::from_iter( buf.drain( ..offset )))
}
}
impl Altin {
pub fn send( &self, text: &str ) {
if !text.is_empty() {
loop {
if let Ok( mut buf ) = self.0.lock() {
buf.push_str( text );
return;
}
}
}
}
pub fn send_line( &self, text: &str ) {
loop {
if let Ok( mut buf ) = self.0.lock() {
buf.push_str( text );
buf.push( '\n' );
return;
}
}
}
}
impl Altout {
pub fn recv( &self ) -> String {
loop {
if let Ok( ref mut buf ) = self.0.lock() {
if !buf.is_empty() {
let mut received = String::new();
std::mem::swap( &mut received, buf );
return received;
}
}
}
}
pub fn try_recv( &self ) -> Option<String> {
if let Ok( ref mut buf ) = self.0.try_lock() {
if !buf.is_empty() {
let mut received = String::new();
std::mem::swap( &mut received, buf );
return Some( received );
}
}
None
}
pub fn recv_line( &self ) -> String {
loop {
if let Ok( ref mut buf ) = self.0.lock() {
if let Some( offset ) = buf.find( '\n' ) {
return String::from_iter( buf.drain( ..=offset ));
}
}
}
}
pub fn try_recv_line( &self ) -> Option<String> {
if let Ok( ref mut buf ) = self.0.try_lock() {
if let Some( offset ) = buf.find( '\n' ) {
return Some( String::from_iter( buf.drain( ..=offset )));
}
}
None
}
pub fn recv_lines( &self, cnt: usize ) -> String {
if cnt == 0 {
String::new()
} else {
loop {
if let Some( received ) = self.try_recv_lines( cnt ) {
break received;
}
}
}
}
pub fn try_recv_lines( &self, cnt: usize ) -> Option<String> {
if cnt != 0 {
if let Ok( ref mut buf ) = self.0.try_lock() {
return get_lines( buf, cnt, false );
}
}
None
}
pub fn peek_line( &self ) -> Option<String> {
if let Ok( ref mut buf ) = self.0.try_lock() {
if let Some( offset ) = buf.find( '\n' ) {
return Some( buf[ ..=offset ].to_owned() );
}
}
None
}
pub fn peek_lines( &self, cnt: usize ) -> Option<String> {
if cnt != 0 {
if let Ok( ref mut buf ) = self.0.try_lock() {
return get_lines( buf, cnt, true );
}
}
None
}
}
#[cfg( not( feature = "altio" ))]
#[derive( Debug, Default )]
pub struct Altio;
#[cfg( not( feature = "altio" ))]
impl Altio {
pub fn input( &self ) -> std::io::Stdin { std::io::stdin() }
pub fn out( &self ) -> std::io::Stdout { std::io::stdout() }
pub fn err( &self ) -> std::io::Stderr { std::io::stderr() }
}
#[cfg( feature = "altio" )]
#[derive( Clone, Debug, Default )]
pub struct Altio( std::sync::Arc<(Altin, Altout, Altout)> );
#[cfg( feature = "altio" )]
impl Altio {
pub fn input( &self ) -> &Altin { &self.0.0 }
pub fn out( &self ) -> AltoutLock { self.0.1.lock() }
pub fn err( &self ) -> AltoutLock { self.0.2.lock() }
pub fn send( &self, text: &str ) { self.0.0.send( text )}
pub fn send_line( &self, text: &str ) { self.0.0.send_line( text )}
pub fn recv( &self ) -> String { self.0.1.recv() }
pub fn try_recv( &self ) -> Option<String> { self.0.1.try_recv() }
pub fn recv_line( &self ) -> String { self.0.1.recv_line() }
pub fn try_recv_line( &self ) -> Option<String> { self.0.1.try_recv_line() }
pub fn recv_lines( &self, cnt: usize ) -> String { self.0.1.recv_lines(cnt) }
pub fn try_recv_lines( &self, cnt: usize ) -> Option<String> { self.0.1.try_recv_lines(cnt) }
pub fn peek_line( &self ) -> Option<String> { self.0.1.peek_line() }
pub fn peek_lines( &self, cnt: usize ) -> Option<String> { self.0.1.peek_lines(cnt) }
pub fn recv_err( &self ) -> String { self.0.2.recv() }
pub fn try_recv_err( &self ) -> Option<String> { self.0.2.try_recv() }
pub fn recv_err_line( &self ) -> String { self.0.2.recv_line() }
pub fn try_recv_err_line( &self ) -> Option<String> { self.0.2.try_recv_line() }
pub fn recv_err_lines( &self, cnt: usize ) -> String { self.0.2.recv_lines(cnt) }
pub fn try_recv_err_lines( &self, cnt: usize ) -> Option<String> { self.0.2.try_recv_lines(cnt) }
pub fn peek_err_line( &self ) -> Option<String> { self.0.2.peek_line() }
pub fn peek_err_lines( &self, cnt: usize ) -> Option<String> { self.0.2.peek_lines(cnt) }
}
#[macro_export]
macro_rules! impl_altio_output {
($ty:ty) => {
#[cfg( feature = "altio" )]
impl $ty {
pub fn out( &self ) -> altio::AltoutLock { self.altio.out() }
pub fn err( &self ) -> altio::AltoutLock { self.altio.err() }
}
#[cfg( not( feature = "altio" ))]
impl $ty {
pub fn out( &self ) -> std::io::Stdout { std::io::stdout() }
pub fn err( &self ) -> std::io::Stderr { std::io::stderr() }
}
};
}
#[cfg( all( test, feature="altio" ))]
pub mod tests {
use super::{Altio, echo};
use std::io::Result;
const ALPHABET: &'static str = "abcdefg\nhijklmn\nopq rst\nuvw xyz";
#[test]
fn altin_lock_read_line() -> Result<()> {
let io = Altio::default();
io.send_line( ALPHABET );
let mut lock = io.input().lock();
let mut buf = String::new();
lock.read_line( &mut buf )?;
assert_eq!( buf, "abcdefg\n" );
lock.read_line( &mut buf )?;
assert_eq!( buf, "abcdefg\nhijklmn\n" );
lock.read_line( &mut buf )?;
assert_eq!( buf, "abcdefg\nhijklmn\nopq rst\n" );
lock.read_line( &mut buf )?;
assert_eq!( buf, "abcdefg\nhijklmn\nopq rst\nuvw xyz\n" );
Ok(())
}
#[test]
fn altin_lock_read_to_string() -> Result<()> {
let io = Altio::default();
io.send( ALPHABET );
let mut lock = io.input().lock();
let mut buf = String::new();
lock.read_to_string( &mut buf )?;
assert_eq!( buf, ALPHABET );
Ok(())
}
#[test]
fn lines() {
let io = Altio::default();
assert!( io.input().lines().collect::<String>().is_empty() );
io.send( ALPHABET );
assert_eq!( io.input().lines().collect::<Vec<String>>(),
vec![ "abcdefg\n".to_owned(), "hijklmn\n".to_owned(), "opq rst\n".to_owned() ]);
}
#[test]
fn altin_read_line() -> Result<()> {
let io = Altio::default();
io.send( ALPHABET );
let mut buf = String::new();
io.input().read_line( &mut buf )?;
assert_eq!( buf, "abcdefg\n" );
Ok(())
}
#[test]
fn altin_read_to_string() -> Result<()> {
let io = Altio::default();
io.send( ALPHABET );
let mut buf = String::new();
io.input().read_to_string( &mut buf )?;
assert_eq!( buf, ALPHABET );
Ok(())
}
#[test]
fn altout_lock_write_fmt() -> Result<()> {
let io = Altio::default();
{
let mut lock = io.out();
let contents = ALPHABET;
for line in contents.lines() {
writeln!( lock, "{}", line )?;
}
}
assert_eq!( io.recv().trim(), ALPHABET );
Ok(())
}
#[test]
fn altout_write_fmt() -> Result<()> {
let io = Altio::default();
let contents = ALPHABET;
for line in contents.lines() {
writeln!( io.out(), "{}", line )?;
}
assert_eq!( io.recv().trim(), ALPHABET );
Ok(())
}
#[test]
fn alterr_lock_write_fmt() -> Result<()> {
let io = Altio::default();
{
let mut lock = io.err();
let contents = ALPHABET;
for line in contents.lines() {
writeln!( lock, "{}", line )?;
}
}
assert_eq!( io.recv_err().trim(), ALPHABET );
Ok(())
}
#[test]
fn alterr_write_fmt() -> Result<()> {
let io = Altio::default();
let contents = ALPHABET;
for line in contents.lines() {
writeln!( io.err(), "{}", line )?;
}
assert_eq!( io.recv_err().trim(), ALPHABET );
Ok(())
}
#[test]
fn nothing_received() {
let io = Altio::default();
assert!( io.try_recv().is_none() );
assert!( io.try_recv_line().is_none() );
assert!( io.try_recv_err().is_none() );
assert!( io.try_recv_err_line().is_none() );
}
#[test]
fn io_print() {
{ let io = Altio::default(); echo!( -n, io.out(), "" ); assert!( io.try_recv().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.out(), "" ); assert!( io.try_recv_line().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.out(), "" ); assert!( io.try_recv_err().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.out(), "" ); assert!( io.try_recv_err_line().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.out(), " " ); assert!( io.try_recv().is_some() ); }
{ let io = Altio::default(); echo!( -n, io.out(), " " ); assert!( io.try_recv_line().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.out(), " " ); assert!( io.try_recv_err().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.out(), " " ); assert!( io.try_recv_err_line().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.out(), "\n" ); assert!( io.try_recv().is_some() ); }
{ let io = Altio::default(); echo!( -n, io.out(), "\n" ); assert!( io.try_recv_line().is_some() ); }
{ let io = Altio::default(); echo!( -n, io.out(), "\n" ); assert!( io.try_recv_err().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.out(), "\n" ); assert!( io.try_recv_err_line().is_none() ); }
}
#[test]
fn io_println() {
{ let io = Altio::default(); echo!( io.out(), "" ); assert!( io.try_recv().is_some() ); }
{ let io = Altio::default(); echo!( io.out(), "" ); assert!( io.try_recv_line().is_some() ); }
{ let io = Altio::default(); echo!( io.out(), "" ); assert!( io.try_recv_err().is_none() ); }
{ let io = Altio::default(); echo!( io.out(), "" ); assert!( io.try_recv_err_line().is_none() ); }
}
#[test]
fn io_eprint() {
{ let io = Altio::default(); echo!( -n, io.err(), "" ); assert!( io.try_recv().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.err(), "" ); assert!( io.try_recv_line().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.err(), "" ); assert!( io.try_recv_err().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.err(), "" ); assert!( io.try_recv_err_line().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.err(), " " ); assert!( io.try_recv().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.err(), " " ); assert!( io.try_recv_line().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.err(), " " ); assert!( io.try_recv_err().is_some() ); }
{ let io = Altio::default(); echo!( -n, io.err(), " " ); assert!( io.try_recv_err_line().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.err(), "\n" ); assert!( io.try_recv().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.err(), "\n" ); assert!( io.try_recv_line().is_none() ); }
{ let io = Altio::default(); echo!( -n, io.err(), "\n" ); assert!( io.try_recv_err().is_some() ); }
{ let io = Altio::default(); echo!( -n, io.err(), "\n" ); assert!( io.try_recv_err_line().is_some() ); }
}
#[test]
fn io_eprintln() {
{ let io = Altio::default(); echo!( io.err(), "" ); assert!( io.try_recv().is_none() ); }
{ let io = Altio::default(); echo!( io.err(), "" ); assert!( io.try_recv_line().is_none() ); }
{ let io = Altio::default(); echo!( io.err(), "" ); assert!( io.try_recv_err().is_some() ); }
{ let io = Altio::default(); echo!( io.err(), "" ); assert!( io.try_recv_err_line().is_some() ); }
}
#[test]
fn receive_out() {
let io = Altio::default();
echo!( -n, io.out(), "" );
assert!( io.try_recv().is_none() );
echo!( -n, io.out(), " " );
assert!( io.try_recv_err().is_none() );
assert_eq!( io.try_recv(), Some( " ".to_owned() ));
echo!( -n, io.out(), "abcdefg\nhijklmn\nopq rst\nuvw xyz" );
assert_eq!( io.try_recv_line(), Some( "abcdefg\n".to_owned() ));
assert_eq!( io.recv_line(), "hijklmn\n" );
assert_eq!( io.recv(), "opq rst\nuvw xyz" );
}
#[test]
fn receive_err() {
let io = Altio::default();
echo!( -n, io.err(), "" );
assert!( io.try_recv_err().is_none() );
echo!( -n, io.err(), " " );
assert!( io.try_recv().is_none() );
assert_eq!( io.try_recv_err(), Some( " ".to_owned() ));
echo!( -n, io.err(), "abcdefg\nhijklmn\nopq rst\nuvw xyz" );
assert_eq!( io.try_recv_err_line(), Some( "abcdefg\n".to_owned() ));
assert_eq!( io.recv_err_line(), "hijklmn\n" );
assert_eq!( io.recv_err(), "opq rst\nuvw xyz" );
}
#[test]
fn receive_lines() {
let io = Altio::default();
echo!( -n, io.out(), "abcd\nefg\nhijk\nlmn\nopq\nrst\nuvw\nxyz" );
assert_eq!( io.try_recv_lines(1), Some( "abcd\n".to_owned() ) );
assert_eq!( io.try_recv_lines(2), Some( "efg\nhijk\n".to_owned() ));
assert_eq!( io.try_recv_lines(3), Some( "lmn\nopq\nrst\n".to_owned() ));
assert_eq!( io.try_recv_lines(2), None );
}
#[test]
fn receive_err_lines() {
let io = Altio::default();
echo!( -n, io.err(), "abcd\nefg\nhijk\nlmn\nopq\nrst\nuvw\nxyz" );
assert_eq!( io.try_recv_err_lines(1), Some( "abcd\n".to_owned() ) );
assert_eq!( io.try_recv_err_lines(2), Some( "efg\nhijk\n".to_owned() ));
assert_eq!( io.try_recv_err_lines(3), Some( "lmn\nopq\nrst\n".to_owned() ));
assert_eq!( io.try_recv_err_lines(2), None );
}
#[test]
fn peek_line() {
let io = Altio::default();
echo!( -n, io.out(), "abcd\nefg\nhijk\nlmn\nopq\nrst\nuvw\nxyz" );
assert_eq!( io.peek_line(), Some( "abcd\n".to_owned() ));
assert_eq!( io.peek_line(), Some( "abcd\n".to_owned() ));
assert_eq!( io.recv_line(), "abcd\n".to_owned() );
assert_eq!( io.recv_line(), "efg\n".to_owned() );
}
#[test]
fn peek_err_line() {
let io = Altio::default();
echo!( -n, io.err(), "abcd\nefg\nhijk\nlmn\nopq\nrst\nuvw\nxyz" );
assert_eq!( io.peek_err_line(), Some( "abcd\n".to_owned() ));
assert_eq!( io.peek_err_line(), Some( "abcd\n".to_owned() ));
assert_eq!( io.recv_err_line(), "abcd\n".to_owned() );
assert_eq!( io.recv_err_line(), "efg\n".to_owned() );
}
}