macro_toolset/string/slice_sep.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
//! A slice, separated by a separator.
use super::{SeparatorT, StringExtT};
#[macro_export]
/// Create a [`SliceSep`].
///
/// # Examples
///
/// Generally, this macro takes a slice of [`StringExtT`] types, like `Vec<T>`
/// or `&[T]` where T implements [`StringExtT`].
///
/// ```
/// # use macro_toolset::{slice_sep, str_concat};
/// let slice_sep = slice_sep!(["a", "b", "c"], ',');
/// assert_eq!(
/// str_concat!("Hello: ", &slice_sep, " World!"),
/// "Hello: a,b,c World!"
/// );
/// let slice_sep = slice_sep!(vec!["a", "b", "c"], ',');
/// assert_eq!(
/// str_concat!(sep = ';'; "TEXT_1", slice_sep, "2_TEXT"),
/// "TEXT_1;a,b,c;2_TEXT"
/// );
/// ```
///
/// [Map](std::iter::Map) is accepted too.
///
/// ```
/// # use macro_toolset::{slice_sep, str_concat};
/// let slice_sep = slice_sep!(["a", "b", "c"].iter().map(|s| s.to_ascii_uppercase()), ',');
/// assert_eq!(
/// str_concat!("Hello: ", slice_sep, " World!"),
/// "Hello: A,B,C World!"
/// );
/// ```
///
/// In fact all types that implements `StringExtT` can be used, though useless
/// and do not do so.
///
/// ```
/// # use macro_toolset::{slice_sep, str_concat};
/// let slice_sep = slice_sep!("a", ',');
/// assert_eq!(
/// str_concat!(sep = ';'; "TEXT_1", &slice_sep, "2_TEXT"),
/// "TEXT_1;a;2_TEXT"
/// );
/// ```
///
/// # Notes
///
/// Take the following as an example:
///
/// ```should_panic
/// # use macro_toolset::{slice_sep, string::StringExtT};
/// let post_ids = vec![1, 2, 3];
/// let l = slice_sep!(
/// post_ids
/// .iter()
/// .map(|id| { slice_sep!(("post_ids[]=", id), "") }),
/// '&'
/// )
/// .to_string_ext();
/// let r = slice_sep!(post_ids.iter().map(|id| { ("post_ids[]=", id) }), '&').to_string_ext();
/// assert_eq!(l, r);
/// ```
///
/// Since the contents in tuple like `("post_ids[]=", id)` are recognized as
/// independent, the separator will be also inserted between them.
///
/// To avoid this, you can use `slice_sep!` with an empty separator to avoid
/// recognizing the contents as independent, while `()` is better:
///
/// ```
/// # use macro_toolset::{slice_sep, string::StringExtT};
/// let post_ids = vec![1, 2, 3];
/// let l = slice_sep!(
/// post_ids
/// .iter()
/// .map(|id| { slice_sep!(("post_ids[]=", id), "") }),
/// '&'
/// )
/// .to_string_ext();
/// let r = slice_sep!(
/// post_ids
/// .iter()
/// .map(|id| { slice_sep!(("post_ids[]=", id)) }), // No separator specified.
/// '&'
/// )
/// .to_string_ext();
/// assert_eq!(l, r);
/// ```
macro_rules! slice_sep {
($inner:expr) => {
$crate::string::SliceSep {
inner: $inner,
separator: (),
}
};
($inner:expr, $sep:expr) => {
$crate::string::SliceSep {
inner: $inner,
separator: $sep,
}
};
}
#[derive(Debug, Clone, Copy)]
/// A slice, separated by a separator.
///
/// The separator you specify like `str_concat!(sep = ',', ...)` will override
/// the one you set when creating [`SliceSep`].
pub struct SliceSep<S, T: StringExtT> {
/// The inner slice.
pub inner: T,
/// The separator.
pub separator: S,
}
impl<S: SeparatorT, T: StringExtT> SliceSep<S, T> {
#[inline]
/// Create a new slice, separated by a separator.
pub const fn new(inner: T, separator: S) -> Self {
Self { inner, separator }
}
}
impl<S: SeparatorT, T: StringExtT> StringExtT for SliceSep<S, T> {
#[inline]
fn push_to_string(self, string: &mut Vec<u8>) {
self.inner
.push_to_string_with_separator(string, self.separator);
self.separator.remove_end(string);
}
}
impl<T: StringExtT> StringExtT for SliceSep<(), T> {
#[inline]
fn push_to_string(self, string: &mut Vec<u8>) {
self.inner.push_to_string(string);
}
}
impl<S: SeparatorT, T: StringExtT + Copy> StringExtT for &SliceSep<S, T> {
#[inline]
fn push_to_string(self, string: &mut Vec<u8>) {
(*self).push_to_string(string);
}
}