Skip to content

Commit 501f0df

Browse files
committed
implement SPARQL functions UCASE and LCASE
1 parent 2a9f0e8 commit 501f0df

File tree

3 files changed

+71
-3
lines changed

3 files changed

+71
-3
lines changed

sparql/src/function.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,18 @@ pub fn call_function(function: &Function, mut arguments: Vec<EvalResult>) -> Opt
117117
Some(str_len(string.as_string_lit()?.0))
118118
}
119119
Replace => todo("Replace"),
120-
UCase => todo("UCase"),
121-
LCase => todo("LCase"),
120+
UCase => {
121+
let [string] = &arguments[..] else {
122+
unreachable!();
123+
};
124+
Some(u_case(string.as_string_lit()?))
125+
}
126+
LCase => {
127+
let [string] = &arguments[..] else {
128+
unreachable!();
129+
};
130+
Some(l_case(string.as_string_lit()?))
131+
}
122132
EncodeForUri => todo("EncodeForUri"),
123133
Contains => todo("Contains"),
124134
StrStarts => todo("StrStarts"),
@@ -329,6 +339,16 @@ pub fn str_len(string: &Arc<str>) -> EvalResult {
329339
}
330340
}
331341

342+
pub fn u_case(source: (&Arc<str>, Option<&LanguageTag<Arc<str>>>)) -> EvalResult {
343+
let lex: String = source.0.chars().flat_map(char::to_uppercase).collect();
344+
EvalResult::from((Arc::from(lex), source.1.cloned()))
345+
}
346+
347+
pub fn l_case(source: (&Arc<str>, Option<&LanguageTag<Arc<str>>>)) -> EvalResult {
348+
let lex: String = source.0.chars().flat_map(char::to_lowercase).collect();
349+
EvalResult::from((Arc::from(lex), source.1.cloned()))
350+
}
351+
332352
pub fn triple(s: &EvalResult, p: &EvalResult, o: &EvalResult) -> Option<EvalResult> {
333353
let EvalResult::Term(s) = s else { return None };
334354
let EvalResult::Term(p) = p else { return None };

sparql/src/function/test.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,45 @@ fn sub_str(source: &str, start: f64, length: Option<f64>, exp: Option<&str>) ->
193193
fn str_len(string: &str, exp: isize) -> TestResult {
194194
let pair = txt2pair(string);
195195
let string = &pair.0;
196-
let source = (&pair.0, pair.1.as_ref());
197196
let exp = EvalResult::from(SparqlNumber::from(exp));
198197
assert!(eval_eq(Some(super::str_len(string)), Some(exp)));
199198
Ok(())
200199
}
201200

201+
#[test_case("foo", "FOO")]
202+
#[test_case("foo@en", "FOO@en")]
203+
#[test_case("FOO", "FOO"; "noop")]
204+
#[test_case("FOO@en", "FOO@en"; "noop en")]
205+
#[test_case("fooBAR 1!xY", "FOOBAR 1!XY")]
206+
#[test_case("fooBAR 1!xY@en", "FOOBAR 1!XY@en")]
207+
#[test_case("àéîôù", "ÀÉÎÔÙ"; "accents")]
208+
#[test_case("àéîôù@fr", "ÀÉÎÔÙ@fr"; "accents fr")]
209+
#[test_case("ff ʼn", "FF ʼN"; "multichar")]
210+
#[test_case("ff ʼn@en", "FF ʼN@en"; "multichar en")]
211+
fn u_case(string: &str, exp: &str) -> TestResult {
212+
let pair = txt2pair(string);
213+
let source = (&pair.0, pair.1.as_ref());
214+
let exp = EvalResult::from(txt2pair(exp));
215+
assert!(eval_eq(Some(super::u_case(source)), Some(exp)));
216+
Ok(())
217+
}
218+
219+
#[test_case("FOO", "foo")]
220+
#[test_case("FOO@en", "foo@en")]
221+
#[test_case("foo", "foo"; "noop")]
222+
#[test_case("foo@en", "foo@en"; "noop en")]
223+
#[test_case("fooBAR 1!xY", "foobar 1!xy")]
224+
#[test_case("fooBAR 1!xY@en", "foobar 1!xy@en")]
225+
#[test_case("ÀÉÎÔÙ", "àéîôù"; "accents")]
226+
#[test_case("ÀÉÎÔÙ@fr", "àéîôù@fr"; "accents fr")]
227+
fn l_case(string: &str, exp: &str) -> TestResult {
228+
let pair = txt2pair(string);
229+
let source = (&pair.0, pair.1.as_ref());
230+
let exp = EvalResult::from(txt2pair(exp));
231+
assert!(eval_eq(Some(super::l_case(source)), Some(exp)));
232+
Ok(())
233+
}
234+
202235
#[test_case("<tag:s>", "<tag:p>", "<tag:o>", true)]
203236
#[test_case("<tag:s>", "<tag:p>", "bnode()", true)]
204237
#[test_case("<tag:s>", "<tag:p>", " \"o\" ", true)]

sparql/src/test.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,21 @@ fn test_expr_variable() -> TestResult {
495495
#[test_case("strLen(\"foobar\"@en)", "6"; "strLen for language string")]
496496
#[test_case("strLen(42)", ""; "strLen for number")]
497497
#[test_case("strLen(<< <tag:s> <tag:p> <tag:o> >>)", ""; "strLen for triple")]
498+
// TODO test replace
499+
// test uCase
500+
#[test_case("uCase(<tag:x>)", ""; "uCase for IRI")]
501+
#[test_case("uCase(bnode())", ""; "uCase for bnode")]
502+
#[test_case("uCase(\"fooBAR\")", "\"FOOBAR\""; "uCase for string")]
503+
#[test_case("uCase(\"fooBAR\"@en)", "\"FOOBAR\"@en"; "uCase for language string")]
504+
#[test_case("uCase(42)", ""; "uCase for number")]
505+
#[test_case("uCase(<< <tag:s> <tag:p> <tag:o> >>)", ""; "uCase for triple")]
506+
// test lCase
507+
#[test_case("lCase(<tag:x>)", ""; "lCase for IRI")]
508+
#[test_case("lCase(bnode())", ""; "lCase for bnode")]
509+
#[test_case("lCase(\"fooBAR\")", "\"foobar\""; "lCase for string")]
510+
#[test_case("lCase(\"fooBAR\"@en)", "\"foobar\"@en"; "lCase for language string")]
511+
#[test_case("lCase(42)", ""; "lCase for number")]
512+
#[test_case("lCase(<< <tag:s> <tag:p> <tag:o> >>)", ""; "lCase for triple")]
498513
// TODO test other function calls
499514
// test isIri
500515
#[test_case("isIri(<tag:x>)", "true"; "isIri for IRI")]

0 commit comments

Comments
 (0)