rdf_fusion_functions/scalar/strings/
concat.rs

1use crate::scalar::dispatch::dispatch_n_ary_owned_typed_value;
2use crate::scalar::sparql_op_impl::{
3    ScalarSparqlOpImpl, create_typed_value_sparql_op_impl,
4};
5use crate::scalar::{ScalarSparqlOp, ScalarSparqlOpSignature, SparqlOpArity};
6use rdf_fusion_encoding::typed_value::TypedValueEncoding;
7use rdf_fusion_extensions::functions::BuiltinName;
8use rdf_fusion_extensions::functions::FunctionName;
9use rdf_fusion_model::{
10    LanguageString, SimpleLiteral, StringLiteralRef, ThinError, ThinResult, TypedValue,
11};
12
13#[derive(Debug, Hash, PartialEq, Eq)]
14pub struct ConcatSparqlOp;
15
16impl Default for ConcatSparqlOp {
17    fn default() -> Self {
18        Self::new()
19    }
20}
21
22impl ConcatSparqlOp {
23    const NAME: FunctionName = FunctionName::Builtin(BuiltinName::Concat);
24
25    pub fn new() -> Self {
26        Self {}
27    }
28}
29
30impl ScalarSparqlOp for ConcatSparqlOp {
31    fn name(&self) -> &FunctionName {
32        &Self::NAME
33    }
34
35    fn signature(&self) -> ScalarSparqlOpSignature {
36        ScalarSparqlOpSignature::default_with_arity(SparqlOpArity::Variadic)
37    }
38
39    fn typed_value_encoding_op(
40        &self,
41    ) -> Option<Box<dyn ScalarSparqlOpImpl<TypedValueEncoding>>> {
42        Some(create_typed_value_sparql_op_impl(|args| {
43            dispatch_n_ary_owned_typed_value(
44                args.args.as_slice(),
45                args.number_rows,
46                |args| {
47                    let args = args
48                        .iter()
49                        .map(|arg| StringLiteralRef::try_from(*arg))
50                        .collect::<ThinResult<Vec<_>>>()?;
51
52                    let mut result = String::default();
53                    let mut language = None;
54
55                    for arg in args {
56                        if let Some(lang) = &language {
57                            if *lang != arg.1 {
58                                language = Some(None)
59                            }
60                        } else {
61                            language = Some(arg.1)
62                        }
63                        result += arg.0;
64                    }
65
66                    Ok(match language.flatten().map(ToOwned::to_owned) {
67                        Some(language) => {
68                            TypedValue::LanguageStringLiteral(LanguageString {
69                                value: result,
70                                language,
71                            })
72                        }
73                        None => {
74                            TypedValue::SimpleLiteral(SimpleLiteral { value: result })
75                        }
76                    })
77                },
78                |_| ThinError::expected(),
79            )
80        }))
81    }
82}