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
9macro_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}