Skip to content

Commit e846b95

Browse files
committed
feat(str): Add contains(...).count
This sets a required count for number of matches. It could be of value to make this accept a predicate. In that case, for API convinience, we should probably have a `ToCountPredicate` that is attached to both `Predicate<usize>` and `usize`. This would allow things like `contains("Two").count(predicate::in_iter([3, 4])`. Fixes #25
1 parent c984772 commit e846b95

File tree

1 file changed

+74
-26
lines changed

1 file changed

+74
-26
lines changed

src/str/basics.rs

+74-26
Original file line numberDiff line numberDiff line change
@@ -35,29 +35,17 @@ pub fn is_empty() -> IsEmptyPredicate {
3535
IsEmptyPredicate {}
3636
}
3737

38-
#[derive(Copy, Clone, Debug)]
39-
enum PatternOp {
40-
StartsWith,
41-
EndsWith,
42-
Contains,
43-
}
44-
45-
/// Predicate that checks for patterns.
38+
/// Predicate checks start of str
4639
///
47-
/// This is created by `predicates::str::{starts_with, ends_with, contains}`.
40+
/// This is created by `predicates::str::starts_with`.
4841
#[derive(Clone, Debug)]
49-
pub struct PatternPredicate {
42+
pub struct StartsWithPredicate {
5043
pattern: String,
51-
op: PatternOp,
5244
}
5345

54-
impl Predicate<str> for PatternPredicate {
46+
impl Predicate<str> for StartsWithPredicate {
5547
fn eval(&self, variable: &str) -> bool {
56-
match self.op {
57-
PatternOp::StartsWith => variable.starts_with(&self.pattern),
58-
PatternOp::EndsWith => variable.ends_with(&self.pattern),
59-
PatternOp::Contains => variable.contains(&self.pattern),
60-
}
48+
variable.starts_with(&self.pattern)
6149
}
6250
}
6351

@@ -72,13 +60,26 @@ impl Predicate<str> for PatternPredicate {
7260
/// assert_eq!(true, predicate_fn.eval("Hello World"));
7361
/// assert_eq!(false, predicate_fn.eval("Goodbye World"));
7462
/// ```
75-
pub fn starts_with<P>(pattern: P) -> PatternPredicate
63+
pub fn starts_with<P>(pattern: P) -> StartsWithPredicate
7664
where
7765
P: Into<String>,
7866
{
79-
PatternPredicate {
67+
StartsWithPredicate {
8068
pattern: pattern.into(),
81-
op: PatternOp::StartsWith,
69+
}
70+
}
71+
72+
/// Predicate checks end of str
73+
///
74+
/// This is created by `predicates::str::ends_with`.
75+
#[derive(Clone, Debug)]
76+
pub struct EndsWithPredicate {
77+
pattern: String,
78+
}
79+
80+
impl Predicate<str> for EndsWithPredicate {
81+
fn eval(&self, variable: &str) -> bool {
82+
variable.ends_with(&self.pattern)
8283
}
8384
}
8485

@@ -93,13 +94,61 @@ where
9394
/// assert_eq!(true, predicate_fn.eval("Hello World"));
9495
/// assert_eq!(false, predicate_fn.eval("Hello Moon"));
9596
/// ```
96-
pub fn ends_with<P>(pattern: P) -> PatternPredicate
97+
pub fn ends_with<P>(pattern: P) -> EndsWithPredicate
9798
where
9899
P: Into<String>,
99100
{
100-
PatternPredicate {
101+
EndsWithPredicate {
101102
pattern: pattern.into(),
102-
op: PatternOp::EndsWith,
103+
}
104+
}
105+
106+
/// Predicate that checks for patterns.
107+
///
108+
/// This is created by `predicates::str:contains`.
109+
#[derive(Clone, Debug)]
110+
pub struct ContainsPredicate {
111+
pattern: String,
112+
}
113+
114+
impl ContainsPredicate {
115+
/// Require a specific count of matches.
116+
///
117+
/// # Examples
118+
///
119+
/// ```
120+
/// use predicates::prelude::*;
121+
///
122+
/// let predicate_fn = predicate::str::contains("Two").count(2);
123+
/// assert_eq!(true, predicate_fn.eval("One Two Three Two One"));
124+
/// assert_eq!(false, predicate_fn.eval("One Two Three"));
125+
/// ```
126+
pub fn count(self, count: usize) -> MatchesPredicate {
127+
MatchesPredicate {
128+
pattern: self.pattern,
129+
count,
130+
}
131+
}
132+
}
133+
134+
impl Predicate<str> for ContainsPredicate {
135+
fn eval(&self, variable: &str) -> bool {
136+
variable.contains(&self.pattern)
137+
}
138+
}
139+
140+
/// Predicate that checks for repeated patterns.
141+
///
142+
/// This is created by `predicates::str::contains(...).count`.
143+
#[derive(Clone, Debug)]
144+
pub struct MatchesPredicate {
145+
pattern: String,
146+
count: usize,
147+
}
148+
149+
impl Predicate<str> for MatchesPredicate {
150+
fn eval(&self, variable: &str) -> bool {
151+
variable.matches(&self.pattern).count() == self.count
103152
}
104153
}
105154

@@ -114,12 +163,11 @@ where
114163
/// assert_eq!(true, predicate_fn.eval("One Two Three"));
115164
/// assert_eq!(false, predicate_fn.eval("Four Five Six"));
116165
/// ```
117-
pub fn contains<P>(pattern: P) -> PatternPredicate
166+
pub fn contains<P>(pattern: P) -> ContainsPredicate
118167
where
119168
P: Into<String>,
120169
{
121-
PatternPredicate {
170+
ContainsPredicate {
122171
pattern: pattern.into(),
123-
op: PatternOp::Contains,
124172
}
125173
}

0 commit comments

Comments
 (0)