1 //! 0-size types for common separators
2 //!
3 //! This modules provides [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html)-implementing
4 //! types for common separators. These types are 0-size, with fixed `Display` implementations,
5 //! intended to aid with compiler optimization.
6
7 // NOTE: we hope that the compiler will detect that most operations on NoSeparator
8 // are no-ops, and optimize heavily, because I'd rather not implement a separate
9 // type for empty-separator-joins.
10
11 use core::fmt::{self, Display, Formatter};
12
13 #[cfg(feature = "token-stream")]
14 use {quote::ToTokens, syn::Token};
15
16 use crate::join::Separator;
17
18 /// Zero-size type representing the empty separator.
19 ///
20 /// This struct can be used as a separator in cases where you simply want to
21 /// join the elements of a separator without any elements between them.
22 ///
23 /// See also the [`join_concat`](crate::Joinable::join_concat) method.
24 ///
25 /// # Examples
26 ///
27 /// ```
28 /// use joinery::JoinableIterator;
29 /// use joinery::separators::NoSeparator;
30 ///
31 /// let parts = (0..10);
32 /// let join = parts.join_with(NoSeparator);
33 /// assert_eq!(join.to_string(), "0123456789");
34 /// ```
35 ///
36 /// *New in 1.1.0*
37 #[derive(Debug, Clone, Copy, Default)]
38 #[must_use]
39 pub struct NoSeparator;
40
41 impl Display for NoSeparator {
42 #[inline(always)]
fmt(&self, _f: &mut Formatter) -> fmt::Result43 fn fmt(&self, _f: &mut Formatter) -> fmt::Result {
44 Ok(())
45 }
46 }
47
48 impl Separator for NoSeparator {}
49
50 #[cfg(feature = "token-stream")]
51 impl ToTokens for NoSeparator {
to_tokens(&self, _tokens: &mut proc_macro2::TokenStream)52 fn to_tokens(&self, _tokens: &mut proc_macro2::TokenStream) {}
53 }
54
55 #[cfg(test)]
56 #[test]
test_no_separator()57 fn test_no_separator() {
58 use crate::join::Joinable;
59 use crate::separators::NoSeparator;
60
61 let data = [1, 2, 3, 4, 5];
62 let join = data.join_with(NoSeparator);
63 let result = join.to_string();
64
65 assert_eq!(result, "12345");
66 }
67
68 macro_rules! const_separator {
69 ($($Name:ident(sep: $sep:expr, repr: $repr:expr, test: $test_name:ident $(, token: $($token:tt)?)? ))+) => {$(
70 #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
71 #[must_use]
72 #[doc = "Zero size type representing the "]
73 #[doc = $repr]
74 #[doc = " separator."]
75 pub struct $Name;
76
77 impl Display for $Name {
78 #[inline]
79 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
80 $sep.fmt(f)
81 }
82 }
83
84 impl Separator for $Name {}
85
86 $(
87 #[cfg(feature="token-stream")]
88 impl ToTokens for $Name {
89 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
90 $(
91 let punct: Token![$token] = Default::default();
92 punct.to_tokens(tokens);
93 )?
94 let _tokens = tokens;
95 }
96 }
97 )?
98
99 #[cfg(test)]
100 mod $test_name {
101 use crate::separators::$Name;
102 use crate::join::Joinable;
103
104 #[test]
105 fn separator() {
106 let data = [1, 2, 3];
107 let join = data.join_with($Name);
108 let result = join.to_string();
109
110 assert_eq!(result, concat!(1, $sep, 2, $sep, 3));
111 }
112
113 $(
114 #[cfg(feature="token-stream")]
115 #[test]
116 fn to_tokens() {
117 use quote::{ToTokens, quote};
118
119 let data = [1, 2, 3];
120 let join = data.join_with($Name);
121 let result = join.into_token_stream();
122
123 let target = quote! {
124 1i32 $($token)? 2i32 $($token)? 3i32
125 };
126
127 assert_eq!(result.to_string(), target.to_string());
128 }
129 )?
130 }
131 )+}
132 }
133
134 const_separator! {
135 Newline(sep: '\n', repr: "newline", test: test_newline, token: )
136 Space(sep: ' ', repr:"space", test: test_space, token: )
137 Comma(sep: ',', repr: "`,`", test: test_comma, token: ,)
138 CommaSpace(sep: ", ", repr: "comma followed by space", test: test_comma_space, token: ,)
139 Dot(sep: '.', repr: "`.`", test: test_dot, token: .)
140 Slash(sep: '/', repr: "`/`", test: test_slash, token: /)
141 Underscore(sep: '_', repr: "`_`", test: test_underscore)
142 Dash(sep: '-', repr: "`-`", test: test_dash, token: -)
143 Tab(sep: '\t', repr: "tab", test: test_tab, token: )
144 }
145