Skip to content

Conversation

@mxsm
Copy link
Owner

@mxsm mxsm commented Jan 6, 2026

Fix #92

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced pattern matching API: starts_with, ends_with, and contains now accept both character and string patterns for greater flexibility.
    • Improved split functionality with flexible pattern support and double-ended iteration capabilities.
    • New public traits and types to support expanded pattern matching functionality.
  • Documentation

    • Added comprehensive example programs demonstrating pattern matching, splitting, and API usage.
  • Tests

    • Extended test coverage with comprehensive test suites for pattern matching, splitting edge cases, and iterator behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

…ing patterns in starts_with, ends_with, contains, and split methods
Copilot AI review requested due to automatic review settings January 6, 2026 02:46
@coderabbitai
Copy link

coderabbitai bot commented Jan 6, 2026

📝 Walkthrough

Walkthrough

Enhances CheetahString's pattern matching API to accept both character and string patterns. Introduces new traits (StrPattern, SplitPattern) and supporting types to enable polymorphic pattern matching. Updates method signatures for starts_with, ends_with, contains, and split. Includes comprehensive examples and test coverage.

Changes

Cohort / File(s) Summary
Core API Enhancement
src/cheetah_string.rs, src/lib.rs
Introduces StrPattern trait with char, &str, and &String implementations; adds StrPatternImpl enum for pattern representation. Introduces SplitPattern trait with SplitStr struct and SplitWrapper enum for unified split iteration over char or string patterns. Modifies method signatures for starts_with, ends_with, contains (now accept StrPattern); split (now accepts SplitPattern and returns SplitWrapper). Changes chars() return type to concrete std::str::Chars<'_>. Implements DoubleEndedIterator and empty pattern handling. Expands public re-exports in lib.rs.
Example Programs
examples/pattern_matching.rs, examples/comprehensive_patterns.rs, examples/test_empty_pattern.rs, examples/test_split_behavior.rs
Adds four example programs demonstrating pattern matching capabilities: char/&str usage for starts_with/ends_with/contains, split behavior across pattern types, comparison with standard library behavior, and empty pattern handling.
New Test Suites
tests/patterns.rs, tests/comprehensive_tests.rs, tests/split_edge_cases.rs
Introduces three new test modules: basic pattern method validations; comprehensive test coverage including Unicode, multi-delimiter splitting, reverse iteration, DoubleEndedIterator behavior, and long-string handling; edge cases for split functionality (empty strings, leading/trailing separators, consecutive delimiters, multi-char patterns).
Test Data Updates
tests/api_extensions.rs, tests/basic.rs, tests/sso.rs
Updates Unicode test literals to use escaped code points (e.g., \u{00E9}, \u{00E7}) instead of multi-byte characters; adjusts expected lengths and string values accordingly. Functional behavior unchanged.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Whisker-twitch — what joy to see,
Patterns dancing wild and free!
Char, then string, a polymorphic dream,
Split and match with swifter gleam!
The CheetahString now runs so fleet,
Our hoppy code's complete! 🌿

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately describes the main objective: enhancing CheetahString with support for character and string patterns in four key methods.
Linked Issues check ✅ Passed The pull request successfully implements all requirements from issue #92: adds character and string pattern support to starts_with, ends_with, contains, and split methods via new StrPattern and SplitPattern traits.
Out of Scope Changes check ✅ Passed All changes are directly related to issue #92 objectives. API changes, test updates, and example files all support the core enhancement of pattern-based method signatures.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mxsm mxsm merged commit 7b15105 into main Jan 6, 2026
10 of 11 checks passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Fix all issues with AI Agents 🤖
In @examples/test_empty_pattern.rs:
- Around line 19-25: The example shows a behavioral mismatch: CheetahString's
split("") returns the whole string while Rust's standard String.split("") yields
splits between every character; update the implementation or example to resolve
this. Either (A) change CheetahString::split (and any related split internals)
to mirror std::string::String::split on an empty pattern so split("") produces
empty-string-separated characters, (B) if intentional, add a clear API doc
comment on CheetahString::split and surface this limitation in examples and
tests (update examples/test_empty_pattern.rs and tests/split_edge_cases.rs to
document the difference), or (C) make CheetahString::split panic on empty
pattern to avoid silent divergence; pick one approach and apply it consistently
across the split implementation, its public docs, the example
(examples/test_empty_pattern.rs) and the test test_empty_pattern in
tests/split_edge_cases.rs.

In @src/cheetah_string.rs:
- Around line 1410-1420: The current DoubleEndedIterator implementation for
SplitWrapper panics in SplitWrapper::Str branch; change it to return None
instead of panic to avoid unexpected runtime aborts: in impl<'a>
DoubleEndedIterator for SplitWrapper<'a> ensure fn next_back(&mut self) matches
SplitWrapper::Str => return None (keeping SplitWrapper::Char delegating to
iter.next_back()), and update the doc comment on the split function (or
SplitWrapper) to note that reverse iteration over string-pattern splits yields
None rather than supporting reverse traversal.

In @tests/split_edge_cases.rs:
- Around line 63-67: The test file incorrectly defines a main() function that
calls test_split_edge_cases() and test_empty_pattern(); remove the entire main()
function so tests are run by Rust's #[test] harness instead ( locate and delete
the main() definition that invokes test_split_edge_cases and test_empty_pattern
); rely on the existing #[test] functions and run them via cargo test.
- Around line 54-61: The test_empty_pattern currently only prints the split
result; update the test (function test_empty_pattern) to assert that splitting
with an empty pattern yields the standard library behavior: collect the iterator
into a Vec<&str> and assert_eq! it to ["", "h", "e", "l", "l", "o", ""] (or
assert length == 7 and individual elements) for
CheetahString::from("hello").split(""); ensure you reference the existing parts
variable and replace the println! with the appropriate assertions.
🧹 Nitpick comments (4)
src/cheetah_string.rs (2)

1326-1340: SplitStr is missing Clone implementation, limiting usability.

EmptyPatternState derives Clone, but SplitStr does not implement Clone. This means users cannot clone a SplitWrapper::Str iterator, which differs from the standard library's str::Split behavior.

Consider implementing Clone for SplitStr
+impl<'a> Clone for SplitStr<'a> {
+    fn clone(&self) -> Self {
+        SplitStr {
+            string: self.string,
+            pattern: self.pattern,
+            finished: self.finished,
+            empty_pattern_state: self.empty_pattern_state.clone(),
+        }
+    }
+}

1299-1303: Lifetime mismatch in SplitPattern implementation for char.

The impl declares SplitPattern<'_> (anonymous lifetime) but the trait is generic over 'a. This works because char doesn't borrow anything, but it's slightly inconsistent with the &'a str implementation.

Consider using explicit lifetime for consistency
-impl SplitPattern<'_> for char {
-    fn split_str(self, s: &str) -> SplitWrapper<'_> {
+impl<'a> SplitPattern<'a> for char {
+    fn split_str(self, s: &'a str) -> SplitWrapper<'a> {
         SplitWrapper::Char(s.split(self))
     }
 }
tests/patterns.rs (1)

34-39: Consider adding a test for split with string pattern to match test_split_char.

While test_split_str tests basic string splitting, adding a #[should_panic] test for reverse iteration on string patterns would document the expected behavior.

Suggested test addition
#[test]
#[should_panic(expected = "split with string pattern does not support reverse iteration")]
fn test_split_str_reverse_panics() {
    let s = CheetahString::from("a::b::c");
    let _: Vec<&str> = s.split("::").rev().collect();
}
tests/sso.rs (1)

73-83: The SSO Unicode boundary test no longer tests near the 23-byte boundary.

The original test likely used Chinese characters (3 bytes each) to get close to the 23-byte SSO limit. The new test uses 14 bytes (7 × 2-byte chars), which is well under the limit. Consider adding a test that approaches the boundary with multi-byte characters.

Consider adding a boundary test with multi-byte Unicode
#[test]
fn test_sso_unicode_at_boundary() {
    // 11 2-byte chars = 22 bytes (just under 23-byte SSO limit)
    let s = CheetahString::from("\u{00E9}\u{00E7}\u{00F1}\u{00FC}\u{00E0}\u{00F6}\u{00E4}\u{00EB}\u{00EF}\u{00F2}\u{00F9}");
    assert_eq!(s.len(), 22);
    
    // 12 2-byte chars = 24 bytes (just over SSO limit, uses Arc)
    let s2 = CheetahString::from("\u{00E9}\u{00E7}\u{00F1}\u{00FC}\u{00E0}\u{00F6}\u{00E4}\u{00EB}\u{00EF}\u{00F2}\u{00F9}\u{00FA}");
    assert_eq!(s2.len(), 24);
}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2f00aeb and b4578dc.

📒 Files selected for processing (12)
  • examples/comprehensive_patterns.rs
  • examples/pattern_matching.rs
  • examples/test_empty_pattern.rs
  • examples/test_split_behavior.rs
  • src/cheetah_string.rs
  • src/lib.rs
  • tests/api_extensions.rs
  • tests/basic.rs
  • tests/comprehensive_tests.rs
  • tests/patterns.rs
  • tests/split_edge_cases.rs
  • tests/sso.rs
🧰 Additional context used
🧬 Code graph analysis (7)
tests/sso.rs (1)
src/cheetah_string.rs (14)
  • from (30-32)
  • from (37-39)
  • from (44-46)
  • from (58-62)
  • from (82-86)
  • from (91-96)
  • from (101-106)
  • from (120-122)
  • from (163-165)
  • from (170-172)
  • from (177-208)
  • from (243-245)
  • len (428-438)
  • as_str (390-412)
examples/pattern_matching.rs (1)
src/cheetah_string.rs (2)
  • starts_with (468-473)
  • ends_with (504-509)
tests/split_edge_cases.rs (2)
examples/test_empty_pattern.rs (1)
  • main (3-37)
examples/test_split_behavior.rs (1)
  • main (3-72)
examples/test_empty_pattern.rs (4)
src/serde.rs (1)
  • cheetah_string (27-89)
examples/test_split_behavior.rs (1)
  • main (3-72)
tests/split_edge_cases.rs (1)
  • main (63-67)
src/cheetah_string.rs (13)
  • len (428-438)
  • from (30-32)
  • from (37-39)
  • from (44-46)
  • from (58-62)
  • from (82-86)
  • from (91-96)
  • from (101-106)
  • from (120-122)
  • from (163-165)
  • from (170-172)
  • from (177-208)
  • from (243-245)
examples/comprehensive_patterns.rs (4)
examples/pattern_matching.rs (1)
  • main (3-27)
examples/test_empty_pattern.rs (1)
  • main (3-37)
examples/test_split_behavior.rs (1)
  • main (3-72)
src/cheetah_string.rs (15)
  • from (30-32)
  • from (37-39)
  • from (44-46)
  • from (58-62)
  • from (82-86)
  • from (91-96)
  • from (101-106)
  • from (120-122)
  • from (163-165)
  • from (170-172)
  • from (177-208)
  • from (243-245)
  • starts_with (468-473)
  • ends_with (504-509)
  • contains (540-545)
tests/basic.rs (1)
src/cheetah_string.rs (13)
  • from (30-32)
  • from (37-39)
  • from (44-46)
  • from (58-62)
  • from (82-86)
  • from (91-96)
  • from (101-106)
  • from (120-122)
  • from (163-165)
  • from (170-172)
  • from (177-208)
  • from (243-245)
  • len (428-438)
tests/api_extensions.rs (1)
src/cheetah_string.rs (4)
  • contains (540-545)
  • starts_with (468-473)
  • ends_with (504-509)
  • find (575-577)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Agent
🔇 Additional comments (28)
src/cheetah_string.rs (7)

468-472: LGTM!

The starts_with implementation correctly dispatches to the underlying str::starts_with for both char and string patterns. The use of the sealed trait pattern ensures extensibility is controlled.


504-508: LGTM!

The ends_with implementation mirrors starts_with correctly, delegating to the appropriate str::ends_with variant based on pattern type.


540-544: LGTM!

The contains implementation correctly handles both char and string patterns via delegation.


690-691: Good improvement: returning concrete std::str::Chars<'_> enables DoubleEndedIterator.

This change allows users to call .rev() on the chars iterator, which wasn't possible with the previous impl Iterator return type.


1250-1261: LGTM: Sealed trait pattern correctly restricts trait implementations.

The sealed trait pattern prevents external crates from implementing StrPattern and SplitPattern, which is appropriate for maintaining API stability.


1350-1388: Empty pattern split behavior is correct.

The implementation correctly matches Rust's str::split("") behavior: it yields an empty string first, then each UTF-8 character via CharIndices, then an empty string last. Example: "abc".split("") == ["", "a", "b", "c", ""]. The use of CharIndices properly handles multi-byte UTF-8 characters.


653-658: split return type changed from impl Iterator to SplitWrapper<'a> — a breaking change that should be documented.

The method signature was changed from pub fn split<'a>(&'a self, pat: &'a str) -> impl Iterator<Item = &'a str> to pub fn split<'a, P>(&'a self, pat: P) -> SplitWrapper<'a> where P: SplitPattern<'a>. While SplitWrapper<'a> implements Iterator, code with explicit type annotations (e.g., storing the result in a typed variable or struct field) will break and require updates. Most common usage patterns (for loops, .collect(), method chaining) are unaffected since the iterator contract is preserved. This change is intentional and adds useful functionality (supporting both char and string patterns), but it warrants release notes or documentation for users upgrading from 0.1.x.

tests/patterns.rs (1)

1-81: LGTM: Comprehensive test coverage for new pattern API.

The tests cover the key functionality: char patterns with starts_with, ends_with, contains, and split. The double-ended iterator tests for chars() and split(char) are valuable.

tests/basic.rs (2)

48-50: LGTM: Unicode test updated to use Latin extended characters.

The test correctly validates that a 2-byte UTF-8 character (é, \u{00E9}) is handled properly.


236-238: LGTM: Unicode string test updated consistently.

Four 2-byte Latin extended characters (8 bytes total) correctly replace the previous 4-character test.

examples/pattern_matching.rs (1)

1-27: LGTM: Clean example demonstrating the new pattern API.

This example effectively demonstrates starts_with and ends_with with both char and &str patterns, which directly addresses the issue #92 requirements.

tests/api_extensions.rs (4)

117-119: LGTM: Unicode chars test updated to use escape sequences.

Consistent with the changes in other test files.


282-286: LGTM: Unicode query tests correctly validate pattern matching with extended Latin characters.

The test verifies contains, starts_with, ends_with, and find work correctly with Unicode.


291-296: LGTM: Unicode transformation test validates case conversion.

Correctly tests that é (\u{00E9}) uppercases to É (\u{00C9}).


301-303: LGTM: Unicode split test validates splitting with Unicode content.

Tests that the split functionality works correctly with Unicode characters.

tests/sso.rs (1)

66-71: LGTM: Unicode short string test updated.

Test validates that short Unicode strings (2 chars × 2 bytes = 4 bytes) work correctly with SSO.

src/lib.rs (1)

27-27: SplitWrapper must remain public since split() returns it directly.

However, SplitPattern and StrPattern should be marked #[doc(hidden)] if the crate doesn't intend for users to write generic code with explicit trait bounds. Currently, these traits are public and re-exported, but their methods are already marked #[doc(hidden)], suggesting they're not intended for direct use. Since the test suite and typical usage involve passing concrete types (char, &str) directly to these methods rather than writing generic bounds, hiding the traits would clarify that they're implementation details.

SplitStr should be marked #[doc(hidden)] as it's only instantiated internally as a variant of SplitWrapper, which already hides its variants.

examples/comprehensive_patterns.rs (1)

1-48: LGTM! Comprehensive demonstration of pattern-based API.

This example effectively demonstrates all the key features of the enhanced pattern-based API including starts_with, ends_with, contains, and split with both character and string patterns. The output messages are clear and the examples cover practical use cases.

examples/test_split_behavior.rs (1)

1-72: LGTM! Effective validation example.

This example provides valuable validation by comparing CheetahString split behavior with the standard library across comprehensive edge cases including empty strings, leading/trailing separators, and consecutive separators. The clear [OK]/[FAIL] indicators make it easy to verify correctness.

tests/split_edge_cases.rs (1)

3-52: LGTM! Comprehensive edge case coverage.

The test cases thoroughly cover important edge cases for split functionality including empty strings, leading/trailing separators, consecutive separators, and multi-character string patterns. All assertions are clear and correct.

tests/comprehensive_tests.rs (8)

3-13: LGTM! Unicode split tests are clear and correct.

The tests properly validate splitting with both character and string patterns, with clear assertions.


15-26: LGTM! Iterator exhaustion behavior is properly tested.

Good practice to verify that multiple calls to next() after exhaustion continue returning None.


28-41: LGTM! Double-ended iterator behavior is well-tested.

The tests validate both rev() and interleaved next()/next_back() calls, which are important for DoubleEndedIterator correctness.


43-48: LGTM! Documented panic behavior for string pattern reverse.

The test correctly documents that string pattern splits do not support reverse iteration with a clear panic message.


50-65: LGTM! Pattern trait tests cover both char and string patterns.

Comprehensive validation of starts_with, ends_with, contains, and split with both pattern types.


67-79: LGTM! Character-level double-ended iteration is properly validated.

Tests both forward and backward traversal of characters.


81-90: LGTM! Long string handling beyond inline capacity is tested.

This validates that split works correctly for strings that exceed the small string optimization threshold.


92-106: LGTM! Special character patterns are well-covered.

Tests tab, newline, and backslash separators, which are common special cases.

Comment on lines +19 to +25
if std_result == cheetah_result {
println!("\n[OK] Behavior matches");
} else {
println!("\n[FAIL] Behavior mismatch!");
println!("Note: Standard library splits empty pattern between each character");
println!("Current implementation returns the whole string for simplicity");
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Address the empty pattern behavior mismatch with standard library.

This example documents a significant behavioral inconsistency: the standard library's split("") splits between each character, but CheetahString returns the whole string "for simplicity". This breaks compatibility with Rust's standard library semantics and will confuse users who expect standard behavior.

The example itself prints "[FAIL] Behavior mismatch!" which indicates this is a known issue. Users migrating from String to CheetahString will encounter unexpected behavior.

Consider either:

  1. Implementing standard library-compatible empty pattern behavior
  2. Documenting this limitation prominently in the API documentation if there's a technical reason for the difference
  3. Panicking on empty patterns (like some other string operations do) to prevent silent behavioral differences

Based on the related code in tests/split_edge_cases.rs which has a test called test_empty_pattern that only prints without assertions, it appears this behavioral difference may still be under development.

🤖 Prompt for AI Agents
In @examples/test_empty_pattern.rs around lines 19-25, The example shows a
behavioral mismatch: CheetahString's split("") returns the whole string while
Rust's standard String.split("") yields splits between every character; update
the implementation or example to resolve this. Either (A) change
CheetahString::split (and any related split internals) to mirror
std::string::String::split on an empty pattern so split("") produces
empty-string-separated characters, (B) if intentional, add a clear API doc
comment on CheetahString::split and surface this limitation in examples and
tests (update examples/test_empty_pattern.rs and tests/split_edge_cases.rs to
document the difference), or (C) make CheetahString::split panic on empty
pattern to avoid silent divergence; pick one approach and apply it consistently
across the split implementation, its public docs, the example
(examples/test_empty_pattern.rs) and the test test_empty_pattern in
tests/split_edge_cases.rs.

Comment on lines +1410 to +1420
impl<'a> DoubleEndedIterator for SplitWrapper<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
match self {
SplitWrapper::Char(iter) => iter.next_back(),
SplitWrapper::Str(_) => {
// String pattern split doesn't support reverse iteration
// This is consistent with std::str::Split<&str>
panic!("split with string pattern does not support reverse iteration")
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Panic in DoubleEndedIterator::next_back is a significant UX concern.

While the comment notes this is "consistent with std::str::Split<&str>", panicking at runtime when users call .rev() on a string-pattern split is unexpected. Consider returning None or documenting this prominently.

Suggested alternatives

Option 1: Document the panic in the split method's doc comment:

 /// Splits the string by the given pattern.
 ///
+/// # Panics
+///
+/// When using a string pattern (not a char), calling `.rev()` or `.next_back()` 
+/// on the returned iterator will panic. Use a char pattern if reverse iteration is needed.
+///
 /// # Examples

Option 2: Return None instead of panicking (changes behavior but is safer):

 fn next_back(&mut self) -> Option<Self::Item> {
     match self {
         SplitWrapper::Char(iter) => iter.next_back(),
-        SplitWrapper::Str(_) => {
-            // String pattern split doesn't support reverse iteration
-            // This is consistent with std::str::Split<&str>
-            panic!("split with string pattern does not support reverse iteration")
-        }
+        SplitWrapper::Str(_) => None,
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
impl<'a> DoubleEndedIterator for SplitWrapper<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
match self {
SplitWrapper::Char(iter) => iter.next_back(),
SplitWrapper::Str(_) => {
// String pattern split doesn't support reverse iteration
// This is consistent with std::str::Split<&str>
panic!("split with string pattern does not support reverse iteration")
}
}
}
impl<'a> DoubleEndedIterator for SplitWrapper<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
match self {
SplitWrapper::Char(iter) => iter.next_back(),
SplitWrapper::Str(_) => None,
}
}
🤖 Prompt for AI Agents
In @src/cheetah_string.rs around lines 1410-1420, The current
DoubleEndedIterator implementation for SplitWrapper panics in SplitWrapper::Str
branch; change it to return None instead of panic to avoid unexpected runtime
aborts: in impl<'a> DoubleEndedIterator for SplitWrapper<'a> ensure fn
next_back(&mut self) matches SplitWrapper::Str => return None (keeping
SplitWrapper::Char delegating to iter.next_back()), and update the doc comment
on the split function (or SplitWrapper) to note that reverse iteration over
string-pattern splits yields None rather than supporting reverse traversal.

Comment on lines +54 to +61
#[test]
fn test_empty_pattern() {
// Empty pattern should split between each character
let s = CheetahString::from("hello");
let parts: Vec<&str> = s.split("").collect();
println!("Empty pattern: {:?}", parts);
// Standard library behavior: empty pattern splits between each character
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add assertions to validate empty pattern behavior.

The test prints diagnostic output but lacks assertions to verify the expected behavior. Without assertions, this test cannot fail and provides no validation.

🔎 Suggested fix

Based on the comment and the related example in examples/test_empty_pattern.rs, add assertions that match the standard library behavior:

 #[test]
 fn test_empty_pattern() {
     // Empty pattern should split between each character
     let s = CheetahString::from("hello");
     let parts: Vec<&str> = s.split("").collect();
     println!("Empty pattern: {:?}", parts);
-    // Standard library behavior: empty pattern splits between each character
+    
+    // Standard library behavior for comparison
+    let std_parts: Vec<&str> = "hello".split("").collect();
+    assert_eq!(parts, std_parts, "Empty pattern should match standard library behavior");
 }
🤖 Prompt for AI Agents
In @tests/split_edge_cases.rs around lines 54-61, The test_empty_pattern
currently only prints the split result; update the test (function
test_empty_pattern) to assert that splitting with an empty pattern yields the
standard library behavior: collect the iterator into a Vec<&str> and assert_eq!
it to ["", "h", "e", "l", "l", "o", ""] (or assert length == 7 and individual
elements) for CheetahString::from("hello").split(""); ensure you reference the
existing parts variable and replace the println! with the appropriate
assertions.

Comment on lines +63 to +67
fn main() {
test_split_edge_cases();
test_empty_pattern();
println!("\nAll tests passed!");
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Remove the main() function from test file.

Test files should not contain a main() function. When running cargo test, the test framework executes functions marked with #[test], not main(). This creates confusion about test execution and is non-idiomatic.

🔎 Suggested fix

Remove the main() function entirely:

-fn main() {
-    test_split_edge_cases();
-    test_empty_pattern();
-    println!("\nAll tests passed!");
-}

If you need to run these tests manually for debugging, use cargo test instead.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn main() {
test_split_edge_cases();
test_empty_pattern();
println!("\nAll tests passed!");
}
🤖 Prompt for AI Agents
In @tests/split_edge_cases.rs around lines 63-67, The test file incorrectly
defines a main() function that calls test_split_edge_cases() and
test_empty_pattern(); remove the entire main() function so tests are run by
Rust's #[test] harness instead ( locate and delete the main() definition that
invokes test_split_edge_cases and test_empty_pattern ); rely on the existing
#[test] functions and run them via cargo test.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances CheetahString with flexible pattern matching support, allowing methods like starts_with, ends_with, contains, and split to accept both character (char) and string (&str) patterns through sealed trait implementations. This provides a more ergonomic API consistent with Rust's standard library string methods.

Key Changes:

  • Introduced StrPattern and SplitPattern traits with sealed trait pattern to support both char and &str patterns
  • Implemented custom SplitStr iterator with support for empty pattern splitting (matching standard library behavior)
  • Enhanced return type of chars() from impl Iterator to concrete std::str::Chars<'_> to expose DoubleEndedIterator functionality

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/lib.rs Exports new public types including pattern traits and split iterator wrappers
src/cheetah_string.rs Core implementation of pattern traits, updated method signatures for starts_with, ends_with, contains, and split, plus custom SplitStr iterator with empty pattern support
tests/patterns.rs New comprehensive tests for character and string pattern matching across all affected methods
tests/split_edge_cases.rs Edge case tests for split functionality including empty strings, consecutive separators, and empty patterns
tests/comprehensive_tests.rs Integration tests covering unicode support, iterator behavior, reverse iteration, and special characters
tests/sso.rs Updated Unicode tests from Chinese to Latin extended characters (2-byte UTF-8)
tests/basic.rs Updated Unicode character tests to use Latin extended characters
tests/api_extensions.rs Updated Unicode-related tests with Latin extended characters
examples/test_split_behavior.rs Example demonstrating split behavior parity with standard library for both char and string patterns
examples/test_empty_pattern.rs Example showcasing empty pattern splitting behavior
examples/pattern_matching.rs Example demonstrating pattern matching with chars and strings
examples/comprehensive_patterns.rs Comprehensive examples covering all pattern-related functionality
Comments suppressed due to low confidence (3)

src/cheetah_string.rs:489

  • With the new generic starts_with method that accepts both char and &str patterns, the starts_with_char method has become redundant. Users can now call starts_with('+') directly instead of starts_with_char('+'). Consider adding a deprecation warning to guide users to the more flexible API, or document when and why someone would prefer starts_with_char over starts_with.
    /// Returns `true` if the string starts with the given character.
    ///
    /// # Examples
    ///
    /// ```
    /// use cheetah_string::CheetahString;
    ///
    /// let s = CheetahString::from("hello world");
    /// assert!(s.starts_with_char('h'));
    /// assert!(!s.starts_with_char('w'));
    /// ```
    #[inline]
    pub fn starts_with_char(&self, pat: char) -> bool {
        self.as_str().starts_with(pat)
    }

src/cheetah_string.rs:525

  • With the new generic ends_with method that accepts both char and &str patterns, the ends_with_char method has become redundant. Users can now call ends_with('d') directly instead of ends_with_char('d'). Consider adding a deprecation warning to guide users to the more flexible API, or document when and why someone would prefer ends_with_char over ends_with.
    /// Returns `true` if the string ends with the given character.
    ///
    /// # Examples
    ///
    /// ```
    /// use cheetah_string::CheetahString;
    ///
    /// let s = CheetahString::from("hello world");
    /// assert!(s.ends_with_char('d'));
    /// assert!(!s.ends_with_char('h'));
    /// ```
    #[inline]
    pub fn ends_with_char(&self, pat: char) -> bool {
        self.as_str().ends_with(pat)
    }

src/cheetah_string.rs:561

  • With the new generic contains method that accepts both char and &str patterns, the contains_char method has become redundant. Users can now call contains('o') directly instead of contains_char('o'). Consider adding a deprecation warning to guide users to the more flexible API, or document when and why someone would prefer contains_char over contains.
    /// Returns `true` if the string contains the given character.
    ///
    /// # Examples
    ///
    /// ```
    /// use cheetah_string::CheetahString;
    ///
    /// let s = CheetahString::from("hello world");
    /// assert!(s.contains_char('o'));
    /// assert!(!s.contains_char('x'));
    /// ```
    #[inline]
    pub fn contains_char(&self, pat: char) -> bool {
        self.as_str().contains(pat)
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

impl SplitSealed for &str {}
}

/// A pattern that can be used with `starts_with` and `ends_with` methods.
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation comment for StrPattern says "A pattern that can be used with starts_with and ends_with methods" but this trait is also used by the contains method. The documentation should be updated to include contains in the list of methods that accept this pattern type.

Suggested change
/// A pattern that can be used with `starts_with` and `ends_with` methods.
/// A pattern that can be used with `starts_with`, `ends_with`, and `contains` methods.

Copilot uses AI. Check for mistakes.
Comment on lines +1410 to +1421
impl<'a> DoubleEndedIterator for SplitWrapper<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
match self {
SplitWrapper::Char(iter) => iter.next_back(),
SplitWrapper::Str(_) => {
// String pattern split doesn't support reverse iteration
// This is consistent with std::str::Split<&str>
panic!("split with string pattern does not support reverse iteration")
}
}
}
}
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The panic message and behavior for reverse iteration on string patterns is intentional and documented, but it may be surprising to users who don't realize the limitation. The panic occurs at runtime rather than being caught at compile time. Consider whether this behavior matches the standard library. Note that in Rust's standard library, str::split with a string pattern returns Split<'_, &str> which does NOT implement DoubleEndedIterator, so calling .rev() would be a compile-time error, not a runtime panic. The current implementation makes this a runtime error by implementing DoubleEndedIterator unconditionally on SplitWrapper. This is a behavioral difference from the standard library that could lead to unexpected panics in user code.

Copilot uses AI. Check for mistakes.
mod serde;

pub use cheetah_string::CheetahString;
pub use cheetah_string::{CheetahString, SplitPattern, SplitStr, SplitWrapper, StrPattern};
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The types SplitPattern, SplitStr, and SplitWrapper are implementation details and should not be part of the public API. They are not used directly by any tests or examples, and exposing them increases the API surface area unnecessarily, making it harder to change the implementation in the future. Consider removing them from the public exports and keeping only CheetahString and StrPattern (if StrPattern needs to be public for generic code to work).

Suggested change
pub use cheetah_string::{CheetahString, SplitPattern, SplitStr, SplitWrapper, StrPattern};
pub use cheetah_string::{CheetahString, StrPattern};

Copilot uses AI. Check for mistakes.
Comment on lines +54 to +61
#[test]
fn test_empty_pattern() {
// Empty pattern should split between each character
let s = CheetahString::from("hello");
let parts: Vec<&str> = s.split("").collect();
println!("Empty pattern: {:?}", parts);
// Standard library behavior: empty pattern splits between each character
}
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test test_empty_pattern doesn't have any assertions to verify the behavior. The comment mentions "Standard library behavior: empty pattern splits between each character" but there's no actual assertion to check if the implementation matches this behavior. Add an assertion to verify the expected behavior, for example: assert_eq!(parts, vec!["", "h", "e", "l", "l", "o", ""]); to match standard library behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +62 to +67

fn main() {
test_split_edge_cases();
test_empty_pattern();
println!("\nAll tests passed!");
}
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main function in a test file is unusual. Test files should only contain test functions marked with #[test] or test modules marked with #[cfg(test)]. The main function will not be executed by cargo test and serves no purpose here. Consider removing it.

Suggested change
fn main() {
test_split_edge_cases();
test_empty_pattern();
println!("\nAll tests passed!");
}

Copilot uses AI. Check for mistakes.
@mxsm mxsm deleted the feat-92 branch January 6, 2026 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhance CheetahString with support for character and string patterns in starts_with, ends_with, contains, and split methods

3 participants