const_str/__ctfe/
squish.rs1#![allow(unsafe_code)]
2
3use crate::__ctfe::StrBuf;
4
5pub struct Squish<T>(pub T);
6
7impl Squish<&'_ str> {
8 pub const fn output_len(&self) -> usize {
9 let mut len = 0;
10
11 macro_rules! push {
12 ($x: expr) => {
13 len += 1;
14 };
15 }
16
17 let bytes = self.0.as_bytes();
18 let mut i = 0;
19 while i < bytes.len() {
20 let x = bytes[i];
21
22 if x.is_ascii_whitespace() {
23 let mut j = i + 1;
24 while j < bytes.len() {
25 if bytes[j].is_ascii_whitespace() {
26 j += 1;
27 } else {
28 break;
29 }
30 }
31 if !(i == 0 || j == bytes.len()) {
32 push!(b' ');
33 }
34 i = j;
35 continue;
36 }
37
38 push!(x);
39 i += 1;
40 }
41
42 len
43 }
44
45 pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
46 let mut buf = [0; N];
47 let mut pos = 0;
48
49 macro_rules! push {
50 ($x: expr) => {
51 buf[pos] = $x;
52 pos += 1;
53 };
54 }
55
56 let bytes = self.0.as_bytes();
57 let mut i = 0;
58 while i < bytes.len() {
59 let x = bytes[i];
60
61 if x.is_ascii_whitespace() {
62 let mut j = i + 1;
63 while j < bytes.len() {
64 if bytes[j].is_ascii_whitespace() {
65 j += 1;
66 } else {
67 break;
68 }
69 }
70 if !(i == 0 || j == bytes.len()) {
71 push!(b' ');
72 }
73 i = j;
74 continue;
75 }
76
77 push!(x);
78 i += 1;
79 }
80
81 assert!(pos == N);
82 unsafe { StrBuf::new_unchecked(buf) }
83 }
84}
85
86#[macro_export]
109macro_rules! squish {
110 ($s:expr) => {{
111 const INPUT: &str = $s;
112 const N: usize = $crate::__ctfe::Squish(INPUT).output_len();
113 const OUTPUT: $crate::__ctfe::StrBuf<N> = $crate::__ctfe::Squish(INPUT).const_eval();
114 OUTPUT.as_str()
115 }};
116}
117
118#[cfg(test)]
119mod tessts {
120 fn join<'a>(iter: impl IntoIterator<Item = &'a str>, sep: &str) -> String {
121 let mut ans = String::new();
122 let mut iter = iter.into_iter();
123 match iter.next() {
124 None => return ans,
125 Some(first) => ans.push_str(first),
126 }
127 for part in iter {
128 ans.push_str(sep);
129 ans.push_str(part);
130 }
131 ans
132 }
133
134 fn std_squish(input: &str) -> String {
135 join(input.split_ascii_whitespace(), " ")
136 }
137
138 #[test]
139 fn test_squish() {
140 macro_rules! testcase {
141 ($s:expr) => {{
142 const OUTPUT: &str = squish!($s);
143 let expected = std_squish($s);
144 assert_eq!(OUTPUT, expected);
145 }};
146 }
147
148 testcase!("");
149 testcase!(" ");
150 testcase!(" t");
151 testcase!("t ");
152 testcase!(" t ");
153 testcase!(" t t");
154
155 testcase!(" SQUISH \t THAT \t CAT ");
156
157 testcase!(
158 "
159 All you need to know is to \t
160 SQUISH THAT CAT! \
161 "
162 );
163
164 testcase!(concat!("We\n", "always\n", "SQUISH\n", "THAT\n", "CAT."));
165
166 testcase!(
167 "SELECT
168 name,
169 created_at,
170 updated_at
171 FROM users
172 WHERE id = ?"
173 );
174 }
175}