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