1#[cfg(feature = "feat-string-ext-base64")]
4pub mod base64;
5pub mod externs;
6pub mod general;
7#[cfg(feature = "feat-string-ext-hex")]
8pub mod hex;
9pub mod number;
10#[cfg(feature = "feat-string-ext-rand")]
11pub mod rand;
12#[cfg(feature = "feat-string-ext-urlencoding")]
13pub mod urlencoding;
14
15#[cfg(feature = "feat-string-ext-base64")]
16pub use base64::b64_padding;
17#[cfg(feature = "feat-string-ext-hex")]
18pub use hex::HexStr;
20pub use number::NumStr;
22#[cfg(feature = "feat-string-ext-rand")]
23pub use rand::{RandHexStr, RandStr};
25
26wrapper! {
27 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
28 pub StringExt(String)
30}
31
32#[macro_export]
33macro_rules! str_concat {
72 ($($x:expr),*) => {
73 {
74 use $crate::string::StringExtT;
75
76 ($($x,)*).to_string_ext()
77 }
78 };
79 (str = $string_initial:expr; $($x:expr),*) => {
80 {
81 use $crate::string::PushAnyT;
82
83 $(
84 $string_initial.push_any($x);
85 )*
86 }
87 };
88 (cap = $cap:expr; $($x:expr),*) => {
89 {
90 use $crate::string::PushAnyT;
91
92 let mut string_final = String::with_capacity($cap);
93
94 $(
95 string_final.push_any($x);
96 )*
97
98 string_final
99 }
100 };
101 (sep = $sep:expr; $($x:expr),*) => {
102 {
103 use $crate::string::StringExtT;
104
105 ($($x,)*).to_string_ext_with_separator($sep)
106 }
107 };
108}
109
110#[deprecated(since = "0.8.0", note = "Use `str_concat!` instead")]
111pub use crate::str_concat as str_concat_v2;
112use crate::wrapper;
113
114macro_rules! remove_separator_tailing {
118 ($string:expr, $separator:expr) => {
119 let current_len = $string.len();
120 if let Some(target_len) = current_len.checked_sub($separator.len()) {
121 $string.truncate(target_len);
122 }
123 };
124}
125
126pub trait PushAnyT {
128 fn push_any<V>(&mut self, value: V)
130 where
131 V: StringT;
132
133 fn push_any_with_separator<V>(&mut self, value: V, sep: &str)
135 where
136 V: StringT;
137}
138
139impl PushAnyT for String {
140 #[inline]
141 fn push_any<V>(&mut self, value: V)
142 where
143 V: StringT,
144 {
145 #[allow(unsafe_code)]
147 value.encode_to_buf(unsafe { self.as_mut_vec() });
148 }
149
150 #[inline]
151 fn push_any_with_separator<V>(&mut self, value: V, sep: &str)
152 where
153 V: StringT,
154 {
155 #[allow(unsafe_code)]
157 let inner = unsafe { self.as_mut_vec() };
158
159 value.encode_to_buf_with_separator(inner, sep);
160
161 remove_separator_tailing!(inner, sep);
163 }
164}
165
166impl PushAnyT for Vec<u8> {
167 #[inline]
168 fn push_any<V>(&mut self, value: V)
169 where
170 V: StringT,
171 {
172 value.encode_to_buf(self);
173 }
174
175 #[inline]
176 fn push_any_with_separator<V>(&mut self, value: V, sep: &str)
177 where
178 V: StringT,
179 {
180 value.encode_to_buf_with_separator(self, sep);
181 remove_separator_tailing!(self, sep);
182 }
183}
184
185impl PushAnyT for bytes::BytesMut {
186 #[inline]
187 fn push_any<V>(&mut self, value: V)
188 where
189 V: StringT,
190 {
191 value.encode_to_bytes_buf(self);
192 }
193
194 #[inline]
195 fn push_any_with_separator<V>(&mut self, value: V, sep: &str)
196 where
197 V: StringT,
198 {
199 value.encode_to_bytes_buf_with_separator(self, sep);
200 remove_separator_tailing!(self, sep);
201 }
202}
203
204pub trait StringT {
206 fn encode_to_buf(self, string: &mut Vec<u8>);
208
209 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str);
214
215 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut);
217
218 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str);
223}
224
225#[allow(clippy::len_without_is_empty)]
226pub trait StringExtT: StringT + Sized {
229 #[inline]
230 fn with_prefix<P: StringExtT + Copy>(self, prefix: P) -> impl StringExtT {
232 general::tuple::SeplessTuple {
233 inner: (prefix, self),
234 }
235 }
236
237 #[inline]
238 fn with_suffix<S: StringExtT + Copy>(self, suffix: S) -> impl StringExtT {
240 general::tuple::SeplessTuple {
241 inner: (self, suffix),
242 }
243 }
244
245 #[inline]
246 fn to_string_ext(self) -> String {
248 let mut string_buf = String::with_capacity(64);
249
250 string_buf.push_any(self);
251
252 string_buf
253 }
254
255 #[inline]
256 fn to_string_ext_with_separator(self, separator: &str) -> String {
258 let mut string_buf = String::with_capacity(64);
259
260 string_buf.push_any_with_separator(self, separator);
261
262 string_buf
263 }
264
265 #[inline]
266 #[cfg(feature = "feat-string-ext-http")]
267 fn to_http_header_value(self) -> Result<http::HeaderValue, http::header::InvalidHeaderValue> {
269 let mut buf = self.to_string_ext().into_bytes();
270
271 buf.truncate(buf.len());
273
274 http::HeaderValue::from_maybe_shared(bytes::Bytes::from(buf))
275 }
276}
277
278#[doc(hidden)]
281#[macro_export]
282macro_rules! impl_for_shared_ref {
283 (COPIED: $($ty:ty)*) => {
284 $(
285 impl StringT for &$ty {
286 #[inline]
287 fn encode_to_buf(self, string: &mut Vec<u8>) {
288 (*self).encode_to_buf(string);
289 }
290
291 #[inline]
292 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
293 (*self).encode_to_buf_with_separator(string, separator);
294 }
295
296 #[inline]
297 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
298 (*self).encode_to_bytes_buf(string);
299 }
300
301 #[inline]
302 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
303 (*self).encode_to_bytes_buf_with_separator(string, separator);
304 }
305 }
306
307 impl StringExtT for &$ty {}
308
309 impl StringT for &mut $ty {
310 #[inline]
311 fn encode_to_buf(self, string: &mut Vec<u8>) {
312 (*self).encode_to_buf(string);
313 }
314
315 #[inline]
316 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
317 (*self).encode_to_buf_with_separator(string, separator);
318 }
319
320 #[inline]
321 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
322 (*self).encode_to_bytes_buf(string);
323 }
324
325 #[inline]
326 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
327 (*self).encode_to_bytes_buf_with_separator(string, separator);
328 }
329 }
330
331 impl StringExtT for &mut $ty {}
332
333 impl StringT for &&$ty {
334 #[inline]
335 fn encode_to_buf(self, string: &mut Vec<u8>) {
336 (**self).encode_to_buf(string);
337 }
338
339 #[inline]
340 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
341 (**self).encode_to_buf_with_separator(string, separator);
342 }
343
344 #[inline]
345 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
346 (**self).encode_to_bytes_buf(string);
347 }
348
349 #[inline]
350 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
351 (**self).encode_to_bytes_buf_with_separator(string, separator);
352 }
353 }
354
355 impl StringExtT for &&$ty {}
356
357 impl StringT for &mut &$ty {
358 #[inline]
359 fn encode_to_buf(self, string: &mut Vec<u8>) {
360 (**self).encode_to_buf(string);
361 }
362
363 #[inline]
364 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
365 (**self).encode_to_buf_with_separator(string, separator);
366 }
367
368 #[inline]
369 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
370 (**self).encode_to_bytes_buf(string);
371 }
372
373 #[inline]
374 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
375 (**self).encode_to_bytes_buf_with_separator(string, separator);
376 }
377 }
378
379 impl StringExtT for &mut &$ty {}
380
381 impl StringT for &&mut $ty {
382 #[inline]
383 fn encode_to_buf(self, string: &mut Vec<u8>) {
384 (**self).encode_to_buf(string);
385 }
386
387 #[inline]
388 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
389 (**self).encode_to_buf_with_separator(string, separator);
390 }
391
392 #[inline]
393 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
394 (**self).encode_to_bytes_buf(string);
395 }
396
397 #[inline]
398 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
399 (**self).encode_to_bytes_buf_with_separator(string, separator);
400 }
401 }
402
403 impl StringExtT for &&mut $ty {}
404
405 impl StringT for &&&$ty {
406 #[inline]
407 fn encode_to_buf(self, string: &mut Vec<u8>) {
408 (***self).encode_to_buf(string);
409 }
410
411 #[inline]
412 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
413 (***self).encode_to_buf_with_separator(string, separator);
414 }
415
416 #[inline]
417 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
418 (***self).encode_to_bytes_buf(string);
419 }
420
421 #[inline]
422 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
423 (***self).encode_to_bytes_buf_with_separator(string, separator);
424 }
425 }
426
427 impl StringExtT for &&&$ty {}
428 )*
429 };
430 (REF: $($ge:ident => $ty:ty)*) => {
431 $(
432 impl<$ge> StringT for $ty
433 where
434 for <'a> &'a $ge: StringT,
435 {
436 #[inline]
437 fn encode_to_buf(self, string: &mut Vec<u8>) {
438 (&*self).encode_to_buf(string);
439 }
440
441 #[inline]
442 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
443 (&*self).encode_to_buf_with_separator(string, separator);
444 }
445
446 #[inline]
447 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
448 (&*self).encode_to_bytes_buf(string);
449 }
450
451 #[inline]
452 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
453 (&*self).encode_to_bytes_buf_with_separator(string, separator);
454 }
455 }
456
457 impl<$ge> StringExtT for $ty
458 where
459 for <'a> &'a $ge: StringExtT,
460 {}
461 )*
462 };
463}
464
465#[macro_export]
466#[doc(hidden)]
467macro_rules! impl_for_wrapper {
469 (STRING_T: $($tt:tt)*) => {
470 $($tt)* {
471 #[inline]
472 fn encode_to_buf(self, string: &mut Vec<u8>) {
473 (&*self.inner).encode_to_buf(string);
474 }
475
476 #[inline]
477 fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
478 (&*self.inner).encode_to_buf_with_separator(string, separator);
479 }
480
481 #[inline]
482 fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
483 (&*self.inner).encode_to_bytes_buf(string);
484 }
485
486 #[inline]
487 fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
488 (&*self.inner).encode_to_bytes_buf_with_separator(string, separator);
489 }
490 }
491 };
492 (STRING_EXT_T: $($tt:tt)*) => {
493 $($tt)* {}
494 }
495}
496
497#[cfg(test)]
498#[allow(unused)]
499mod tests {
500 use super::*;
501
502 type BoxedString = Box<dyn StringT>;
504
505 #[test]
506 fn test_prefix_or_suffix() {
507 let exp1 = "world".with_prefix("hello");
508 assert_eq!(exp1.to_string_ext(), "helloworld");
509
510 let exp2 = str_concat!(sep = " "; ("hello", "world"));
511 assert_eq!(exp2, "hello world");
512
513 let exp3 = str_concat!(
519 sep = " ";
520 None::<()>.with_prefix("prefix-")
521 );
522
523 assert_eq!(exp3, "");
524
525 let exp4 = str_concat!(
526 sep = " ";
527 "data",
528 None::<()>.with_prefix("prefix-"),
529 None::<()>,
530 None::<()>
531 );
532
533 assert_eq!(exp4, "data");
534
535 let exp5 = str_concat!(
536 sep = " ";
537 (None::<()>.with_prefix("prefix-"), None::<()>.with_prefix("prefix-")),
538 ("hello", "world"),
539 "hello".with_suffix(Some("-suffix")),
540 None::<()>.with_prefix("prefix-"),
541 "3hello".with_suffix(None::<()>).with_prefix(None::<()>),
542 None::<()>.with_prefix("prefix-").with_suffix("-suffix")
543 );
544
545 assert_eq!(exp5, "hello world hello-suffix 3hello");
546
547 let exp6 = str_concat!(
548 sep = "&";
549 [1, 2, 3].with_prefix("post_ids[]=")
550 );
551
552 assert_eq!(exp6, "post_ids[]=1&post_ids[]=2&post_ids[]=3")
553 }
554
555 #[test]
556 fn test_separator() {
557 let mut string = String::new();
558
559 string.push_any_with_separator(["b", "c"], ",");
560 assert_eq!(string, "b,c");
561 string.clear();
562
563 string.push_any(("a", "b", "c"));
564 assert_eq!(string, "abc");
565
566 string.push_any_with_separator(("a", "b", "c"), ",");
567 assert_eq!(string, "abca,b,c");
568
569 string.push_any_with_separator(("a", "b", "c", ("b", "c")), ",");
570 assert_eq!(string, "abca,b,ca,b,c,b,c");
571
572 string.push_any_with_separator(
573 (
574 &&&"a",
575 vec!["b"],
576 ("c"),
577 ["b", "c"],
578 "d".with_prefix("prefix-"),
579 "e".with_suffix("-suffix"),
580 "f".with_prefix("2prefix-").with_suffix("-suffix2"),
581 1u8,
582 ),
583 ",",
584 );
585 assert_eq!(
586 string,
587 "abca,b,ca,b,c,b,ca,b,c,b,c,prefix-d,e-suffix,2prefix-f-suffix2,1"
588 );
589 }
590}