rdf_fusion_functions/scalar/strings/
replace.rs1use crate::scalar::dispatch::{
2 dispatch_quaternary_owned_typed_value, dispatch_ternary_owned_typed_value,
3};
4use crate::scalar::sparql_op_impl::{
5 ScalarSparqlOpImpl, create_typed_value_sparql_op_impl,
6};
7use crate::scalar::strings::regex::compile_pattern;
8use crate::scalar::{ScalarSparqlOp, ScalarSparqlOpSignature, SparqlOpArity};
9use rdf_fusion_encoding::typed_value::TypedValueEncoding;
10use rdf_fusion_extensions::functions::BuiltinName;
11use rdf_fusion_extensions::functions::FunctionName;
12use rdf_fusion_model::{
13 LanguageString, SimpleLiteral, SimpleLiteralRef, StringLiteralRef, ThinError,
14 TypedValue, TypedValueRef,
15};
16use std::borrow::Cow;
17
18#[derive(Debug, Hash, PartialEq, Eq)]
20pub struct ReplaceSparqlOp;
21
22impl Default for ReplaceSparqlOp {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28impl ReplaceSparqlOp {
29 const NAME: FunctionName = FunctionName::Builtin(BuiltinName::Replace);
30
31 pub fn new() -> Self {
33 Self {}
34 }
35}
36
37impl ScalarSparqlOp for ReplaceSparqlOp {
38 fn name(&self) -> &FunctionName {
39 &Self::NAME
40 }
41
42 fn signature(&self) -> ScalarSparqlOpSignature {
43 ScalarSparqlOpSignature::default_with_arity(SparqlOpArity::OneOf(vec![
44 SparqlOpArity::Fixed(3),
45 SparqlOpArity::Fixed(4),
46 ]))
47 }
48
49 fn typed_value_encoding_op(
50 &self,
51 ) -> Option<Box<dyn ScalarSparqlOpImpl<TypedValueEncoding>>> {
52 Some(create_typed_value_sparql_op_impl(|args| {
53 match args.args.len() {
54 3 => dispatch_ternary_owned_typed_value(
55 &args.args[0],
56 &args.args[1],
57 &args.args[2],
58 |arg0, arg1, arg2| evaluate_replace(arg0, arg1, arg2, None)?,
59 |_, _, _| ThinError::expected(),
60 ),
61 4 => dispatch_quaternary_owned_typed_value(
62 &args.args[0],
63 &args.args[1],
64 &args.args[2],
65 &args.args[3],
66 |arg0, arg1, arg2, arg3| {
67 evaluate_replace(arg0, arg1, arg2, Some(arg3))?
68 },
69 |_, _, _, _| ThinError::expected(),
70 ),
71 _ => unreachable!("Invalid number of arguments"),
72 }
73 }))
74 }
75}
76
77fn evaluate_replace(
78 arg0: TypedValueRef<'_>,
79 arg1: TypedValueRef<'_>,
80 arg2: TypedValueRef<'_>,
81 arg3: Option<TypedValueRef<'_>>,
82) -> Result<Result<TypedValue, ThinError>, ThinError> {
83 let arg0 = StringLiteralRef::try_from(arg0)?;
84 let arg1 = SimpleLiteralRef::try_from(arg1)?;
85 let arg2 = SimpleLiteralRef::try_from(arg2)?;
86 let arg3 = arg3.map(SimpleLiteralRef::try_from).transpose()?;
87
88 let regex = compile_pattern(arg1.value, arg3.map(|lit| lit.value))?;
89
90 let result = match regex.replace_all(arg0.0, arg2.value) {
91 Cow::Owned(replaced) => replaced,
92 Cow::Borrowed(_) => arg0.0.to_owned(),
93 };
94
95 Ok(Ok(match arg0.1 {
96 None => TypedValue::SimpleLiteral(SimpleLiteral { value: result }),
97 Some(language) => TypedValue::LanguageStringLiteral(LanguageString {
98 value: result,
99 language: language.to_owned(),
100 }),
101 }))
102}