Skip to content

Commit 5cbdf00

Browse files
committed
starts-with? and ends-with?
1 parent ae6d27c commit 5cbdf00

File tree

4 files changed

+172
-0
lines changed

4 files changed

+172
-0
lines changed

src/clojure_string.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
pub(crate) mod blank_qmark_;
2+
pub(crate) mod ends_with_qmark_;
23
pub(crate) mod join;
34
pub(crate) mod lower_case;
45
pub(crate) mod reverse;
6+
pub(crate) mod starts_with_qmark_;
57
pub(crate) mod upper_case;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use crate::ifn::IFn;
2+
use crate::value::{ToValue, Value};
3+
use std::rc::Rc;
4+
5+
use crate::error_message;
6+
use crate::type_tag::TypeTag;
7+
8+
/// clojure.string/blank? ; returns true if nil, empty or whitespace
9+
#[derive(Debug, Clone)]
10+
pub struct EndsWithFn {}
11+
impl ToValue for EndsWithFn {
12+
fn to_value(&self) -> Value {
13+
Value::IFn(Rc::new(self.clone()))
14+
}
15+
}
16+
impl IFn for EndsWithFn {
17+
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
18+
if args.len() != 2 {
19+
return error_message::wrong_arg_count(2, args.len());
20+
} else {
21+
match (
22+
args.get(0).unwrap().to_value(),
23+
args.get(1).unwrap().to_value(),
24+
) {
25+
(Value::String(s), Value::String(substring)) => {
26+
Value::Boolean(s.ends_with(&substring))
27+
}
28+
_a => error_message::type_mismatch(TypeTag::String, &_a.1.to_value()),
29+
}
30+
}
31+
}
32+
}
33+
34+
#[cfg(test)]
35+
mod tests {
36+
mod reverse_tests {
37+
use crate::clojure_string::ends_with_qmark_::EndsWithFn;
38+
use crate::ifn::IFn;
39+
use crate::value::Value;
40+
use std::rc::Rc;
41+
42+
#[test]
43+
fn hello_ends_with_lo() {
44+
let blank = EndsWithFn {};
45+
let s = "hello";
46+
let substring = "lo";
47+
let args = vec![
48+
Rc::new(Value::String(String::from(s))),
49+
Rc::new(Value::String(String::from(substring))),
50+
];
51+
assert_eq!(Value::Boolean(true), blank.invoke(args));
52+
}
53+
54+
#[test]
55+
fn hello_does_not_end_with_klo() {
56+
let blank = EndsWithFn {};
57+
let s = "hello";
58+
let substring = "klo";
59+
let args = vec![
60+
Rc::new(Value::String(String::from(s))),
61+
Rc::new(Value::String(String::from(substring))),
62+
];
63+
assert_eq!(Value::Boolean(false), blank.invoke(args));
64+
}
65+
66+
#[test]
67+
fn hello_ends_with_empty_string() {
68+
let blank = EndsWithFn {};
69+
let s = "hello";
70+
let substring = "";
71+
let args = vec![
72+
Rc::new(Value::String(String::from(s))),
73+
Rc::new(Value::String(String::from(substring))),
74+
];
75+
assert_eq!(Value::Boolean(true), blank.invoke(args));
76+
}
77+
}
78+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use crate::ifn::IFn;
2+
use crate::value::{ToValue, Value};
3+
use std::rc::Rc;
4+
5+
use crate::error_message;
6+
use crate::type_tag::TypeTag;
7+
8+
/// clojure.string/blank? ; returns true if nil, empty or whitespace
9+
#[derive(Debug, Clone)]
10+
pub struct StartsWithFn {}
11+
impl ToValue for StartsWithFn {
12+
fn to_value(&self) -> Value {
13+
Value::IFn(Rc::new(self.clone()))
14+
}
15+
}
16+
impl IFn for StartsWithFn {
17+
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
18+
if args.len() != 2 {
19+
return error_message::wrong_arg_count(2, args.len());
20+
} else {
21+
match (
22+
args.get(0).unwrap().to_value(),
23+
args.get(1).unwrap().to_value(),
24+
) {
25+
(Value::String(s), Value::String(substring)) => {
26+
Value::Boolean(s.starts_with(&substring))
27+
}
28+
_a => error_message::type_mismatch(TypeTag::String, &_a.1.to_value()),
29+
}
30+
}
31+
}
32+
}
33+
34+
#[cfg(test)]
35+
mod tests {
36+
mod reverse_tests {
37+
use crate::clojure_string::starts_with_qmark_::StartsWithFn;
38+
use crate::ifn::IFn;
39+
use crate::value::Value;
40+
use std::rc::Rc;
41+
42+
#[test]
43+
fn hello_starts_with_hel() {
44+
let blank = StartsWithFn {};
45+
let s = "hello";
46+
let substring = "hel";
47+
let args = vec![
48+
Rc::new(Value::String(String::from(s))),
49+
Rc::new(Value::String(String::from(substring))),
50+
];
51+
assert_eq!(Value::Boolean(true), blank.invoke(args));
52+
}
53+
54+
#[test]
55+
fn hello_does_not_start_with_leh() {
56+
let blank = StartsWithFn {};
57+
let s = "hello";
58+
let substring = "leh";
59+
let args = vec![
60+
Rc::new(Value::String(String::from(s))),
61+
Rc::new(Value::String(String::from(substring))),
62+
];
63+
assert_eq!(Value::Boolean(false), blank.invoke(args));
64+
}
65+
66+
#[test]
67+
fn hello_starts_with_empty_string() {
68+
let blank = StartsWithFn {};
69+
let s = "hello";
70+
let substring = "";
71+
let args = vec![
72+
Rc::new(Value::String(String::from(s))),
73+
Rc::new(Value::String(String::from(substring))),
74+
];
75+
assert_eq!(Value::Boolean(true), blank.invoke(args));
76+
}
77+
}
78+
}

src/environment.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ impl Environment {
208208
let blank_fn = clojure_string::blank_qmark_::BlankFn {};
209209
let upper_case_fn = clojure_string::upper_case::UpperCaseFn {};
210210
let lower_case_fn = clojure_string::lower_case::LowerCaseFn {};
211+
let starts_with_fn = clojure_string::starts_with_qmark_::StartsWithFn {};
212+
let ends_with_fn = clojure_string::ends_with_qmark_::EndsWithFn {};
211213

212214
// Hardcoded fns
213215
let lexical_eval_fn = Value::LexicalEvalFn {};
@@ -285,6 +287,18 @@ impl Environment {
285287
lower_case_fn.to_rc_value(),
286288
);
287289

290+
environment.insert_into_namespace(
291+
&Symbol::intern("clojure.string"),
292+
Symbol::intern("starts-with?"),
293+
starts_with_fn.to_rc_value(),
294+
);
295+
296+
environment.insert_into_namespace(
297+
&Symbol::intern("clojure.string"),
298+
Symbol::intern("ends-with?"),
299+
ends_with_fn.to_rc_value(),
300+
);
301+
288302
environment.insert(Symbol::intern("+"), add_fn.to_rc_value());
289303
environment.insert(Symbol::intern("let"), let_macro.to_rc_value());
290304
environment.insert(Symbol::intern("str"), str_fn.to_rc_value());

0 commit comments

Comments
 (0)