Skip to content

Commit

Permalink
Add tests and update documents.
Browse files Browse the repository at this point in the history
  • Loading branch information
TheVeryDarkness committed Aug 11, 2024
1 parent 45fc5ec commit b218365
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 95 deletions.
2 changes: 1 addition & 1 deletion src/formatted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::sep_by;

/// [std::fmt::Display] with given separator.
pub trait SepBy: IntoIterator {
/// Create a [sep_by::SepBy] with given separator.
/// Create an iterator that implement [core::fmt::Display] using given separator.
fn sep_by(self, sep: &'_ str) -> sep_by::SepBy<'_, Self::IntoIter>;
}

Expand Down
7 changes: 3 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@
clippy::style,
clippy::cargo
)]
#![forbid(clippy::should_panic_without_expect, clippy::incompatible_msrv)]
//! A utility library for reading integers, floating numbers and strings from input/output.
pub use {
formatted::SepBy,
mat::Mat,
read_into::{
read, read_array, read_m_n, read_n, read_tuple, try_read, try_read_array, try_read_m_n,
try_read_n, try_read_tuple, ReadInto, ReadIntoError,
},
read_into::{ReadInto, ReadIntoError},
stdio::read_into::*,
stream::InputStream,
};

Expand Down
20 changes: 13 additions & 7 deletions src/mat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ impl<T> Mat<T> {
None
}
}
/// Convert `self` into a [RowIter].
/// Convert `self` into an iterator over all rows.
pub fn iter(&self) -> RowIter<'_, T> {
RowIter::new(self)
}
Expand Down Expand Up @@ -177,9 +177,9 @@ impl<T, const M: usize, const N: usize> From<[[T; N]; M]> for Mat<T> {
trait AsSlice {
type Item;
fn as_slice(&self) -> &[Self::Item];
fn len(&self) -> usize {
self.as_slice().len()
}
// fn len(&self) -> usize {
// self.as_slice().len()
// }
}
impl<T> AsSlice for [T] {
type Item = T;
Expand All @@ -198,9 +198,15 @@ impl<T, const N: usize> AsSlice for [T; N] {
fn as_slice(&self) -> &[T] {
self
}
#[inline]
fn len(&self) -> usize {
N
// #[inline]
// fn len(&self) -> usize {
// N
// }
}
impl<S: AsSlice + ?Sized> AsSlice for &S {
type Item = S::Item;
fn as_slice(&self) -> &[S::Item] {
(*self).as_slice()
}
}

Expand Down
26 changes: 26 additions & 0 deletions src/mat/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ fn empty() {
assert_eq!(mat.len_rows(), 0);
assert_eq!(mat.len_columns(), 0);
assert_eq!(mat.iter().count(), 0);
assert_eq!(mat.iter().size_hint(), (0, Some(0)));
assert_eq!(mat.iter().last(), None);
assert_eq!(format!("{:?}", mat), "[]");
assert_eq!(mat, Mat::from_iter([] as [[u32; 0]; 0]));
assert_eq!(mat, Mat::from_iter([] as [[u32; 1]; 0]));
assert_eq!(mat, Mat::from_iter(&[] as &[[u32; 0]; 0]));
// assert_eq!(mat, Mat::from_iter(&[] as &[[u32; 1]; 0]));
assert_eq!(mat, Mat::from_iter([] as [&[u32]; 0]));
}

#[test]
Expand Down Expand Up @@ -89,6 +97,20 @@ fn unit() {
[0, 0, 0, 1, 0],
[0, 0, 0, 0, 1],
];
let vec_ref_arr = vec![
&[1, 0, 0, 0, 0],
&[0, 1, 0, 0, 0],
&[0, 0, 1, 0, 0],
&[0, 0, 0, 1, 0],
&[0, 0, 0, 0, 1],
];
let vec_slice_arr = vec![
[1, 0, 0, 0, 0].as_slice(),
[0, 1, 0, 0, 0].as_slice(),
[0, 0, 1, 0, 0].as_slice(),
[0, 0, 0, 1, 0].as_slice(),
[0, 0, 0, 0, 1].as_slice(),
];
let vec_vec = vec![
vec![1, 0, 0, 0, 0],
vec![0, 1, 0, 0, 0],
Expand All @@ -102,6 +124,10 @@ fn unit() {
assert_eq!(b, arr_vec);
assert_eq!(a, vec_arr);
assert_eq!(b, vec_arr);
assert_eq!(a, vec_ref_arr);
assert_eq!(b, vec_ref_arr);
assert_eq!(a, vec_slice_arr);
assert_eq!(b, vec_slice_arr);
assert_eq!(a, vec_vec);
assert_eq!(b, vec_vec);
assert_eq!(Mat::from(arr_arr), a);
Expand Down
99 changes: 17 additions & 82 deletions src/read_into.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::{
mat::Mat,
stdio::STDIN,
stream::{InputStream, RealAll},
};
use std::{
Expand Down Expand Up @@ -50,6 +49,12 @@ macro_rules! unwrap {
}

/// Read data from input stream.
///
/// # Errors
///
/// - If the input cannot be parsed into `T`, [ReadIntoError::FromStrError] is returned.
/// - If the input is not valid UTF-8, [ReadIntoError::IOError] is returned.
/// - If an I/O error occurs, [ReadIntoError::IOError] is returned.
pub trait ReadInto<T> {
/// Errors that come from [ReadInto].
type Error: std::error::Error;
Expand Down Expand Up @@ -117,6 +122,12 @@ pub trait ReadInto<T> {
fn read_line(&mut self) -> T {
unwrap!(self.try_read_line())
}
/// Read an element in a single non-whitespace character from `self`, parse into `T`.
fn try_read_char(&mut self) -> Result<T, Self::Error>;
/// Unwrapping version of [ReadInto::try_read_char].
fn read_char(&mut self) -> T {
unwrap!(self.try_read_char())
}
/// Read all remaining elements from `self`.
fn read_all(&mut self) -> RealAll<'_, Self, T> {
RealAll::new(self)
Expand Down Expand Up @@ -177,85 +188,9 @@ where
.map_err(ReadIntoError::FromStrError)?;
Ok(res)
}
}

/// Read from [std::io::Stdin] and parse into `T`.
pub fn try_read<T: FromStr>() -> Result<T, ReadIntoError<T>>
where
T::Err: std::error::Error,
{
STDIN.with(|lock| lock.borrow_mut().try_read())
}

/// Unwrapping version of [try_read].
pub fn read<T: FromStr>() -> T
where
T::Err: std::error::Error,
{
STDIN.with(|lock| lock.borrow_mut().read())
}

/// Read `n` elements from [std::io::Stdin] and parse into `Vec<T>`.
pub fn try_read_n<T: FromStr>(n: usize) -> Result<Vec<T>, ReadIntoError<T>>
where
T::Err: std::error::Error,
{
STDIN.with(|lock| lock.borrow_mut().try_read_n(n))
}

/// Unwrapping version of [try_read_n].
pub fn read_n<T: FromStr>(n: usize) -> Vec<T>
where
T::Err: std::error::Error,
{
STDIN.with(|lock| lock.borrow_mut().read_n(n))
}

/// Read `n` elements from [std::io::Stdin] and parse into `Vec<T>`.
pub fn try_read_m_n<T: FromStr>(m: usize, n: usize) -> Result<Mat<T>, ReadIntoError<T>>
where
T::Err: std::error::Error,
{
STDIN.with(|lock| lock.borrow_mut().try_read_m_n(m, n))
}

/// Unwrapping version of [try_read_n].
pub fn read_m_n<T: FromStr>(m: usize, n: usize) -> Mat<T>
where
T::Err: std::error::Error,
{
STDIN.with(|lock| lock.borrow_mut().read_m_n(m, n))
}

/// Read `n` elements from [std::io::Stdin] and parse into `Vec<T>`.
pub fn try_read_array<T: FromStr, const N: usize>() -> Result<Box<[T; N]>, ReadIntoError<T>>
where
T::Err: std::error::Error,
{
STDIN.with(|lock| lock.borrow_mut().try_read_array())
}

/// Unwrapping version of [try_read_array].
pub fn read_array<T: FromStr, const N: usize>() -> Box<[T; N]>
where
T::Err: std::error::Error,
{
STDIN.with(|lock| lock.borrow_mut().read_array())
}

/// Read `n` elements from [std::io::Stdin] and parse into `Vec<T>`.
pub fn try_read_tuple<T: FromStr, U: MonoTuple<T, InputStream<std::io::StdinLock<'static>>>>(
) -> Result<U, ReadIntoError<T>>
where
T::Err: std::error::Error,
{
STDIN.with(|lock| lock.borrow_mut().try_read_tuple())
}

/// Unwrapping version of [try_read_tuple].
pub fn read_tuple<T: FromStr, U: MonoTuple<T, InputStream<std::io::StdinLock<'static>>>>() -> U
where
T::Err: std::error::Error,
{
STDIN.with(|lock| lock.borrow_mut().read_tuple())
fn try_read_char(&mut self) -> Result<T, Self::Error> {
let c = self.consume_char().map_err(ReadIntoError::IOError)?;
let res = T::from_str(&c.to_string()).map_err(ReadIntoError::FromStrError)?;
Ok(res)
}
}
4 changes: 3 additions & 1 deletion src/stdio.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::stream::InputStream;
use std::{cell::RefCell, io::StdinLock};

pub(crate) mod read_into;

thread_local! {
pub static STDIN: RefCell<InputStream<StdinLock<'static>>> = RefCell::new(InputStream::new(std::io::stdin().lock()));
pub(crate) static STDIN: RefCell<InputStream<StdinLock<'static>>> = RefCell::new(InputStream::new(std::io::stdin().lock()));
}
96 changes: 96 additions & 0 deletions src/stdio/read_into.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use crate::{
mat::Mat,
read_into::{MonoTuple, ReadInto, ReadIntoError},
stdio::STDIN,
stream::InputStream,
};
use std::str::FromStr;

macro_rules! expose_stdin {
($try_fn:ident $str_try_fn:literal $fn:ident $str_fn:literal [$($ty_arg:tt)*] ($($arg:ident: $arg_ty:ty), *) -> $ret:ty) => {
/// Call [`
#[doc = $str_try_fn]
/// `] on [std::io::StdinLock].
///
/// # Panics
///
/// If [
#[doc = $str_try_fn]
/// ] panics.
///
/// # Errors
///
/// If this function is called in multiple threads, the behavior is undefined, possibly causing a deadlock.
///
/// If [
#[doc = $str_try_fn]
/// ] returns an error.
pub fn $try_fn<$($ty_arg)*>($($arg: $arg_ty),*) -> Result<$ret, ReadIntoError<T>>
where
T::Err: std::error::Error,
{
STDIN.with(|lock| lock.borrow_mut().$try_fn($($arg),*))
}

/// Call [`
#[doc = $str_fn]
/// `] on [std::io::StdinLock].
///
/// # Panics
///
/// If [
#[doc = $str_try_fn]
/// ] returns an error or panics.
///
/// # Errors
///
/// If this function is called in multiple threads, the behavior is undefined, possibly causing a deadlock.
pub fn $fn<$($ty_arg)*>($($arg: $arg_ty),*) -> $ret
where
T::Err: std::error::Error,
{
STDIN.with(|lock| lock.borrow_mut().$fn($($arg),*))
}
};
}

expose_stdin!(
try_read "ReadInto::try_read"
read "ReadInto::read"
[T: FromStr] () -> T
);
expose_stdin!(
try_read_n "ReadInto::try_read_n"
read_n "ReadInto::read_n"
[T: FromStr] (n: usize) -> Vec<T>
);
expose_stdin!(
try_read_m_n "ReadInto::try_read_m_n"
read_m_n "ReadInto::read_m_n"
[T: FromStr] (m: usize, n: usize) -> Mat<T>
);
expose_stdin!(
try_read_array "ReadInto::try_read_array"
read_array "ReadInto::read_array"
[T: FromStr, const N: usize] () -> Box<[T; N]>
);
expose_stdin!(
try_read_tuple "ReadInto::try_read_tuple"
read_tuple "ReadInto::read_tuple"
[T: FromStr, U: MonoTuple<T, InputStream<std::io::StdinLock<'static>>>] () -> U
);
expose_stdin!(
try_read_remained_line "ReadInto::try_read_remained_line"
read_remained_line "ReadInto::read_remained_line"
[T: FromStr] () -> T
);
expose_stdin!(
try_read_line "ReadInto::try_read_line"
read_line "ReadInto::read_line"
[T: FromStr] () -> T
);
expose_stdin!(
try_read_char "ReadInto::try_read_char"
read_char "ReadInto::read_char"
[T: FromStr] () -> T
);
22 changes: 22 additions & 0 deletions tests/ill_buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use iof::{InputStream, ReadInto};

struct IllBuffer;

impl std::io::Read for IllBuffer {
fn read(&mut self, _: &mut [u8]) -> std::io::Result<usize> {
Err(std::io::Error::new(std::io::ErrorKind::Other, "ill buffer"))
}
}
impl std::io::BufRead for IllBuffer {
fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
Err(std::io::Error::new(std::io::ErrorKind::Other, "ill buffer"))
}
fn consume(&mut self, _: usize) {}
}

#[test]
fn ill_buffer() {
let mut buf = InputStream::new(IllBuffer);
let res: Result<u32, _> = buf.try_read();
assert!(res.is_err());
}
17 changes: 17 additions & 0 deletions tests/integers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@ fn read_3() {
assert!(iof::ReadInto::<u32>::try_read(&mut reader).is_err());
}

#[test]
fn read_char_3() {
let reader = Cursor::new("123".as_bytes());
let mut reader = InputStream::new(reader);

let a: u32 = reader.read_char();
assert_eq!(a, 1);

let b: u32 = reader.read_char();
assert_eq!(b, 2);

let c: u32 = reader.read_char();
assert_eq!(c, 3);

assert!(iof::ReadInto::<u32>::try_read_char(&mut reader).is_err());
}

#[test]
#[should_panic = "invalid digit found in string"]
fn read_sign_error() {
Expand Down
Loading

0 comments on commit b218365

Please sign in to comment.