Skip to content

Commit

Permalink
Add a macro sep_by! and update documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
TheVeryDarkness committed Oct 6, 2024
1 parent 2deef2f commit bad3b18
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 24 deletions.
18 changes: 14 additions & 4 deletions src/formatted.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
use crate::write::{
sep_by::{self},
separator::Separator,
};
use crate::write::{sep_by, separator::Separator};

/// Create an object that implement [core::fmt::Display] using given separator.
#[macro_export(local_inner_macros)]
macro_rules! sep_by {
($iter:expr, $sep:expr, $($residual:expr),+ $(,)?) => {
$crate::SepBy::sep_by(::std::iter::IntoIterator::into_iter($iter).map(|iter| $crate::sep_by!(iter, $($residual, )+)), $sep)
};
($iter:expr, $sep:expr $(,)?) => {
$crate::SepBy::sep_by($iter, $sep)
};
}

/// [std::fmt::Display] with given separator.
///
/// Note that this is a trait, and you can use it with any type that implements [IntoIterator] and whose [IntoIterator::IntoIter] implements [Clone].
///
/// # Examples
///
/// ```rust
Expand Down
14 changes: 12 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@
//!
//! # Output
//!
//! ## [SepBy]
//! ## [SepBy] and [sep_by!]
//!
//! Some lower-level functions are provided to write a data sequence with customizing format to output:
//!
Expand All @@ -238,12 +238,22 @@
//! For example:
//!
//! ```rust
//! use iof::SepBy;
//! use iof::{sep_by, SepBy};
//!
//! let v = vec![1, 2, 3];
//! let s = format!("{}", v.sep_by(", "));
//! assert_eq!(s, "1, 2, 3");
//!
//! let v = vec![vec![1, 2, 3], vec![4, 5, 6]];
//! let s = sep_by!(v, "\n", ", ");
//! // Above line is equivalent to:
//! // let s = v.map(|e| e.sep_by("\n")).sep_by(" ");
//! let s = format!("{}", s);
//! assert_eq!(s, "1, 2, 3\n4, 5, 6");
//! ```
//!
//! Note that the iterator must implement [Clone] trait to use the [SepBy] trait.
//!
//! ## [WriteInto]
//!
//! Some higher-level functions are provided to write data sequence with default format to output:
Expand Down
2 changes: 1 addition & 1 deletion src/read/macros.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[macro_export]
#[macro_export(local_inner_macros)]
/// Read a single data item, a [Vec] or a [Mat] from input using [ReadInto].
///
/// - `read!()` reads a single data item from input.
Expand Down
13 changes: 11 additions & 2 deletions src/write/sep_by.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{ranked::Rank, separator::Separator, WriteInto};
use std::{
fmt::{self, Binary, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex},
fmt::{self, Binary, Debug, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex},
io::Write,
};

Expand All @@ -11,12 +11,20 @@ use std::{
/// [Clone] is required, as this implementation consumes the iterator.
///
/// All configuration on [fmt::Formatter] is delegated to the item type.
#[derive(Debug, Clone)]
pub struct SepBy<'a, I, S: ?Sized> {
sep: &'a S,
iter: I,
}

impl<'a, I: Clone, S: ?Sized> Clone for SepBy<'a, I, S> {
fn clone(&self) -> Self {
Self {
sep: self.sep,
iter: self.iter.clone(),
}
}
}

impl<'a, I: Iterator + Clone, S: Separator + ?Sized> SepBy<'a, I, S> {
/// Create a [SepBy].
pub fn new(iter: I, sep: &'a S) -> Self {
Expand Down Expand Up @@ -47,6 +55,7 @@ macro_rules! impl_for_sep_by {
}

impl_for_sep_by!(Display);
impl_for_sep_by!(Debug);

impl_for_sep_by!(Octal);
impl_for_sep_by!(Binary);
Expand Down
85 changes: 70 additions & 15 deletions tests/sep_by.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use iof::{SepBy, WriteInto};
use std::io::Write;
use iof::{sep_by, SepBy, WriteInto};
use std::{
collections::{BTreeMap, BTreeSet},
io::Write,
};

#[test]
fn hex() {
Expand Down Expand Up @@ -43,28 +46,80 @@ fn limited_buffer_4() {
}

#[test]
fn test_sep_by() {
fn test_macro_set() {
let set = BTreeSet::from_iter([3, 2, 1]);

let x = sep_by!(&set, " :: ");
assert_eq!(x.to_string(), "1 :: 2 :: 3");

let x = sep_by!(set.iter(), " :: ");
assert_eq!(x.to_string(), "1 :: 2 :: 3");
}

#[test]
fn test_macro_map() {
let map = BTreeMap::from_iter([(3, "x"), (2, "y"), (1, "z")]);

let x = sep_by!(map.iter().map(|(k, v)| format!("{}->{}", k, v)), ", ");
assert_eq!(x.to_string(), "1->z, 2->y, 3->x");
}

#[test]
fn test_macro_vec() {
let x = sep_by!([1, 2, 3], " ");
assert_eq!(x.to_string(), "1 2 3");

let x = sep_by!([1, 2, 3], ", ");
assert_eq!(x.to_string(), "1, 2, 3");

let x = sep_by!(vec![1, 2, 3], " ");
assert_eq!(x.to_string(), "1 2 3");

let x = sep_by!(vec![1, 2, 3], ", ");
assert_eq!(x.to_string(), "1, 2, 3");
}

#[test]
fn test_macro_mat() {
let x = sep_by!([[1, 2], [3, 4]], "\n", " ");
assert_eq!(x.to_string(), "1 2\n3 4");
}

#[test]
fn test_macro_tensor() {
let x = sep_by!([[[1, 2], [3, 4]], [[4, 5], [6, 7]]], "\n\n", "\n", " ");
assert_eq!(x.to_string(), "1 2\n3 4\n\n4 5\n6 7");

let x = sep_by!([[[1, 2], [3, 4]], [[4, 5], [6, 7]]], "\n\n", "\n", " ");
assert_eq!(format!("{x:?}"), "1 2\n3 4\n\n4 5\n6 7");
}

#[test]
fn test_vec() {
let s = [1, 2, 3].sep_by(", ");
assert_eq!(s.to_string(), "1, 2, 3");
assert_eq!(s.try_write_into_string().unwrap(), "1, 2, 3");
assert_eq!(
format!("{:?}", s),
"SepBy { sep: \", \", iter: IntoIter([1, 2, 3]) }",
);
assert_eq!(format!("{:?}", s), "1, 2, 3");

let s = ([0i32; 0]).sep_by(", ");
assert_eq!(s.to_string(), "");
assert_eq!(s.try_write_into_string().unwrap(), "");
assert_eq!(
format!("{:?}", s),
"SepBy { sep: \", \", iter: IntoIter([]) }",
);
assert_eq!(format!("{:?}", s), "");

let s = [1].sep_by(", ");
assert_eq!(s.to_string(), "1");
assert_eq!(s.try_write_into_string().unwrap(), "1");
assert_eq!(
format!("{:?}", s),
"SepBy { sep: \", \", iter: IntoIter([1]) }"
);
assert_eq!(format!("{:?}", s), "1");
}

#[test]
fn test_mat() {
let s = [[1, 2], [3, 4]].map(|x| x.sep_by(", ")).sep_by("\n");
assert_eq!(s.to_string(), "1, 2\n3, 4");
}

#[test]
fn test_set() {
let s = [1, 2, 3].iter().sep_by(", ");
assert_eq!(s.to_string(), "1, 2, 3");
}

0 comments on commit bad3b18

Please sign in to comment.