1use crate::guard_empty;
20
21pub trait StringContains {
38 #[must_use]
55 fn contains_any_char(&self, search_chars: &[char]) -> bool;
56
57 #[must_use]
73 fn contains_any_in(&self, search_chars: &str) -> bool;
74
75 #[must_use]
91 fn contains_any(&self, search: &[&str]) -> bool;
92
93 #[must_use]
110 fn contains_ignore_case(&self, search: &str) -> bool;
111
112 #[must_use]
128 fn contains_any_ignore_case(&self, search: &[&str]) -> bool;
129
130 #[must_use]
147 fn contains_none_char(&self, invalid_chars: &[char]) -> bool;
148
149 #[must_use]
166 fn contains_none(&self, invalid_chars: &str) -> bool;
167
168 #[must_use]
186 fn contains_only_char(&self, valid_chars: &[char]) -> bool;
187
188 #[must_use]
205 fn contains_only(&self, valid_chars: &str) -> bool;
206
207 #[must_use]
228 fn contains_whitespace(&self) -> bool;
229}
230
231impl StringContains for str {
232 fn contains_any_char(&self, search_chars: &[char]) -> bool {
233 guard_empty!(self, search_chars, false);
234 self.chars().any(|c| search_chars.contains(&c))
235 }
236
237 fn contains_any_in(&self, search_chars: &str) -> bool {
238 guard_empty!(self, search_chars, false);
239 self.chars().any(|c| search_chars.chars().any(|sc| sc == c))
240 }
241
242 fn contains_any(&self, search: &[&str]) -> bool {
243 guard_empty!(self, search, false);
244 search.iter().any(|s| !s.is_empty() && self.contains(s))
245 }
246
247 fn contains_ignore_case(&self, search: &str) -> bool {
248 guard_empty!(self, search, false);
249 self.to_lowercase().contains(&search.to_lowercase())
250 }
251
252 fn contains_any_ignore_case(&self, search: &[&str]) -> bool {
253 guard_empty!(self, search, false);
254 let lower = self.to_lowercase();
255 search
256 .iter()
257 .any(|s| !s.is_empty() && lower.contains(&s.to_lowercase()))
258 }
259
260 fn contains_none_char(&self, invalid_chars: &[char]) -> bool {
261 guard_empty!(self, invalid_chars, true);
262 !self.chars().any(|c| invalid_chars.contains(&c))
263 }
264
265 fn contains_none(&self, invalid_chars: &str) -> bool {
266 guard_empty!(self, invalid_chars, true);
267 !self
268 .chars()
269 .any(|c| invalid_chars.chars().any(|ic| ic == c))
270 }
271
272 fn contains_only_char(&self, valid_chars: &[char]) -> bool {
273 guard_empty!(self, valid_chars, false);
274 self.chars().all(|c| valid_chars.contains(&c))
275 }
276
277 fn contains_only(&self, valid_chars: &str) -> bool {
278 guard_empty!(self, valid_chars, false);
279 self.chars().all(|c| valid_chars.chars().any(|vc| vc == c))
280 }
281
282 fn contains_whitespace(&self) -> bool {
283 self.chars().any(|c| c.is_whitespace())
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 mod contains_any_char {
292 use super::*;
293
294 #[test]
295 fn empty_string() {
296 assert!(!"".contains_any_char(&['a', 'b']));
297 }
298
299 #[test]
300 fn empty_search() {
301 assert!(!"abc".contains_any_char(&[]));
302 }
303
304 #[test]
305 fn both_empty() {
306 assert!(!"".contains_any_char(&[]));
307 }
308
309 #[test]
310 fn found_first_char() {
311 assert!("zzabyycdxx".contains_any_char(&['z', 'a']));
312 }
313
314 #[test]
315 fn found_middle_char() {
316 assert!("zzabyycdxx".contains_any_char(&['b', 'y']));
317 }
318
319 #[test]
320 fn found_multiple() {
321 assert!("zzabyycdxx".contains_any_char(&['z', 'y']));
322 }
323
324 #[test]
325 fn not_found() {
326 assert!(!"aba".contains_any_char(&['z']));
327 }
328
329 #[test]
330 fn unicode() {
331 assert!("日本語".contains_any_char(&['本', 'x']));
332 assert!(!"日本語".contains_any_char(&['x', 'y']));
333 }
334 }
335
336 mod contains_any_in {
337 use super::*;
338
339 #[test]
340 fn empty_string() {
341 assert!(!"".contains_any_in("ab"));
342 }
343
344 #[test]
345 fn empty_search() {
346 assert!(!"abc".contains_any_in(""));
347 }
348
349 #[test]
350 fn both_empty() {
351 assert!(!"".contains_any_in(""));
352 }
353
354 #[test]
355 fn found() {
356 assert!("zzabyycdxx".contains_any_in("za"));
357 }
358
359 #[test]
360 fn found_middle() {
361 assert!("zzabyycdxx".contains_any_in("by"));
362 }
363
364 #[test]
365 fn not_found() {
366 assert!(!"aba".contains_any_in("z"));
367 }
368
369 #[test]
370 fn unicode() {
371 assert!("日本語".contains_any_in("本x"));
372 assert!(!"日本語".contains_any_in("xy"));
373 }
374 }
375
376 mod contains_any {
377 use super::*;
378
379 #[test]
380 fn empty_string() {
381 assert!(!"".contains_any(&["ab", "cd"]));
382 }
383
384 #[test]
385 fn empty_search() {
386 assert!(!"abc".contains_any(&[]));
387 }
388
389 #[test]
390 fn both_empty() {
391 assert!(!"".contains_any(&[]));
392 }
393
394 #[test]
395 fn found_first() {
396 assert!("abcd".contains_any(&["ab", "cd"]));
397 }
398
399 #[test]
400 fn found_second() {
401 assert!("abcd".contains_any(&["xy", "cd"]));
402 }
403
404 #[test]
405 fn not_found() {
406 assert!(!"abcd".contains_any(&["xy", "zz"]));
407 }
408
409 #[test]
410 fn empty_search_string() {
411 assert!(!"abc".contains_any(&[""]));
412 assert!("abc".contains_any(&["", "b"]));
413 }
414
415 #[test]
416 fn unicode() {
417 assert!("日本語".contains_any(&["本", "x"]));
418 assert!(!"日本語".contains_any(&["x", "y"]));
419 }
420
421 #[test]
422 fn with_empty_string_in_array() {
423 assert!("abcd".contains_any(&["ab", ""]));
424 }
425
426 #[test]
427 fn case_sensitive() {
428 assert!(!"hello, goodbye".contains_any(&["Hello", "Goodbye"]));
429 }
430 }
431
432 mod contains_ignore_case {
433 use super::*;
434
435 #[test]
436 fn empty_string() {
437 assert!(!"".contains_ignore_case("a"));
438 }
439
440 #[test]
441 fn empty_search() {
442 assert!(!"abc".contains_ignore_case(""));
443 }
444
445 #[test]
446 fn both_empty() {
447 assert!(!"".contains_ignore_case(""));
448 }
449
450 #[test]
451 fn found_lower_in_lower() {
452 assert!("abc".contains_ignore_case("a"));
453 }
454
455 #[test]
456 fn found_upper_in_lower() {
457 assert!("abc".contains_ignore_case("A"));
458 }
459
460 #[test]
461 fn found_lower_in_upper() {
462 assert!("ABC".contains_ignore_case("a"));
463 }
464
465 #[test]
466 fn found_mixed() {
467 assert!("abc".contains_ignore_case("ABC"));
468 assert!("ABC".contains_ignore_case("abc"));
469 assert!("AbCdEf".contains_ignore_case("cDe"));
470 }
471
472 #[test]
473 fn not_found() {
474 assert!(!"abc".contains_ignore_case("Z"));
475 }
476
477 #[test]
478 fn unicode() {
479 assert!("Héllo".contains_ignore_case("héllo"));
480 assert!("HÉLLO".contains_ignore_case("héllo"));
481 }
482 }
483
484 mod contains_any_ignore_case {
485 use super::*;
486
487 #[test]
488 fn empty_string() {
489 assert!(!"".contains_any_ignore_case(&["ab"]));
490 }
491
492 #[test]
493 fn empty_search() {
494 assert!(!"abc".contains_any_ignore_case(&[]));
495 }
496
497 #[test]
498 fn found() {
499 assert!("abcd".contains_any_ignore_case(&["AB", "cd"]));
500 }
501
502 #[test]
503 fn found_second() {
504 assert!("abc".contains_any_ignore_case(&["D", "ABC"]));
505 }
506
507 #[test]
508 fn not_found() {
509 assert!(!"abc".contains_any_ignore_case(&["D", "XYZ"]));
510 }
511
512 #[test]
513 fn empty_search_string() {
514 assert!(!"abc".contains_any_ignore_case(&[""]));
515 }
516
517 #[test]
518 fn hello_goodbye_both_lowercase() {
519 assert!("hello, goodbye".contains_any_ignore_case(&["hello", "goodbye"]));
520 }
521
522 #[test]
523 fn hello_goodbye_mixed_case() {
524 assert!("hello, goodbye".contains_any_ignore_case(&["hello", "Goodbye"]));
525 }
526
527 #[test]
528 fn hello_goodbye_both_capitalized() {
529 assert!("hello, goodbye".contains_any_ignore_case(&["Hello", "Goodbye"]));
530 }
531 }
532
533 mod contains_none_char {
534 use super::*;
535
536 #[test]
537 fn empty_string() {
538 assert!("".contains_none_char(&['a', 'b']));
539 }
540
541 #[test]
542 fn empty_invalid() {
543 assert!("abc".contains_none_char(&[]));
544 }
545
546 #[test]
547 fn both_empty() {
548 assert!("".contains_none_char(&[]));
549 }
550
551 #[test]
552 fn none_found() {
553 assert!("abab".contains_none_char(&['x', 'y', 'z']));
554 }
555
556 #[test]
557 fn found_first() {
558 assert!(!"abab".contains_none_char(&['a', 'b', 'z']));
559 }
560
561 #[test]
562 fn found_last() {
563 assert!(!"abab".contains_none_char(&['x', 'y', 'z', 'a']));
564 }
565
566 #[test]
567 fn unicode() {
568 assert!("日本語".contains_none_char(&['x', 'y']));
569 assert!(!"日本語".contains_none_char(&['本', 'x']));
570 }
571 }
572
573 mod contains_none {
574 use super::*;
575
576 #[test]
577 fn empty_string() {
578 assert!("".contains_none("ab"));
579 }
580
581 #[test]
582 fn empty_invalid() {
583 assert!("abc".contains_none(""));
584 }
585
586 #[test]
587 fn both_empty() {
588 assert!("".contains_none(""));
589 }
590
591 #[test]
592 fn none_found() {
593 assert!("abab".contains_none("xyz"));
594 }
595
596 #[test]
597 fn found() {
598 assert!(!"abab".contains_none("abz"));
599 }
600
601 #[test]
602 fn found_partial() {
603 assert!(!"abz".contains_none("xyz"));
604 }
605
606 #[test]
607 fn unicode() {
608 assert!("日本語".contains_none("xy"));
609 assert!(!"日本語".contains_none("本x"));
610 }
611
612 #[test]
613 fn single_none() {
614 assert!("a".contains_none("b"));
615 }
616
617 #[test]
618 fn single_contains() {
619 assert!(!"a".contains_none("a"));
620 }
621
622 #[test]
623 fn single_contains_2() {
624 assert!(!"b".contains_none("b"));
625 }
626
627 #[test]
628 fn first_char_contained() {
629 assert!(!"ab".contains_none("a"));
630 }
631
632 #[test]
633 fn second_char_contained() {
634 assert!(!"ab".contains_none("b"));
635 }
636 }
637
638 mod contains_only_char {
639 use super::*;
640
641 #[test]
642 fn empty_string() {
643 assert!(!"".contains_only_char(&['a', 'b']));
644 }
645
646 #[test]
647 fn empty_valid() {
648 assert!(!"abc".contains_only_char(&[]));
649 }
650
651 #[test]
652 fn both_empty() {
653 assert!(!"".contains_only_char(&[]));
654 }
655
656 #[test]
657 fn valid() {
658 assert!("ab".contains_only_char(&['a', 'b', 'c']));
659 }
660
661 #[test]
662 fn valid_repeated() {
663 assert!("abab".contains_only_char(&['a', 'b', 'c']));
664 }
665
666 #[test]
667 fn invalid_digit() {
668 assert!(!"ab1".contains_only_char(&['a', 'b', 'c']));
669 }
670
671 #[test]
672 fn invalid_char() {
673 assert!(!"abz".contains_only_char(&['a', 'b', 'c']));
674 }
675
676 #[test]
677 fn unicode() {
678 assert!("日本".contains_only_char(&['日', '本', '語']));
679 assert!(!"日本x".contains_only_char(&['日', '本', '語']));
680 }
681 }
682
683 mod contains_only {
684 use super::*;
685
686 #[test]
687 fn empty_string() {
688 assert!(!"".contains_only("abc"));
689 }
690
691 #[test]
692 fn empty_valid() {
693 assert!(!"abc".contains_only(""));
694 }
695
696 #[test]
697 fn both_empty() {
698 assert!(!"".contains_only(""));
699 }
700
701 #[test]
702 fn valid() {
703 assert!("ab".contains_only("abc"));
704 }
705
706 #[test]
707 fn valid_repeated() {
708 assert!("abab".contains_only("abc"));
709 }
710
711 #[test]
712 fn invalid_digit() {
713 assert!(!"ab1".contains_only("abc"));
714 }
715
716 #[test]
717 fn invalid_char() {
718 assert!(!"abz".contains_only("abc"));
719 }
720
721 #[test]
722 fn unicode() {
723 assert!("日本".contains_only("日本語"));
724 assert!(!"日本x".contains_only("日本語"));
725 }
726
727 #[test]
728 fn does_contain_any() {
729 assert!(!"a".contains_only("b"));
730 }
731
732 #[test]
733 fn contains_single() {
734 assert!("a".contains_only("a"));
735 }
736
737 #[test]
738 fn contains_single_and_ignored() {
739 assert!("a".contains_only("ab"));
740 }
741
742 #[test]
743 fn equals() {
744 assert!("ab".contains_only("ab"));
745 }
746 }
747
748 mod contains_whitespace {
749 use super::*;
750
751 #[test]
752 fn empty_string() {
753 assert!(!"".contains_whitespace());
754 }
755
756 #[test]
757 fn only_spaces() {
758 assert!(" ".contains_whitespace());
759 }
760
761 #[test]
762 fn leading_space() {
763 assert!(" ab".contains_whitespace());
764 }
765
766 #[test]
767 fn trailing_space() {
768 assert!("ab ".contains_whitespace());
769 }
770
771 #[test]
772 fn middle_space() {
773 assert!("a b".contains_whitespace());
774 }
775
776 #[test]
777 fn tab() {
778 assert!("a\tb".contains_whitespace());
779 }
780
781 #[test]
782 fn newline() {
783 assert!("a\nb".contains_whitespace());
784 }
785
786 #[test]
787 fn carriage_return() {
788 assert!("a\rb".contains_whitespace());
789 }
790
791 #[test]
792 fn no_whitespace() {
793 assert!(!"abc".contains_whitespace());
794 }
795
796 #[test]
797 fn single_char_no_whitespace() {
798 assert!(!"a".contains_whitespace());
799 }
800
801 #[test]
802 fn trailing_tab() {
803 assert!("a\t".contains_whitespace());
804 }
805
806 #[test]
807 fn only_newline() {
808 assert!("\n".contains_whitespace());
809 }
810
811 #[test]
812 fn unicode_space() {
813 assert!("a\u{00A0}b".contains_whitespace());
815 }
816 }
817
818 mod string_types {
819 use super::*;
820
821 #[test]
822 fn string_type() {
823 assert!(String::from("abc").contains_any_char(&['a']));
824 }
825
826 #[test]
827 fn string_ref() {
828 let s = String::from("abc");
829 assert_eq!(s.contains_whitespace(), false);
830 }
831
832 #[test]
833 fn boxed_str() {
834 let s: Box<str> = "a b".into();
835 assert!(s.contains_whitespace());
836 }
837 }
838}