Skip to content

Commit

Permalink
Update documentation and test; add 2 traits for output
Browse files Browse the repository at this point in the history
  • Loading branch information
TheVeryDarkness committed Aug 30, 2024
1 parent 35fd6c0 commit 6a9039a
Show file tree
Hide file tree
Showing 17 changed files with 529 additions and 69 deletions.
2 changes: 1 addition & 1 deletion src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl<'a, T, const N: usize> ArrayGuard<'a, T, N> {

/// Use [usize::unchecked_add] if it's stablized.
pub(crate) unsafe fn push_unchecked(&mut self, value: T) {
self.array.get_unchecked_mut(self.len).write(value);
let _ = self.array.get_unchecked_mut(self.len).write(value);
// Safety: We just wrote to the array.
self.len = self.len.wrapping_add(1);
}
Expand Down
2 changes: 1 addition & 1 deletion src/formatted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ where
I::IntoIter: Clone,
{
fn sep_by(self, sep: &'_ str) -> sep_by::SepBy<'_, Self::IntoIter> {
super::sep_by::SepBy::new(self.into_iter(), sep)
sep_by::SepBy::new(self.into_iter(), sep)
}
}
165 changes: 162 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#![forbid(missing_docs, rust_2021_compatibility, rust_2018_idioms)]
#![forbid(missing_docs)]
#![forbid(rust_2021_compatibility, rust_2018_idioms, future_incompatible)]
#![forbid(unused_imports, unused_qualifications, unused_results)]
#![forbid(
clippy::correctness,
clippy::suspicious,
Expand All @@ -10,13 +12,169 @@
#![forbid(clippy::should_panic_without_expect, clippy::incompatible_msrv)]
//! A utility library for reading data from input
//! and writting data from output.
//!
//! # Input
//!
//! Some lower-level functions are provided to read a single data item from input:
//!
//! - [read_single<T>()] (or [try_read_single<T>()]) reads a ASCII-whitespace-separated fragment of string,
//! and converts it to a value of `T`.
//!
//! The fragment is a string that does not contain any ASCII whitespace characters, and
//! ASCII whitespace characters here are defined as `[' ', '\t', '\n', '\r']`.
//!
//! For example, given the input below:
//!
//! ```txt
//! 1 2,3
//! 3 4;sa
//! ```
//!
//! If you call [read_single<T>()] for 4 times, it will read 4 string fragments `1`, `2,3`, `3`, and `4;sa`.
//!
//! - [read_line<T>()] (or [try_read_line<T>()]) reads a non-empty line of string from input,
//! trims the trailing newline, and converts it to a value of `T`.
//!
//! If all characters in remained part of current line are ASCII whitespace characters,
//! it will discard current line and read one more line, otherwise it will return remained part of current line.
//!
//! For example, given the input below:
//!
//! ```txt
//! 1 2 3
//! 4 5 6
//! ```
//!
//! If you call [read_line<T>()] for 2 times, it will read `1 2 3` and `4 5 6` as two lines of string.
//!
//! And given the input below:
//!
//! ```txt
//! 1 2 3
//! ```
//!
//! If you call [read_single<T>()] once and [read_line<T>()] once, they will read `1` and `2 3` respectively.
//!
//! If you call [read_line<T>()] once more, it will panic because there is no non-empty line remained.
//!
//! Again, given the input below:
//!
//! ```txt
//! 1 2 3
//! 4 5 6
//! ```
//!
//! If you call [read_single<T>()] for 3 times and [read_line<T>()] for 1 time, they will read `1`, `2`, `3`, and `4 5 6` respectively.
//! - [read_remained_line<T>()] (or [try_read_remained_line<T>()]) reads the remained line from input regardless of whether it is empty or not,
//! trims the trailing newline, and converts it to a value of `T`.
//!
//! For example, given the input below:
//!
//! ```txt
//! 1 2 3
//! 4 5 6
//! ```
//!
//! If you call [read_single<T>] for 3 times and [read_remained_line<T>] for 1 time, they will read `1`, `2`, `3`, and an empty string respectively.
//!
//! If you call [read_remained_line<T>] once more, it will still read an empty string.
//!
//! - [read_char<T>] (or [try_read_char<T>]) reads a single non-ASCII-whitespace character from input and converts it to a value of `T`.
//!
//! For example, given the input below:
//!
//! ```txt
//! 1 2 3
//! ```
//!
//! If you call [read_char] for 3 times, it will read `1`, `2`, and `3` as three characters.
//!
//! These functions are implemented for types that implement [ReadIntoSingle] trait. Currently, the following types implement [ReadIntoSingle] trait:
//!
//! - [String];
//! - [char];
//! - [u8], [u16], [u32], [u64], [u128], [usize];
//! - [i8], [i16], [i32], [i64], [i128], [isize];
//! - [f32], [f64];
//! - [bool];
//! - [std::num::NonZeroU8], [std::num::NonZeroU16], [std::num::NonZeroU32], [std::num::NonZeroU64], [std::num::NonZeroU128], [std::num::NonZeroUsize];
//! - [std::num::NonZeroI8], [std::num::NonZeroI16], [std::num::NonZeroI32], [std::num::NonZeroI64], [std::num::NonZeroI128], [std::num::NonZeroIsize];
//! - ...
//!
//! Some higher-level functions are provided to read data sequence (a single item is also a sequnce) from input:
//!
//! - [read<T>()] (or [try_read()]) reads a single sequence from input and converts it to a value of `T`.
//! - [read_n<T>(n)] (or [try_read_n<T>(n)]) reads `n`` sequences from input and converts them to a value of [Vec<T>].
//! - [read_m_n<T>(m, n)] (or [try_read_m_n<T>(m, n)]) reads `m * n` sequences from input and converts them to a value of [Mat<T>].
//!
//! These functions are implemented for types that implement [ReadInto] trait. Currently, the following types implement [ReadInto] trait:
//!
//! - All types that implement [ReadIntoSingle] trait;
//! - `[T; N]` where `T` implements [ReadInto] trait;
//! - `Box<[T; N]>` where `T` implements [ReadInto] trait.
//! - Tuple types, e.g., `(T1, T2, ..., Tn)`, where `Ti` implements [ReadInto] trait and `n` is neither 0 nor more than 12.
//! - ...
//!
//! ## Complex Examples for Input
//!
//! Function [read] is the simplest way to read data from [standard input](std::io::Stdin).
//!
//! Given the input below:
//!
//! ```txt
//! 42
//! Hello, World!
//! 1 2 3 4
//! 1 2
//! 3 4
//! ```
//!
//! Code below reads the input and stores it in variables:
//!
//! ```rust,no_run
//! use iof::read;
//!
//! // Read a single integer from input, whose type is inferred from the context.
//! let n: u32 = read();
//! assert_eq!(n, 42);
//!
//! // Read a string from input.
//! let s: String = read();
//! assert_eq!(s, "Hello, World!");
//!
//! // Read an array of integers from input.
//! let arr: [u32; 4] = read();
//! assert_eq!(arr, [1, 2, 3, 4]);
//!
//! // Read a nested array of integers from input.
//! let arr: [[u32; 2]; 2] = read();
//! assert_eq!(arr, [[1, 2], [3, 4]]);
//! ```
//!
//! # Output
//!
//! Some lower-level functions are provided to write a data sequence with customizing format to output:
//!
//! - [SepBy::sep_by()] writes a sequence of data items, which implements [IntoIterator], to output with a separator.
//!
//! There won't be any separator before the first item or after the last item.
//!
//! For example, given the code below:
//!
//! ```rust
//! use iof::SepBy;
//! let v = vec![1, 2, 3];
//! let s = format!("{}", v.sep_by(", "));
//! assert_eq!(s, "1, 2, 3");
//! ```
pub use {
formatted::SepBy,
mat::Mat,
read_into::{parse::Parse, ReadInto, ReadIntoError, ReadIntoSingle},
read_into::{from_str::FromStr, ReadInto, ReadIntoError, ReadIntoSingle},
stdio::read_into::*,
stream::InputStream,
stream::{InputStream, OutputStream},
write_into::{display::Display, WriteInto},
};

mod array;
Expand All @@ -26,3 +184,4 @@ mod read_into;
mod sep_by;
mod stdio;
mod stream;
mod write_into;
30 changes: 15 additions & 15 deletions src/read_into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ use crate::{
mat::Mat,
stream::{InputStream, RealAll},
};
use parse::Parse;
use from_str::FromStr;
use std::{
fmt::{Debug, Display},
fmt::{self, Debug, Display},
io::BufRead,
};

pub(super) mod parse;
pub(super) mod from_str;

/// Error during using [ReadInto] or [ReadIntoSingle].
///
Expand All @@ -25,7 +25,7 @@ impl<E> Debug for ReadIntoError<E>
where
E: std::error::Error,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IOError(error) => Debug::fmt(error, f),
Self::FromStrError(error) => Debug::fmt(error, f),
Expand All @@ -37,7 +37,7 @@ impl<E> Display for ReadIntoError<E>
where
E: std::error::Error,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IOError(error) => Display::fmt(error, f),
Self::FromStrError(error) => Display::fmt(error, f),
Expand Down Expand Up @@ -93,32 +93,32 @@ pub trait ReadIntoSingle<T> {
}
}

impl<T: Parse, B: BufRead> ReadIntoSingle<T> for InputStream<B> {
impl<T: FromStr, B: BufRead> ReadIntoSingle<T> for InputStream<B> {
type Error = ReadIntoError<T::Err>;
fn try_read_single(&mut self) -> Result<T, Self::Error> {
let res = self
.consume_string(|s| T::parse(s))
.consume_string(|s| T::from_str(s))
.map_err(ReadIntoError::IOError)?
.map_err(ReadIntoError::FromStrError)?;
Ok(res)
}
fn try_read_remained_line(&mut self) -> Result<T, Self::Error> {
let res = self
.consume_remained_line(|s| T::parse(s))
.consume_remained_line(|s| T::from_str(s))
.map_err(ReadIntoError::IOError)?
.map_err(ReadIntoError::FromStrError)?;
Ok(res)
}
fn try_read_line(&mut self) -> Result<T, Self::Error> {
let res = self
.consume_line(|s| T::parse(s))
.consume_line(|s| T::from_str(s))
.map_err(ReadIntoError::IOError)?
.map_err(ReadIntoError::FromStrError)?;
Ok(res)
}
fn try_read_char(&mut self) -> Result<T, Self::Error> {
let c = self.consume_char().map_err(ReadIntoError::IOError)?;
let res = T::parse(&c.to_string()).map_err(ReadIntoError::FromStrError)?;
let res = T::from_str(&c.to_string()).map_err(ReadIntoError::FromStrError)?;
Ok(res)
}
}
Expand Down Expand Up @@ -174,7 +174,7 @@ pub trait ReadInto<T> {
}
}

impl<T: Parse, B: BufRead> ReadInto<T> for InputStream<B> {
impl<T: FromStr, B: BufRead> ReadInto<T> for InputStream<B> {
type Error = ReadIntoError<T::Err>;
fn try_read(&mut self) -> Result<T, Self::Error> {
self.try_read_single()
Expand Down Expand Up @@ -250,15 +250,15 @@ macro_rules! impl_read_into_for_tuple {
pub enum $e<$($t, )* > {
$($t($t), )*
}
impl<$($t: std::error::Error, )* > std::fmt::Display for $e<$($t, )* > {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<$($t: std::error::Error, )* > Display for $e<$($t, )* > {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
$(Self::$t(err) => std::fmt::Display::fmt(err, f), )*
$(Self::$t(err) => Display::fmt(err, f), )*
}
}
}
impl<$($t: std::error::Error, )* > std::error::Error for $e<$($t, )* > {}
impl<$($t: Parse, )* S: $(ReadInto<$t> +)* ?Sized> ReadInto<($($t, )*)> for S {
impl<$($t: FromStr, )* S: $(ReadInto<$t> +)* ?Sized> ReadInto<($($t, )*)> for S {
type Error = $e<$(<S as ReadInto<$t>>::Error, )*>;
fn try_read(&mut self) -> Result<($($t, )*), Self::Error> {
Ok(( $(ReadInto::<$t>::try_read(self).map_err($e::$t)?, )* ))
Expand Down
20 changes: 10 additions & 10 deletions src/read_into/parse.rs → src/read_into/from_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,32 @@ use std::{
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
},
path::PathBuf,
str::FromStr,
str,
};

/// Parse a string into a type. Similar to [std::str::FromStr], but we need this to avoid conflicting.
/// Parse a value from a string. Similar to [str::FromStr], but we need this to avoid conflicting.
///
/// # Example
///
/// ```rust
/// use iof::Parse;
/// let x: i32 = Parse::parse("42").unwrap();
/// use iof::FromStr;
/// let x: i32 = FromStr::from_str("42").unwrap();
/// assert_eq!(x, 42);
/// ```
pub trait Parse: Sized {
pub trait FromStr: Sized {
/// Error that comes from [Parse].
type Err: std::error::Error;

/// Parse a string into a type.
fn parse(s: &str) -> Result<Self, Self::Err>;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}

macro_rules! impl_parse {
($ty:ty) => {
impl Parse for $ty {
type Err = <$ty as FromStr>::Err;
fn parse(s: &str) -> Result<Self, Self::Err> {
<$ty as FromStr>::from_str(s)
impl FromStr for $ty {
type Err = <$ty as str::FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
<$ty as str::FromStr>::from_str(s)
}
}
};
Expand Down
Loading

0 comments on commit 6a9039a

Please sign in to comment.