use crate::{catch_flow, push_str};
pub fn join_to(
out: &mut String,
iter: impl Iterator<Item = impl AsRef<str>>,
sep: impl AsRef<str>,
) {
let mut is_first = true;
for s in iter {
match is_first {
true => is_first = false,
false => out.push_str(sep.as_ref()),
}
out.push_str(s.as_ref());
}
}
pub fn join_to_new(iter: impl Iterator<Item = impl AsRef<str>>, sep: impl AsRef<str>) -> String {
catch_flow!(join_to; iter, sep)
}
pub fn join_to_multi(
out: &mut String,
iter: impl Iterator<Item = impl AsRef<str>>,
separators: &[impl AsRef<str>],
) {
let mut is_first = true;
for s in iter {
match is_first {
true => is_first = false,
false => {
for sep in separators {
push_str!(out; sep.as_ref());
}
}
}
out.push_str(s.as_ref());
}
}
pub fn join_to_multi_new(
iter: impl Iterator<Item = impl AsRef<str>>,
sep: &[impl AsRef<str>],
) -> String {
catch_flow!(join_to_multi; iter, sep)
}
pub fn add_space_if_necessary_and_flush_buffer(
out: &mut String,
buffer: &mut String,
separator: impl AsRef<str>,
) {
match buffer.is_empty() {
true => {}
false => {
push_str!(out; separator.as_ref(), buffer);
buffer.clear();
}
}
}
pub fn join_lest_multiple_separators<S>(
out: &mut String,
mut elements: impl Iterator<Item = S>,
separator: impl AsRef<str>,
) where
S: AsRef<str>,
{
match elements.next() {
Some(s) => out.push_str(s.as_ref()),
None => return,
};
for element in elements {
match element.as_ref().is_empty() {
true => continue,
false => push_str!(out; separator.as_ref(), element.as_ref()),
}
}
}
pub trait JoinTo {
fn join_to<S>(self, out: &mut String, sep: impl AsRef<str>)
where
Self: Iterator<Item = S> + Sized,
S: AsRef<str>,
{
join_to(out, self, sep)
}
fn join_to_new<S>(self, sep: impl AsRef<str>) -> String
where
Self: Iterator<Item = S> + Sized,
S: AsRef<str>,
{
join_to_new(self, sep)
}
fn join_to_multi<S>(self, out: &mut String, sep: &[impl AsRef<str>])
where
Self: Iterator<Item = S> + Sized,
S: AsRef<str>,
{
join_to_multi(out, self, sep)
}
fn join_to_multi_new<S>(self, sep: &[impl AsRef<str>]) -> String
where
Self: Iterator<Item = S> + Sized,
S: AsRef<str>,
{
join_to_multi_new(self, sep)
}
}
impl<T> JoinTo for T {}
mod macro_join_to {
pub trait MacroJoinable<Suffix> {
fn join_to(self, suffix: Suffix);
}
impl MacroJoinable<&str> for &mut String {
fn join_to(self, suffix: &str) {
self.push_str(suffix);
}
}
impl MacroJoinable<&String> for &mut String {
fn join_to(self, suffix: &String) {
self.push_str(suffix);
}
}
impl MacroJoinable<String> for &mut String {
fn join_to(self, suffix: String) {
self.push_str(&suffix); }
}
impl MacroJoinable<char> for &mut String {
fn join_to(self, suffix: char) {
self.push(suffix);
}
}
#[macro_export]
macro_rules! join {
(@EX {# $ex:expr}) => {
format!("{}", $ex)
};
(@EX {# $ex:expr ; $($fmt:tt)*}) => {
format!(concat!("{:", stringify!($($fmt)*), "}"), $ex)
};
(@EX {# $fmt:literal in $($ex:tt)*}) => {
format!($fmt, $($ex)*)
};
(@EX $ex:expr) => {
$ex
};
(
=> $string:tt
$( => $($tail:tt)*)?
) => {
{
let mut string_mut = $crate::join!(@EX $string);
$crate::join!(&mut string_mut $( => $($tail)*)?);
string_mut
}
};
(
=> $string:expr
$( => $($tail:tt)*)?
) => {
$crate::join!(
=> ($string) $( => $($tail)*)?
)
};
(
$string:expr
=> $ex:tt
$( => $($tail:tt)*)?
) => {
$crate::MacroJoinable::join_to(
$string,
$crate::join!(@EX $ex) );
$crate::join!($string $( => $($tail)*)?);
};
(
$string:expr
=> $ex:tt if $condition:expr
$( => $($tail:tt)*)?
) => {
if $condition {
$crate::MacroJoinable::join_to(
$string,
$crate::join!(@EX $ex)
);
}
$crate::join!($string $( => $($tail)*)?);
};
(
$string:expr
=> $ex:tt if let $pattern:pat = $condition:expr
$( => $($tail:tt)*)?
) => {
if let $pattern = $condition {
$crate::MacroJoinable::join_to(
$string,
$crate::join!(@EX $ex)
);
}
$crate::join!($string $( => $($tail)*)?);
};
(
$string:expr
=> $ex:tt while $condition:expr
$( => $($tail:tt)*)?
) => {
while $condition {
$crate::MacroJoinable::join_to(
$string,
$crate::join!(@EX $ex)
);
}
$crate::join!($string $( => $($tail)*)?);
};
(
$string:expr
=> $ex:tt while let $pattern:pat = $condition:expr
$( => $($tail:tt)*)?
) => {
while let $pattern = $condition {
$crate::MacroJoinable::join_to(
$string,
$crate::join!(@EX $ex)
);
}
$crate::join!($string $( => $($tail)*)?);
};
(
$string:expr
=> $ex:tt for $pattern:pat in $iter:expr
$( => $($tail:tt)*)?
) => {
for $pattern in $iter {
$crate::MacroJoinable::join_to(
$string,
$crate::join!(@EX $ex)
);
}
$crate::join!($string $( => $($tail)*)?);
};
(
$string:expr
=> $ex:expr
$( => $($tail:tt)*)?
) => {
$crate::join!(
$string
=> ($ex) $( => $($tail)*)?
);
};
( $string:expr ) => {};
}
}
pub use macro_join_to::*;
#[cfg(test)]
mod tests {
use super::*;
use crate::{asserts, catch_flow};
#[test]
fn test_join_to() {
asserts! {
catch_flow!(join_to; ["a", "b", "c"].iter(), ",") => "a,b,c",
["a", "b", "c"].iter().join_to_new(",") => "a,b,c"
catch_flow!(
join_to;
[
String::from("a"),
String::from("b"),
String::from("c"),
].iter(),
String::from(","),
) => "a,b,c"
catch_flow!(join_to_multi; ["a", "b", "c"].iter(), &[",", " "]) => "a, b, c"
catch_flow!(join_to_multi; ["a", "b", "c"].iter(), &[",".to_owned(), " ".to_owned()]) => "a, b, c",
["a", "b", "c"].iter().join_to_multi_new(&[",", " "]) => "a, b, c"
}
}
#[test]
fn test_add_space_if_necessary_and_flush_buffer() {
asserts! {
{
let mut s = String::from("A");
let mut buffer = String::from("B");
add_space_if_necessary_and_flush_buffer(&mut s, &mut buffer, ",");
(s, buffer)
} => ("A,B".into(), "".into())
{
let mut s = String::from("A");
let mut buffer = String::from("");
add_space_if_necessary_and_flush_buffer(&mut s, &mut buffer, ",");
(s, buffer)
} => ("A".into(), "".into())
}
}
#[test]
fn test_join_lest_multiple_separators() {
asserts! {
catch_flow!(
join_lest_multiple_separators;
["A", "B", "C"].iter(),
", "
) => "A, B, C"
catch_flow!(
join_lest_multiple_separators;
["A", "B", "", "C"].iter(),
", "
) => "A, B, C"
}
}
}