strchunk/
impls.rs

1#![cfg_attr(feature = "specialization", allow(unused_macros))]
2
3use crate::{StrChunk, StrChunkMut};
4
5use range_split::TakeRange;
6
7use std::ops::{RangeFrom, RangeFull, RangeTo, RangeToInclusive};
8
9// A generic impl implemented through the intrinsic take_range/remove_range
10// would be enough for the purposes of this crate, but it would commit to
11// Bytes as the internal representation and limit any third-party trait
12// implementations to those implemented via Bytes. Also, the concrete impls
13// make better documentation.
14macro_rules! impl_take_range {
15    (<$Range:ty> for $T:path) => {
16        impl TakeRange<$Range> for $T {
17            type Output = $T;
18
19            fn take_range(&mut self, range: $Range) -> Self::Output {
20                Self::take_range(self, range)
21            }
22
23            fn remove_range(&mut self, range: $Range) {
24                Self::remove_range(self, range)
25            }
26        }
27    };
28}
29
30impl_take_range!(<RangeFull> for StrChunk);
31impl_take_range!(<RangeFrom<usize>> for StrChunk);
32impl_take_range!(<RangeTo<usize>> for StrChunk);
33impl_take_range!(<RangeToInclusive<usize>> for StrChunk);
34impl_take_range!(<RangeFull> for StrChunkMut);
35impl_take_range!(<RangeFrom<usize>> for StrChunkMut);
36impl_take_range!(<RangeTo<usize>> for StrChunkMut);
37impl_take_range!(<RangeToInclusive<usize>> for StrChunkMut);
38
39#[cfg(feature = "specialization")]
40mod generic {
41    use crate::{StrChunk, StrChunkMut};
42    use std::borrow::Borrow;
43    use std::cmp::Ordering;
44
45    impl<Rhs> PartialEq<Rhs> for StrChunk
46    where
47        Rhs: ?Sized + Borrow<str>,
48    {
49        default fn eq(&self, other: &Rhs) -> bool {
50            self.as_str() == other.borrow()
51        }
52    }
53
54    impl<Rhs> PartialEq<Rhs> for StrChunkMut
55    where
56        Rhs: ?Sized + Borrow<str>,
57    {
58        default fn eq(&self, other: &Rhs) -> bool {
59            self.as_str() == other.borrow()
60        }
61    }
62
63    impl<Rhs> PartialOrd<Rhs> for StrChunk
64    where
65        Rhs: ?Sized + Borrow<str>,
66    {
67        default fn partial_cmp(&self, other: &Rhs) -> Option<Ordering> {
68            PartialOrd::partial_cmp(self.as_str(), other.borrow())
69        }
70    }
71
72    impl<Rhs> PartialOrd<Rhs> for StrChunkMut
73    where
74        Rhs: ?Sized + Borrow<str>,
75    {
76        default fn partial_cmp(&self, other: &Rhs) -> Option<Ordering> {
77            PartialOrd::partial_cmp(self.as_str(), other.borrow())
78        }
79    }
80}
81
82macro_rules! for_all_foreign_str_types {
83    {
84        $impl_macro:ident! for $T:ty
85    } => {
86        $impl_macro! { impl <str> for $T }
87        $impl_macro! { impl<'a> <&'a str> for $T }
88        $impl_macro! { impl <String> for $T }
89        $impl_macro! { impl<'a> <::std::borrow::Cow<'a, str>> for $T }
90    };
91}
92
93macro_rules! for_all_str_types {
94    {
95        $impl_macro:ident! for $T:ty
96    } => {
97        $impl_macro! { impl <crate::StrChunk> for $T }
98        $impl_macro! { impl <crate::StrChunkMut> for $T }
99        for_all_foreign_str_types! { $impl_macro! for $T }
100    };
101}
102
103#[cfg(not(feature = "specialization"))]
104mod tedious {
105    use crate::{StrChunk, StrChunkMut};
106    use std::borrow::Borrow;
107    use std::cmp::Ordering;
108
109    macro_rules! impl_partial_eq {
110        {
111            impl<$a:lifetime> <$Rhs:ty> for $T:ty
112        } => {
113            impl<$a> PartialEq<$Rhs> for $T {
114                #[inline]
115                fn eq(&self, other: &$Rhs) -> bool {
116                    Borrow::<str>::borrow(self) == Borrow::<str>::borrow(other)
117                }
118            }
119        };
120        {
121            impl <$Rhs:ty> for $T:ty
122        } => {
123            impl PartialEq<$Rhs> for $T {
124                #[inline]
125                fn eq(&self, other: &$Rhs) -> bool {
126                    Borrow::<str>::borrow(self) == Borrow::<str>::borrow(other)
127                }
128            }
129        };
130    }
131
132    macro_rules! impl_partial_ord {
133        {
134            impl<$a:lifetime> <$Rhs:ty> for $T:ty
135        } => {
136            impl<$a> PartialOrd<$Rhs> for $T {
137                #[inline]
138                fn partial_cmp(&self, other: &$Rhs) -> Option<Ordering> {
139                    PartialOrd::partial_cmp(
140                        Borrow::<str>::borrow(self),
141                        Borrow::<str>::borrow(other),
142                    )
143                }
144            }
145        };
146        {
147            impl <$Rhs:ty> for $T:ty
148        } => {
149            impl PartialOrd<$Rhs> for $T {
150                #[inline]
151                fn partial_cmp(&self, other: &$Rhs) -> Option<Ordering> {
152                    PartialOrd::partial_cmp(
153                        Borrow::<str>::borrow(self),
154                        Borrow::<str>::borrow(other),
155                    )
156                }
157            }
158        };
159    }
160
161    for_all_str_types! { impl_partial_eq! for StrChunk }
162    for_all_str_types! { impl_partial_eq! for StrChunkMut }
163    for_all_str_types! { impl_partial_ord! for StrChunk }
164    for_all_str_types! { impl_partial_ord! for StrChunkMut }
165}
166
167mod foreign {
168    use crate::{StrChunk, StrChunkMut};
169    use std::borrow::Borrow;
170    use std::cmp::Ordering;
171
172    macro_rules! impl_partial_eq_rhs {
173        {
174            impl<$a:lifetime> <$Lhs:ty> for $T:ty
175        } => {
176            impl<$a> PartialEq<$T> for $Lhs {
177                #[inline]
178                fn eq(&self, other: &$T) -> bool {
179                    other == self
180                }
181            }
182        };
183        {
184            impl <$Lhs:ty> for $T:ty
185        } => {
186            impl PartialEq<$T> for $Lhs {
187                #[inline]
188                fn eq(&self, other: &$T) -> bool {
189                    other == self
190                }
191            }
192        };
193    }
194
195    macro_rules! impl_partial_ord_rhs {
196        {
197            impl<$a:lifetime> <$Lhs:ty> for $T:ty
198        } => {
199            impl<$a> PartialOrd<$T> for $Lhs {
200                #[inline]
201                fn partial_cmp(&self, other: &$T) -> Option<Ordering> {
202                    PartialOrd::partial_cmp(
203                        Borrow::<str>::borrow(self),
204                        Borrow::<str>::borrow(other),
205                    )
206                }
207            }
208        };
209        {
210            impl <$Lhs:ty> for $T:ty
211        } => {
212            impl PartialOrd<$T> for $Lhs {
213                #[inline]
214                fn partial_cmp(&self, other: &$T) -> Option<Ordering> {
215                    PartialOrd::partial_cmp(
216                        Borrow::<str>::borrow(self),
217                        Borrow::<str>::borrow(other),
218                    )
219                }
220            }
221        };
222    }
223
224    for_all_foreign_str_types! { impl_partial_eq_rhs! for StrChunk }
225    for_all_foreign_str_types! { impl_partial_eq_rhs! for StrChunkMut }
226    for_all_foreign_str_types! { impl_partial_ord_rhs! for StrChunk }
227    for_all_foreign_str_types! { impl_partial_ord_rhs! for StrChunkMut }
228}
229
230#[cfg(test)]
231mod tests {
232    #![allow(clippy::cmp_owned)]
233
234    use crate::{StrChunk, StrChunkMut};
235
236    mod take_range {
237        macro_rules! test_take_range_effects_with {
238            ($func:expr) => {
239                #[test]
240                fn full() {
241                    let mut buf = "Hello".into();
242                    $func(&mut buf, .., "Hello", "");
243                }
244
245                #[test]
246                fn from_start() {
247                    let mut buf = "Hello".into();
248                    $func(&mut buf, 0.., "Hello", "");
249                }
250
251                #[test]
252                fn from_end() {
253                    let mut buf = "Hello".into();
254                    $func(&mut buf, 5.., "", "Hello");
255                }
256
257                #[test]
258                fn from_mid() {
259                    let mut buf = "Привет".into();
260                    $func(&mut buf, 6.., "вет", "При");
261                }
262
263                #[test]
264                fn to_start() {
265                    let mut buf = "Hello".into();
266                    $func(&mut buf, ..0, "", "Hello");
267                }
268
269                #[test]
270                fn to_end() {
271                    let mut buf = "Hello".into();
272                    $func(&mut buf, ..5, "Hello", "");
273                }
274
275                #[test]
276                fn to_mid() {
277                    let mut buf = "Привет".into();
278                    $func(&mut buf, ..6, "При", "вет");
279                }
280
281                #[test]
282                fn to_inclusive_end() {
283                    let mut buf = "Hello".into();
284                    $func(&mut buf, ..=4, "Hello", "");
285                }
286
287                #[test]
288                fn to_inclusive_mid() {
289                    let mut buf = "Привет".into();
290                    $func(&mut buf, ..=5, "При", "вет");
291                }
292            };
293        }
294
295        macro_rules! test_take_range_panics_with {
296            ($func:expr) => {
297                #[test]
298                #[should_panic]
299                fn panics_on_oob_start() {
300                    let mut buf = "Hello".into();
301                    $func(&mut buf, 6..);
302                }
303
304                #[test]
305                #[should_panic]
306                fn panics_on_oob_end() {
307                    let mut buf = "Hello".into();
308                    $func(&mut buf, ..6);
309                }
310
311                #[test]
312                #[should_panic]
313                fn panics_on_oob_inclusive_end() {
314                    let mut buf = "Hello".into();
315                    $func(&mut buf, ..=5);
316                }
317
318                #[test]
319                #[should_panic]
320                fn panics_on_split_utf8_start() {
321                    let mut buf = "Привет".into();
322                    $func(&mut buf, 3..);
323                }
324
325                #[test]
326                #[should_panic]
327                fn panics_on_split_utf8_end() {
328                    let mut buf = "Привет".into();
329                    $func(&mut buf, ..3);
330                }
331
332                #[test]
333                #[should_panic]
334                fn panics_on_split_utf8_inclusive_end() {
335                    let mut buf = "Привет".into();
336                    $func(&mut buf, ..=2);
337                }
338            };
339        }
340
341        macro_rules! test_take_range_for {
342            ($T:ty) => {
343                mod take_range {
344                    use super::*;
345                    use range_split::TakeRange;
346
347                    test_take_range_effects_with!(
348                        |buf: &mut $T,
349                         range,
350                         expected_output,
351                         expected_remainder| {
352                            let method_dbg =
353                                format!("take_range({:?})", &range);
354                            let output = TakeRange::take_range(buf, range);
355                            assert_eq!(
356                                output,
357                                expected_output,
358                                "expected output of `{}` for `{}`",
359                                method_dbg,
360                                stringify!($T)
361                            );
362                            assert_eq!(
363                                buf,
364                                expected_remainder,
365                                "expected buffer content after `{}` for `{}`",
366                                method_dbg,
367                                stringify!($T)
368                            );
369                        }
370                    );
371
372                    test_take_range_panics_with!(|buf: &mut $T, range| {
373                        TakeRange::take_range(buf, range)
374                    });
375                }
376
377                mod remove_range {
378                    use super::*;
379                    use range_split::TakeRange;
380
381                    test_take_range_effects_with!(
382                        |buf: &mut $T, range, _, expected_remainder| {
383                            let method_dbg =
384                                format!("remove_range({:?})", &range);
385                            TakeRange::remove_range(buf, range);
386                            assert_eq!(
387                                buf,
388                                expected_remainder,
389                                "expected buffer content after `{}` for `{}`",
390                                method_dbg,
391                                stringify!($T)
392                            );
393                        }
394                    );
395
396                    test_take_range_panics_with!(|buf: &mut $T, range| {
397                        TakeRange::remove_range(buf, range);
398                    });
399                }
400            };
401        }
402
403        mod chunk {
404            use crate::StrChunk;
405            test_take_range_for!(StrChunk);
406        }
407        mod chunk_mut {
408            use crate::StrChunkMut;
409            test_take_range_for!(StrChunkMut);
410        }
411    }
412
413    const TEST_STR: &str = "Hello";
414    const TEST_STR_LESSER: &str = "Hell";
415
416    macro_rules! test_all_str_types {
417        ($macro:ident!, $v:expr) => {
418            $macro! { str, $v, *TEST_STR }
419            $macro! { str_ref, $v, TEST_STR }
420            $macro! { string, $v, String::from(TEST_STR) }
421            $macro! { chunk, $v, crate::StrChunk::from(TEST_STR) }
422            $macro! { chunk_mut, $v, crate::StrChunkMut::from(TEST_STR) }
423            $macro! { cow_borrowed, $v, ::std::borrow::Cow::from(TEST_STR) }
424            $macro! { cow_owned, $v, ::std::borrow::Cow::from(String::from(TEST_STR)) }
425        };
426    }
427
428    mod eq {
429        use super::*;
430
431        macro_rules! test_equal {
432            ($name:ident, $arg1:expr, $arg2:expr) => {
433                #[test]
434                fn $name() {
435                    assert_eq!($arg1, $arg2);
436                    assert_eq!($arg2, $arg1);
437                }
438            };
439        }
440
441        mod chunk {
442            use super::*;
443            test_all_str_types! { test_equal!, StrChunk::from_static(TEST_STR) }
444        }
445
446        mod chunk_mut {
447            use super::*;
448            test_all_str_types! { test_equal!, StrChunkMut::from(TEST_STR) }
449        }
450    }
451
452    mod ord {
453        use super::*;
454
455        mod equal {
456            use super::*;
457
458            macro_rules! test_equal {
459                ($name:ident, $arg1:expr, $arg2:expr) => {
460                    #[test]
461                    fn $name() {
462                        assert!($arg1 <= $arg2);
463                        assert!(!($arg1 > $arg2));
464                        assert!($arg2 >= $arg1);
465                        assert!(!($arg2 < $arg1));
466                    }
467                };
468            }
469
470            mod chunk {
471                use super::*;
472                test_all_str_types! { test_equal!, StrChunk::from_static(TEST_STR) }
473            }
474
475            mod chunk_mut {
476                use super::*;
477                test_all_str_types! { test_equal!, StrChunkMut::from(TEST_STR) }
478            }
479        }
480
481        mod unequal {
482            use super::*;
483
484            macro_rules! test_lesser {
485                ($name:ident, $arg1:expr, $arg2:expr) => {
486                    #[test]
487                    fn $name() {
488                        assert!($arg1 < $arg2);
489                        assert!(!($arg1 >= $arg2));
490                        assert!($arg2 > $arg1);
491                        assert!(!($arg2 <= $arg1));
492                    }
493                };
494            }
495
496            mod chunk {
497                use super::*;
498                test_all_str_types! { test_lesser!, StrChunk::from_static(TEST_STR_LESSER) }
499            }
500
501            mod chunk_mut {
502                use super::*;
503                test_all_str_types! { test_lesser!, StrChunkMut::from(TEST_STR_LESSER) }
504            }
505        }
506    }
507
508    mod hash {
509        use super::*;
510
511        macro_rules! test_hash {
512            ($v:expr) => {
513                #[test]
514                #[allow(clippy::mutable_key_type)]
515                fn same_as_str() {
516                    let mut set = ::std::collections::HashSet::new();
517                    set.insert($v);
518                    assert!(set.contains(TEST_STR));
519                }
520            };
521        }
522
523        mod chunk {
524            use super::*;
525            test_hash!(StrChunk::from(TEST_STR));
526        }
527
528        mod chunk_mut {
529            use super::*;
530            test_hash!(StrChunkMut::from(TEST_STR));
531        }
532    }
533}