diff --git a/sparql/src/function.rs b/sparql/src/function.rs index 6380cd6..d65ea2a 100644 --- a/sparql/src/function.rs +++ b/sparql/src/function.rs @@ -117,8 +117,18 @@ pub fn call_function(function: &Function, mut arguments: Vec) -> Opt Some(str_len(string.as_string_lit()?.0)) } Replace => todo("Replace"), - UCase => todo("UCase"), - LCase => todo("LCase"), + UCase => { + let [string] = &arguments[..] else { + unreachable!(); + }; + Some(u_case(string.as_string_lit()?)) + } + LCase => { + let [string] = &arguments[..] else { + unreachable!(); + }; + Some(l_case(string.as_string_lit()?)) + } EncodeForUri => todo("EncodeForUri"), Contains => todo("Contains"), StrStarts => todo("StrStarts"), @@ -329,6 +339,16 @@ pub fn str_len(string: &Arc) -> EvalResult { } } +pub fn u_case(source: (&Arc, Option<&LanguageTag>>)) -> EvalResult { + let lex: String = source.0.chars().flat_map(char::to_uppercase).collect(); + EvalResult::from((Arc::from(lex), source.1.cloned())) +} + +pub fn l_case(source: (&Arc, Option<&LanguageTag>>)) -> EvalResult { + let lex: String = source.0.chars().flat_map(char::to_lowercase).collect(); + EvalResult::from((Arc::from(lex), source.1.cloned())) +} + pub fn triple(s: &EvalResult, p: &EvalResult, o: &EvalResult) -> Option { let EvalResult::Term(s) = s else { return None }; let EvalResult::Term(p) = p else { return None }; diff --git a/sparql/src/function/test.rs b/sparql/src/function/test.rs index 2b20d9c..75718d6 100644 --- a/sparql/src/function/test.rs +++ b/sparql/src/function/test.rs @@ -193,12 +193,45 @@ fn sub_str(source: &str, start: f64, length: Option, exp: Option<&str>) -> fn str_len(string: &str, exp: isize) -> TestResult { let pair = txt2pair(string); let string = &pair.0; - let source = (&pair.0, pair.1.as_ref()); let exp = EvalResult::from(SparqlNumber::from(exp)); assert!(eval_eq(Some(super::str_len(string)), Some(exp))); Ok(()) } +#[test_case("foo", "FOO")] +#[test_case("foo@en", "FOO@en")] +#[test_case("FOO", "FOO"; "noop")] +#[test_case("FOO@en", "FOO@en"; "noop en")] +#[test_case("fooBAR 1!xY", "FOOBAR 1!XY")] +#[test_case("fooBAR 1!xY@en", "FOOBAR 1!XY@en")] +#[test_case("àéîôù", "ÀÉÎÔÙ"; "accents")] +#[test_case("àéîôù@fr", "ÀÉÎÔÙ@fr"; "accents fr")] +#[test_case("ff ʼn", "FF ʼN"; "multichar")] +#[test_case("ff ʼn@en", "FF ʼN@en"; "multichar en")] +fn u_case(string: &str, exp: &str) -> TestResult { + let pair = txt2pair(string); + let source = (&pair.0, pair.1.as_ref()); + let exp = EvalResult::from(txt2pair(exp)); + assert!(eval_eq(Some(super::u_case(source)), Some(exp))); + Ok(()) +} + +#[test_case("FOO", "foo")] +#[test_case("FOO@en", "foo@en")] +#[test_case("foo", "foo"; "noop")] +#[test_case("foo@en", "foo@en"; "noop en")] +#[test_case("fooBAR 1!xY", "foobar 1!xy")] +#[test_case("fooBAR 1!xY@en", "foobar 1!xy@en")] +#[test_case("ÀÉÎÔÙ", "àéîôù"; "accents")] +#[test_case("ÀÉÎÔÙ@fr", "àéîôù@fr"; "accents fr")] +fn l_case(string: &str, exp: &str) -> TestResult { + let pair = txt2pair(string); + let source = (&pair.0, pair.1.as_ref()); + let exp = EvalResult::from(txt2pair(exp)); + assert!(eval_eq(Some(super::l_case(source)), Some(exp))); + Ok(()) +} + #[test_case("", "", "", true)] #[test_case("", "", "bnode()", true)] #[test_case("", "", " \"o\" ", true)] diff --git a/sparql/src/test.rs b/sparql/src/test.rs index c3ea991..9a69fef 100644 --- a/sparql/src/test.rs +++ b/sparql/src/test.rs @@ -495,6 +495,21 @@ fn test_expr_variable() -> TestResult { #[test_case("strLen(\"foobar\"@en)", "6"; "strLen for language string")] #[test_case("strLen(42)", ""; "strLen for number")] #[test_case("strLen(<< >>)", ""; "strLen for triple")] +// TODO test replace +// test uCase +#[test_case("uCase()", ""; "uCase for IRI")] +#[test_case("uCase(bnode())", ""; "uCase for bnode")] +#[test_case("uCase(\"fooBAR\")", "\"FOOBAR\""; "uCase for string")] +#[test_case("uCase(\"fooBAR\"@en)", "\"FOOBAR\"@en"; "uCase for language string")] +#[test_case("uCase(42)", ""; "uCase for number")] +#[test_case("uCase(<< >>)", ""; "uCase for triple")] +// test lCase +#[test_case("lCase()", ""; "lCase for IRI")] +#[test_case("lCase(bnode())", ""; "lCase for bnode")] +#[test_case("lCase(\"fooBAR\")", "\"foobar\""; "lCase for string")] +#[test_case("lCase(\"fooBAR\"@en)", "\"foobar\"@en"; "lCase for language string")] +#[test_case("lCase(42)", ""; "lCase for number")] +#[test_case("lCase(<< >>)", ""; "lCase for triple")] // TODO test other function calls // test isIri #[test_case("isIri()", "true"; "isIri for IRI")]