oxirs_shacl/advanced_features/
functions.rs

1//! SHACL Advanced Features - Functions
2//!
3//! Implementation of SHACL Functions for custom operations and transformations.
4//! Provides built-in functions and a registry for custom functions.
5//!
6//! Based on the W3C SHACL Advanced Features specification.
7
8use crate::{Result, ShaclError};
9use oxirs_core::{
10    model::{Literal, NamedNode, Term},
11    Store,
12};
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15use std::sync::Arc;
16
17/// Parameter type for SHACL functions
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19pub enum ParameterType {
20    /// IRI/URI parameter
21    Iri,
22    /// Literal value parameter
23    Literal,
24    /// Any RDF term (IRI, literal, or blank node)
25    RdfTerm,
26    /// Boolean parameter
27    Boolean,
28    /// Integer parameter
29    Integer,
30    /// Decimal parameter
31    Decimal,
32    /// String parameter
33    String,
34    /// Custom type parameter
35    Custom(String),
36    /// List of values
37    List(Box<ParameterType>),
38}
39
40/// Function parameter definition
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct FunctionParameter {
43    /// Parameter name
44    pub name: String,
45    /// Parameter type
46    pub param_type: ParameterType,
47    /// Whether this parameter is optional
48    pub optional: bool,
49    /// Parameter order/position
50    pub order: usize,
51    /// Default value if parameter is optional
52    pub default_value: Option<String>,
53    /// Parameter description
54    pub description: Option<String>,
55}
56
57impl FunctionParameter {
58    /// Create a new required parameter
59    pub fn required(name: impl Into<String>, param_type: ParameterType, order: usize) -> Self {
60        Self {
61            name: name.into(),
62            param_type,
63            optional: false,
64            order,
65            default_value: None,
66            description: None,
67        }
68    }
69
70    /// Create a new optional parameter
71    pub fn optional(
72        name: impl Into<String>,
73        param_type: ParameterType,
74        order: usize,
75        default_value: Option<String>,
76    ) -> Self {
77        Self {
78            name: name.into(),
79            param_type,
80            optional: true,
81            order,
82            default_value,
83            description: None,
84        }
85    }
86
87    /// Add description to parameter
88    pub fn with_description(mut self, description: impl Into<String>) -> Self {
89        self.description = Some(description.into());
90        self
91    }
92}
93
94/// Return type for SHACL functions
95#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
96pub enum ReturnType {
97    /// Returns a single value
98    Single(ParameterType),
99    /// Returns a list of values
100    List(ParameterType),
101    /// Returns multiple different types
102    Multiple(Vec<ParameterType>),
103    /// Returns void/nothing
104    Void,
105}
106
107/// Function metadata
108#[derive(Debug, Clone, Serialize, Deserialize, Default)]
109pub struct FunctionMetadata {
110    /// Function author
111    pub author: Option<String>,
112    /// Function version
113    pub version: Option<String>,
114    /// Function description
115    pub description: Option<String>,
116    /// Function documentation URL
117    pub documentation: Option<String>,
118    /// Function tags
119    pub tags: Vec<String>,
120    /// Custom metadata
121    pub custom: HashMap<String, String>,
122}
123
124/// SHACL Function definition
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct ShaclFunction {
127    /// Unique function identifier (IRI)
128    pub id: String,
129    /// Function name
130    pub name: String,
131    /// Function parameters
132    pub parameters: Vec<FunctionParameter>,
133    /// Return type
134    pub return_type: ReturnType,
135    /// Function metadata
136    pub metadata: FunctionMetadata,
137    /// Whether this function has side effects
138    pub has_side_effects: bool,
139    /// Whether this function is deterministic
140    pub deterministic: bool,
141}
142
143impl ShaclFunction {
144    /// Create a new function
145    pub fn new(
146        id: impl Into<String>,
147        name: impl Into<String>,
148        parameters: Vec<FunctionParameter>,
149        return_type: ReturnType,
150    ) -> Self {
151        Self {
152            id: id.into(),
153            name: name.into(),
154            parameters,
155            return_type,
156            metadata: FunctionMetadata::default(),
157            has_side_effects: false,
158            deterministic: true,
159        }
160    }
161
162    /// Set metadata
163    pub fn with_metadata(mut self, metadata: FunctionMetadata) -> Self {
164        self.metadata = metadata;
165        self
166    }
167
168    /// Mark as having side effects
169    pub fn with_side_effects(mut self) -> Self {
170        self.has_side_effects = true;
171        self
172    }
173
174    /// Mark as non-deterministic
175    pub fn non_deterministic(mut self) -> Self {
176        self.deterministic = false;
177        self
178    }
179
180    /// Validate arguments against parameter definitions
181    pub fn validate_arguments(&self, arguments: &HashMap<String, Term>) -> Result<()> {
182        // Check required parameters
183        for param in &self.parameters {
184            if !param.optional && !arguments.contains_key(&param.name) {
185                return Err(ShaclError::ValidationEngine(format!(
186                    "Missing required parameter: {}",
187                    param.name
188                )));
189            }
190        }
191
192        // Check for unknown parameters
193        for arg_name in arguments.keys() {
194            if !self.parameters.iter().any(|p| &p.name == arg_name) {
195                return Err(ShaclError::ValidationEngine(format!(
196                    "Unknown parameter: {}",
197                    arg_name
198                )));
199            }
200        }
201
202        Ok(())
203    }
204}
205
206/// Function invocation
207#[derive(Debug, Clone)]
208pub struct FunctionInvocation {
209    /// Function identifier
210    pub function_id: String,
211    /// Arguments passed to the function
212    pub arguments: HashMap<String, Term>,
213    /// Context for execution
214    pub context: FunctionContext,
215}
216
217/// Function execution context
218#[derive(Debug, Clone, Default)]
219pub struct FunctionContext {
220    /// Variables available in the context
221    pub variables: HashMap<String, Term>,
222    /// Execution depth (for recursion detection)
223    pub depth: usize,
224    /// Maximum allowed depth
225    pub max_depth: usize,
226}
227
228impl FunctionContext {
229    /// Create a new context
230    pub fn new() -> Self {
231        Self {
232            variables: HashMap::new(),
233            depth: 0,
234            max_depth: 100,
235        }
236    }
237
238    /// Increment depth
239    pub fn increment_depth(&mut self) -> Result<()> {
240        self.depth += 1;
241        if self.depth > self.max_depth {
242            return Err(ShaclError::RecursionLimit(format!(
243                "Maximum function call depth exceeded: {}",
244                self.max_depth
245            )));
246        }
247        Ok(())
248    }
249
250    /// Decrement depth
251    pub fn decrement_depth(&mut self) {
252        if self.depth > 0 {
253            self.depth -= 1;
254        }
255    }
256}
257
258/// Function execution result
259#[derive(Debug, Clone)]
260pub enum FunctionResult {
261    /// Single value result
262    Single(Option<Term>),
263    /// Multiple values result
264    Multiple(Vec<Term>),
265    /// Error during execution
266    Error(String),
267}
268
269impl FunctionResult {
270    /// Create a success result with a single value
271    pub fn value(term: Term) -> Self {
272        FunctionResult::Single(Some(term))
273    }
274
275    /// Create a success result with no value
276    pub fn none() -> Self {
277        FunctionResult::Single(None)
278    }
279
280    /// Create a success result with multiple values
281    pub fn values(terms: Vec<Term>) -> Self {
282        FunctionResult::Multiple(terms)
283    }
284
285    /// Create an error result
286    pub fn error(message: impl Into<String>) -> Self {
287        FunctionResult::Error(message.into())
288    }
289
290    /// Check if result is an error
291    pub fn is_error(&self) -> bool {
292        matches!(self, FunctionResult::Error(_))
293    }
294
295    /// Get single value if available
296    pub fn as_single(&self) -> Option<&Term> {
297        match self {
298            FunctionResult::Single(Some(term)) => Some(term),
299            _ => None,
300        }
301    }
302
303    /// Get multiple values if available
304    pub fn as_multiple(&self) -> Option<&[Term]> {
305        match self {
306            FunctionResult::Multiple(terms) => Some(terms),
307            _ => None,
308        }
309    }
310}
311
312/// Function executor trait
313pub trait FunctionExecutor: Send + Sync {
314    /// Execute a function
315    fn execute(
316        &self,
317        function: &ShaclFunction,
318        invocation: &FunctionInvocation,
319        store: &dyn Store,
320    ) -> Result<FunctionResult>;
321
322    /// Get executor name
323    fn name(&self) -> &str;
324
325    /// Check if executor can handle a function
326    fn can_execute(&self, function: &ShaclFunction) -> bool;
327}
328
329/// Built-in function executor
330pub struct BuiltInFunctionExecutor;
331
332impl BuiltInFunctionExecutor {
333    /// Create a new built-in function executor
334    pub fn new() -> Self {
335        Self
336    }
337
338    /// Execute string concatenation
339    fn concat(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
340        let mut result = String::new();
341        let mut i = 0;
342        loop {
343            let key = format!("arg{}", i);
344            if let Some(term) = args.get(&key) {
345                match term {
346                    Term::Literal(lit) => result.push_str(lit.value()),
347                    _ => result.push_str(&term.to_string()),
348                }
349                i += 1;
350            } else {
351                break;
352            }
353        }
354        Ok(FunctionResult::value(Term::Literal(
355            Literal::new_simple_literal(result),
356        )))
357    }
358
359    /// Execute string uppercase
360    fn upper_case(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
361        if let Some(Term::Literal(lit)) = args.get("input") {
362            let upper = lit.value().to_uppercase();
363            Ok(FunctionResult::value(Term::Literal(
364                Literal::new_simple_literal(upper),
365            )))
366        } else {
367            Err(ShaclError::ValidationEngine(
368                "upperCase requires a string literal argument".to_string(),
369            ))
370        }
371    }
372
373    /// Execute string lowercase
374    fn lower_case(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
375        if let Some(Term::Literal(lit)) = args.get("input") {
376            let lower = lit.value().to_lowercase();
377            Ok(FunctionResult::value(Term::Literal(
378                Literal::new_simple_literal(lower),
379            )))
380        } else {
381            Err(ShaclError::ValidationEngine(
382                "lowerCase requires a string literal argument".to_string(),
383            ))
384        }
385    }
386
387    /// Execute substring
388    fn substring(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
389        if let Some(Term::Literal(lit)) = args.get("input") {
390            let start = match args.get("start") {
391                Some(Term::Literal(l)) => l
392                    .value()
393                    .parse::<usize>()
394                    .map_err(|_| ShaclError::ValidationEngine("Invalid start index".to_string()))?,
395                _ => {
396                    return Err(ShaclError::ValidationEngine(
397                        "substring requires start index".to_string(),
398                    ))
399                }
400            };
401
402            let value = lit.value();
403            let result: String = if let Some(Term::Literal(l)) = args.get("length") {
404                let length = l
405                    .value()
406                    .parse::<usize>()
407                    .map_err(|_| ShaclError::ValidationEngine("Invalid length".to_string()))?;
408                value.chars().skip(start).take(length).collect()
409            } else {
410                value.chars().skip(start).collect()
411            };
412
413            Ok(FunctionResult::value(Term::Literal(
414                Literal::new_simple_literal(result),
415            )))
416        } else {
417            Err(ShaclError::ValidationEngine(
418                "substring requires a string literal input".to_string(),
419            ))
420        }
421    }
422
423    /// Execute string length
424    fn str_length(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
425        if let Some(Term::Literal(lit)) = args.get("input") {
426            let length = lit.value().chars().count();
427            Ok(FunctionResult::value(Term::Literal(
428                Literal::new_typed_literal(
429                    length.to_string(),
430                    NamedNode::new_unchecked("http://www.w3.org/2001/XMLSchema#integer"),
431                ),
432            )))
433        } else {
434            Err(ShaclError::ValidationEngine(
435                "strLength requires a string literal argument".to_string(),
436            ))
437        }
438    }
439
440    /// Execute absolute value
441    fn abs(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
442        if let Some(Term::Literal(lit)) = args.get("value") {
443            let value: f64 = lit.value().parse().map_err(|_| {
444                ShaclError::ValidationEngine("abs requires a numeric argument".to_string())
445            })?;
446            let result = value.abs();
447            Ok(FunctionResult::value(Term::Literal(
448                Literal::new_typed_literal(
449                    result.to_string(),
450                    NamedNode::new_unchecked("http://www.w3.org/2001/XMLSchema#decimal"),
451                ),
452            )))
453        } else {
454            Err(ShaclError::ValidationEngine(
455                "abs requires a numeric literal argument".to_string(),
456            ))
457        }
458    }
459
460    /// Execute ceiling
461    fn ceil(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
462        if let Some(Term::Literal(lit)) = args.get("value") {
463            let value: f64 = lit.value().parse().map_err(|_| {
464                ShaclError::ValidationEngine("ceil requires a numeric argument".to_string())
465            })?;
466            let result = value.ceil();
467            Ok(FunctionResult::value(Term::Literal(
468                Literal::new_typed_literal(
469                    result.to_string(),
470                    NamedNode::new_unchecked("http://www.w3.org/2001/XMLSchema#integer"),
471                ),
472            )))
473        } else {
474            Err(ShaclError::ValidationEngine(
475                "ceil requires a numeric literal argument".to_string(),
476            ))
477        }
478    }
479
480    /// Execute floor
481    fn floor(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
482        if let Some(Term::Literal(lit)) = args.get("value") {
483            let value: f64 = lit.value().parse().map_err(|_| {
484                ShaclError::ValidationEngine("floor requires a numeric argument".to_string())
485            })?;
486            let result = value.floor();
487            Ok(FunctionResult::value(Term::Literal(
488                Literal::new_typed_literal(
489                    result.to_string(),
490                    NamedNode::new_unchecked("http://www.w3.org/2001/XMLSchema#integer"),
491                ),
492            )))
493        } else {
494            Err(ShaclError::ValidationEngine(
495                "floor requires a numeric literal argument".to_string(),
496            ))
497        }
498    }
499
500    /// Execute round
501    fn round(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
502        if let Some(Term::Literal(lit)) = args.get("value") {
503            let value: f64 = lit.value().parse().map_err(|_| {
504                ShaclError::ValidationEngine("round requires a numeric argument".to_string())
505            })?;
506            let result = value.round();
507            Ok(FunctionResult::value(Term::Literal(
508                Literal::new_typed_literal(
509                    result.to_string(),
510                    NamedNode::new_unchecked("http://www.w3.org/2001/XMLSchema#integer"),
511                ),
512            )))
513        } else {
514            Err(ShaclError::ValidationEngine(
515                "round requires a numeric literal argument".to_string(),
516            ))
517        }
518    }
519
520    /// Execute contains (string contains substring)
521    fn contains(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
522        if let (Some(Term::Literal(haystack)), Some(Term::Literal(needle))) =
523            (args.get("string"), args.get("substring"))
524        {
525            let result = haystack.value().contains(needle.value());
526            Ok(FunctionResult::value(Term::Literal(
527                Literal::new_typed_literal(
528                    result.to_string(),
529                    NamedNode::new_unchecked("http://www.w3.org/2001/XMLSchema#boolean"),
530                ),
531            )))
532        } else {
533            Err(ShaclError::ValidationEngine(
534                "contains requires string and substring arguments".to_string(),
535            ))
536        }
537    }
538
539    /// Execute starts-with
540    fn starts_with(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
541        if let (Some(Term::Literal(string)), Some(Term::Literal(prefix))) =
542            (args.get("string"), args.get("prefix"))
543        {
544            let result = string.value().starts_with(prefix.value());
545            Ok(FunctionResult::value(Term::Literal(
546                Literal::new_typed_literal(
547                    result.to_string(),
548                    NamedNode::new_unchecked("http://www.w3.org/2001/XMLSchema#boolean"),
549                ),
550            )))
551        } else {
552            Err(ShaclError::ValidationEngine(
553                "startsWith requires string and prefix arguments".to_string(),
554            ))
555        }
556    }
557
558    /// Execute ends-with
559    fn ends_with(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
560        if let (Some(Term::Literal(string)), Some(Term::Literal(suffix))) =
561            (args.get("string"), args.get("suffix"))
562        {
563            let result = string.value().ends_with(suffix.value());
564            Ok(FunctionResult::value(Term::Literal(
565                Literal::new_typed_literal(
566                    result.to_string(),
567                    NamedNode::new_unchecked("http://www.w3.org/2001/XMLSchema#boolean"),
568                ),
569            )))
570        } else {
571            Err(ShaclError::ValidationEngine(
572                "endsWith requires string and suffix arguments".to_string(),
573            ))
574        }
575    }
576
577    /// Execute trim
578    fn trim(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
579        if let Some(Term::Literal(lit)) = args.get("input") {
580            let trimmed = lit.value().trim();
581            Ok(FunctionResult::value(Term::Literal(
582                Literal::new_simple_literal(trimmed),
583            )))
584        } else {
585            Err(ShaclError::ValidationEngine(
586                "trim requires a string literal argument".to_string(),
587            ))
588        }
589    }
590
591    /// Execute replace
592    fn replace(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
593        if let (
594            Some(Term::Literal(input)),
595            Some(Term::Literal(pattern)),
596            Some(Term::Literal(replacement)),
597        ) = (
598            args.get("input"),
599            args.get("pattern"),
600            args.get("replacement"),
601        ) {
602            let result = input.value().replace(pattern.value(), replacement.value());
603            Ok(FunctionResult::value(Term::Literal(
604                Literal::new_simple_literal(result),
605            )))
606        } else {
607            Err(ShaclError::ValidationEngine(
608                "replace requires input, pattern, and replacement arguments".to_string(),
609            ))
610        }
611    }
612
613    /// Execute min
614    fn min(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
615        if let (Some(Term::Literal(val1)), Some(Term::Literal(val2))) =
616            (args.get("value1"), args.get("value2"))
617        {
618            let v1: f64 = val1.value().parse().map_err(|_| {
619                ShaclError::ValidationEngine("min requires numeric arguments".to_string())
620            })?;
621            let v2: f64 = val2.value().parse().map_err(|_| {
622                ShaclError::ValidationEngine("min requires numeric arguments".to_string())
623            })?;
624            let result = v1.min(v2);
625            Ok(FunctionResult::value(Term::Literal(
626                Literal::new_typed_literal(
627                    result.to_string(),
628                    NamedNode::new_unchecked("http://www.w3.org/2001/XMLSchema#decimal"),
629                ),
630            )))
631        } else {
632            Err(ShaclError::ValidationEngine(
633                "min requires two numeric literal arguments".to_string(),
634            ))
635        }
636    }
637
638    /// Execute max
639    fn max(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
640        if let (Some(Term::Literal(val1)), Some(Term::Literal(val2))) =
641            (args.get("value1"), args.get("value2"))
642        {
643            let v1: f64 = val1.value().parse().map_err(|_| {
644                ShaclError::ValidationEngine("max requires numeric arguments".to_string())
645            })?;
646            let v2: f64 = val2.value().parse().map_err(|_| {
647                ShaclError::ValidationEngine("max requires numeric arguments".to_string())
648            })?;
649            let result = v1.max(v2);
650            Ok(FunctionResult::value(Term::Literal(
651                Literal::new_typed_literal(
652                    result.to_string(),
653                    NamedNode::new_unchecked("http://www.w3.org/2001/XMLSchema#decimal"),
654                ),
655            )))
656        } else {
657            Err(ShaclError::ValidationEngine(
658                "max requires two numeric literal arguments".to_string(),
659            ))
660        }
661    }
662
663    /// Execute sqrt
664    fn sqrt(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
665        if let Some(Term::Literal(lit)) = args.get("value") {
666            let value: f64 = lit.value().parse().map_err(|_| {
667                ShaclError::ValidationEngine("sqrt requires a numeric argument".to_string())
668            })?;
669            if value < 0.0 {
670                return Err(ShaclError::ValidationEngine(
671                    "sqrt requires a non-negative argument".to_string(),
672                ));
673            }
674            let result = value.sqrt();
675            Ok(FunctionResult::value(Term::Literal(
676                Literal::new_typed_literal(
677                    result.to_string(),
678                    NamedNode::new_unchecked("http://www.w3.org/2001/XMLSchema#decimal"),
679                ),
680            )))
681        } else {
682            Err(ShaclError::ValidationEngine(
683                "sqrt requires a numeric literal argument".to_string(),
684            ))
685        }
686    }
687
688    /// Execute pow
689    fn pow(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
690        if let (Some(Term::Literal(base)), Some(Term::Literal(exponent))) =
691            (args.get("base"), args.get("exponent"))
692        {
693            let base_val: f64 = base.value().parse().map_err(|_| {
694                ShaclError::ValidationEngine("pow requires numeric arguments".to_string())
695            })?;
696            let exp_val: f64 = exponent.value().parse().map_err(|_| {
697                ShaclError::ValidationEngine("pow requires numeric arguments".to_string())
698            })?;
699            let result = base_val.powf(exp_val);
700            Ok(FunctionResult::value(Term::Literal(
701                Literal::new_typed_literal(
702                    result.to_string(),
703                    NamedNode::new_unchecked("http://www.w3.org/2001/XMLSchema#decimal"),
704                ),
705            )))
706        } else {
707            Err(ShaclError::ValidationEngine(
708                "pow requires two numeric literal arguments".to_string(),
709            ))
710        }
711    }
712
713    /// Execute encodeURI
714    fn encode_uri(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
715        if let Some(Term::Literal(lit)) = args.get("input") {
716            use url::form_urlencoded;
717            let encoded: String = form_urlencoded::byte_serialize(lit.value().as_bytes()).collect();
718            Ok(FunctionResult::value(Term::Literal(
719                Literal::new_simple_literal(encoded),
720            )))
721        } else {
722            Err(ShaclError::ValidationEngine(
723                "encodeURI requires a string literal argument".to_string(),
724            ))
725        }
726    }
727
728    /// Execute decodeURI
729    fn decode_uri(&self, args: &HashMap<String, Term>) -> Result<FunctionResult> {
730        if let Some(Term::Literal(lit)) = args.get("input") {
731            use url::form_urlencoded;
732            let decoded = form_urlencoded::parse(lit.value().as_bytes())
733                .map(|(key, _)| key)
734                .collect::<Vec<_>>()
735                .join("");
736            Ok(FunctionResult::value(Term::Literal(
737                Literal::new_simple_literal(decoded),
738            )))
739        } else {
740            Err(ShaclError::ValidationEngine(
741                "decodeURI requires a string literal argument".to_string(),
742            ))
743        }
744    }
745}
746
747impl Default for BuiltInFunctionExecutor {
748    fn default() -> Self {
749        Self::new()
750    }
751}
752
753impl FunctionExecutor for BuiltInFunctionExecutor {
754    fn execute(
755        &self,
756        function: &ShaclFunction,
757        invocation: &FunctionInvocation,
758        _store: &dyn Store,
759    ) -> Result<FunctionResult> {
760        // Validate arguments
761        function.validate_arguments(&invocation.arguments)?;
762
763        // Execute based on function name
764        match function.name.as_str() {
765            // String functions
766            "concat" => self.concat(&invocation.arguments),
767            "upperCase" => self.upper_case(&invocation.arguments),
768            "lowerCase" => self.lower_case(&invocation.arguments),
769            "substring" => self.substring(&invocation.arguments),
770            "strLength" => self.str_length(&invocation.arguments),
771            "contains" => self.contains(&invocation.arguments),
772            "startsWith" => self.starts_with(&invocation.arguments),
773            "endsWith" => self.ends_with(&invocation.arguments),
774            "trim" => self.trim(&invocation.arguments),
775            "replace" => self.replace(&invocation.arguments),
776            // Mathematical functions
777            "abs" => self.abs(&invocation.arguments),
778            "ceil" => self.ceil(&invocation.arguments),
779            "floor" => self.floor(&invocation.arguments),
780            "round" => self.round(&invocation.arguments),
781            "min" => self.min(&invocation.arguments),
782            "max" => self.max(&invocation.arguments),
783            "sqrt" => self.sqrt(&invocation.arguments),
784            "pow" => self.pow(&invocation.arguments),
785            // URI encoding functions
786            "encodeURI" => self.encode_uri(&invocation.arguments),
787            "decodeURI" => self.decode_uri(&invocation.arguments),
788            _ => Err(ShaclError::UnsupportedOperation(format!(
789                "Unknown built-in function: {}",
790                function.name
791            ))),
792        }
793    }
794
795    fn name(&self) -> &str {
796        "BuiltInFunctionExecutor"
797    }
798
799    fn can_execute(&self, function: &ShaclFunction) -> bool {
800        matches!(
801            function.name.as_str(),
802            "concat"
803                | "upperCase"
804                | "lowerCase"
805                | "substring"
806                | "strLength"
807                | "contains"
808                | "startsWith"
809                | "endsWith"
810                | "trim"
811                | "replace"
812                | "abs"
813                | "ceil"
814                | "floor"
815                | "round"
816                | "min"
817                | "max"
818                | "sqrt"
819                | "pow"
820                | "encodeURI"
821                | "decodeURI"
822        )
823    }
824}
825
826/// Function registry for managing SHACL functions
827pub struct FunctionRegistry {
828    /// Registered functions
829    functions: HashMap<String, ShaclFunction>,
830    /// Function executors
831    executors: Vec<Arc<dyn FunctionExecutor>>,
832}
833
834impl FunctionRegistry {
835    /// Create a new function registry
836    pub fn new() -> Self {
837        let mut registry = Self {
838            functions: HashMap::new(),
839            executors: Vec::new(),
840        };
841
842        // Register built-in executor
843        registry.add_executor(Arc::new(BuiltInFunctionExecutor::new()));
844
845        // Register built-in functions
846        registry.register_built_in_functions();
847
848        registry
849    }
850
851    /// Register built-in SHACL functions
852    fn register_built_in_functions(&mut self) {
853        // String concatenation
854        self.register_function(ShaclFunction::new(
855            "http://www.w3.org/ns/shacl#concat",
856            "concat",
857            vec![],
858            ReturnType::Single(ParameterType::String),
859        ))
860        .ok();
861
862        // String uppercase
863        self.register_function(ShaclFunction::new(
864            "http://www.w3.org/ns/shacl#upperCase",
865            "upperCase",
866            vec![FunctionParameter::required(
867                "input",
868                ParameterType::String,
869                0,
870            )],
871            ReturnType::Single(ParameterType::String),
872        ))
873        .ok();
874
875        // String lowercase
876        self.register_function(ShaclFunction::new(
877            "http://www.w3.org/ns/shacl#lowerCase",
878            "lowerCase",
879            vec![FunctionParameter::required(
880                "input",
881                ParameterType::String,
882                0,
883            )],
884            ReturnType::Single(ParameterType::String),
885        ))
886        .ok();
887
888        // Substring
889        self.register_function(ShaclFunction::new(
890            "http://www.w3.org/ns/shacl#substring",
891            "substring",
892            vec![
893                FunctionParameter::required("input", ParameterType::String, 0),
894                FunctionParameter::required("start", ParameterType::Integer, 1),
895                FunctionParameter::optional("length", ParameterType::Integer, 2, None),
896            ],
897            ReturnType::Single(ParameterType::String),
898        ))
899        .ok();
900
901        // String length
902        self.register_function(ShaclFunction::new(
903            "http://www.w3.org/ns/shacl#strLength",
904            "strLength",
905            vec![FunctionParameter::required(
906                "input",
907                ParameterType::String,
908                0,
909            )],
910            ReturnType::Single(ParameterType::Integer),
911        ))
912        .ok();
913
914        // Contains
915        self.register_function(ShaclFunction::new(
916            "http://www.w3.org/ns/shacl#contains",
917            "contains",
918            vec![
919                FunctionParameter::required("string", ParameterType::String, 0),
920                FunctionParameter::required("substring", ParameterType::String, 1),
921            ],
922            ReturnType::Single(ParameterType::Boolean),
923        ))
924        .ok();
925
926        // Starts with
927        self.register_function(ShaclFunction::new(
928            "http://www.w3.org/ns/shacl#startsWith",
929            "startsWith",
930            vec![
931                FunctionParameter::required("string", ParameterType::String, 0),
932                FunctionParameter::required("prefix", ParameterType::String, 1),
933            ],
934            ReturnType::Single(ParameterType::Boolean),
935        ))
936        .ok();
937
938        // Ends with
939        self.register_function(ShaclFunction::new(
940            "http://www.w3.org/ns/shacl#endsWith",
941            "endsWith",
942            vec![
943                FunctionParameter::required("string", ParameterType::String, 0),
944                FunctionParameter::required("suffix", ParameterType::String, 1),
945            ],
946            ReturnType::Single(ParameterType::Boolean),
947        ))
948        .ok();
949
950        // Mathematical functions
951
952        // Absolute value
953        self.register_function(ShaclFunction::new(
954            "http://www.w3.org/ns/shacl#abs",
955            "abs",
956            vec![FunctionParameter::required(
957                "value",
958                ParameterType::Decimal,
959                0,
960            )],
961            ReturnType::Single(ParameterType::Decimal),
962        ))
963        .ok();
964
965        // Ceiling
966        self.register_function(ShaclFunction::new(
967            "http://www.w3.org/ns/shacl#ceil",
968            "ceil",
969            vec![FunctionParameter::required(
970                "value",
971                ParameterType::Decimal,
972                0,
973            )],
974            ReturnType::Single(ParameterType::Integer),
975        ))
976        .ok();
977
978        // Floor
979        self.register_function(ShaclFunction::new(
980            "http://www.w3.org/ns/shacl#floor",
981            "floor",
982            vec![FunctionParameter::required(
983                "value",
984                ParameterType::Decimal,
985                0,
986            )],
987            ReturnType::Single(ParameterType::Integer),
988        ))
989        .ok();
990
991        // Round
992        self.register_function(ShaclFunction::new(
993            "http://www.w3.org/ns/shacl#round",
994            "round",
995            vec![FunctionParameter::required(
996                "value",
997                ParameterType::Decimal,
998                0,
999            )],
1000            ReturnType::Single(ParameterType::Integer),
1001        ))
1002        .ok();
1003
1004        // Additional string functions
1005
1006        // Trim whitespace
1007        self.register_function(ShaclFunction::new(
1008            "http://www.w3.org/ns/shacl#trim",
1009            "trim",
1010            vec![FunctionParameter::required(
1011                "input",
1012                ParameterType::String,
1013                0,
1014            )],
1015            ReturnType::Single(ParameterType::String),
1016        ))
1017        .ok();
1018
1019        // String replace
1020        self.register_function(ShaclFunction::new(
1021            "http://www.w3.org/ns/shacl#replace",
1022            "replace",
1023            vec![
1024                FunctionParameter::required("input", ParameterType::String, 0),
1025                FunctionParameter::required("pattern", ParameterType::String, 1),
1026                FunctionParameter::required("replacement", ParameterType::String, 2),
1027            ],
1028            ReturnType::Single(ParameterType::String),
1029        ))
1030        .ok();
1031
1032        // Additional mathematical functions
1033
1034        // Minimum value
1035        self.register_function(ShaclFunction::new(
1036            "http://www.w3.org/ns/shacl#min",
1037            "min",
1038            vec![
1039                FunctionParameter::required("value1", ParameterType::Decimal, 0),
1040                FunctionParameter::required("value2", ParameterType::Decimal, 1),
1041            ],
1042            ReturnType::Single(ParameterType::Decimal),
1043        ))
1044        .ok();
1045
1046        // Maximum value
1047        self.register_function(ShaclFunction::new(
1048            "http://www.w3.org/ns/shacl#max",
1049            "max",
1050            vec![
1051                FunctionParameter::required("value1", ParameterType::Decimal, 0),
1052                FunctionParameter::required("value2", ParameterType::Decimal, 1),
1053            ],
1054            ReturnType::Single(ParameterType::Decimal),
1055        ))
1056        .ok();
1057
1058        // Square root
1059        self.register_function(ShaclFunction::new(
1060            "http://www.w3.org/ns/shacl#sqrt",
1061            "sqrt",
1062            vec![FunctionParameter::required(
1063                "value",
1064                ParameterType::Decimal,
1065                0,
1066            )],
1067            ReturnType::Single(ParameterType::Decimal),
1068        ))
1069        .ok();
1070
1071        // Power
1072        self.register_function(ShaclFunction::new(
1073            "http://www.w3.org/ns/shacl#pow",
1074            "pow",
1075            vec![
1076                FunctionParameter::required("base", ParameterType::Decimal, 0),
1077                FunctionParameter::required("exponent", ParameterType::Decimal, 1),
1078            ],
1079            ReturnType::Single(ParameterType::Decimal),
1080        ))
1081        .ok();
1082
1083        // URI encoding functions
1084
1085        // URL encode
1086        self.register_function(ShaclFunction::new(
1087            "http://www.w3.org/ns/shacl#encodeURI",
1088            "encodeURI",
1089            vec![FunctionParameter::required(
1090                "input",
1091                ParameterType::String,
1092                0,
1093            )],
1094            ReturnType::Single(ParameterType::String),
1095        ))
1096        .ok();
1097
1098        // URL decode
1099        self.register_function(ShaclFunction::new(
1100            "http://www.w3.org/ns/shacl#decodeURI",
1101            "decodeURI",
1102            vec![FunctionParameter::required(
1103                "input",
1104                ParameterType::String,
1105                0,
1106            )],
1107            ReturnType::Single(ParameterType::String),
1108        ))
1109        .ok();
1110    }
1111
1112    /// Register a function
1113    pub fn register_function(&mut self, function: ShaclFunction) -> Result<()> {
1114        self.functions.insert(function.id.clone(), function);
1115        Ok(())
1116    }
1117
1118    /// Get a function by ID
1119    pub fn get_function(&self, id: &str) -> Option<&ShaclFunction> {
1120        self.functions.get(id)
1121    }
1122
1123    /// Add a function executor
1124    pub fn add_executor(&mut self, executor: Arc<dyn FunctionExecutor>) {
1125        self.executors.push(executor);
1126    }
1127
1128    /// Execute a function
1129    pub fn execute(
1130        &self,
1131        invocation: &FunctionInvocation,
1132        store: &dyn Store,
1133    ) -> Result<FunctionResult> {
1134        // Get function definition
1135        let function = self.get_function(&invocation.function_id).ok_or_else(|| {
1136            ShaclError::ValidationEngine(format!("Function not found: {}", invocation.function_id))
1137        })?;
1138
1139        // Find executor
1140        for executor in &self.executors {
1141            if executor.can_execute(function) {
1142                return executor.execute(function, invocation, store);
1143            }
1144        }
1145
1146        Err(ShaclError::ValidationEngine(format!(
1147            "No executor found for function: {}",
1148            invocation.function_id
1149        )))
1150    }
1151
1152    /// List all registered functions
1153    pub fn list_functions(&self) -> Vec<&ShaclFunction> {
1154        self.functions.values().collect()
1155    }
1156
1157    /// Get function count
1158    pub fn function_count(&self) -> usize {
1159        self.functions.len()
1160    }
1161}
1162
1163impl Default for FunctionRegistry {
1164    fn default() -> Self {
1165        Self::new()
1166    }
1167}
1168
1169#[cfg(test)]
1170mod tests {
1171    use super::*;
1172
1173    #[test]
1174    fn test_function_parameter_creation() {
1175        let param = FunctionParameter::required("test", ParameterType::String, 0);
1176        assert_eq!(param.name, "test");
1177        assert!(!param.optional);
1178        assert_eq!(param.order, 0);
1179    }
1180
1181    #[test]
1182    fn test_function_registry_creation() {
1183        let registry = FunctionRegistry::new();
1184        assert!(registry.function_count() > 0);
1185    }
1186
1187    #[test]
1188    fn test_built_in_functions_registered() {
1189        let registry = FunctionRegistry::new();
1190        assert!(registry
1191            .get_function("http://www.w3.org/ns/shacl#concat")
1192            .is_some());
1193        assert!(registry
1194            .get_function("http://www.w3.org/ns/shacl#upperCase")
1195            .is_some());
1196    }
1197
1198    #[test]
1199    fn test_function_validation() {
1200        let function = ShaclFunction::new(
1201            "test:func",
1202            "testFunc",
1203            vec![FunctionParameter::required(
1204                "arg1",
1205                ParameterType::String,
1206                0,
1207            )],
1208            ReturnType::Single(ParameterType::String),
1209        );
1210
1211        let mut args = HashMap::new();
1212        args.insert(
1213            "arg1".to_string(),
1214            Term::Literal(Literal::new_simple_literal("test")),
1215        );
1216
1217        assert!(function.validate_arguments(&args).is_ok());
1218    }
1219
1220    #[test]
1221    fn test_function_validation_missing_required() {
1222        let function = ShaclFunction::new(
1223            "test:func",
1224            "testFunc",
1225            vec![FunctionParameter::required(
1226                "arg1",
1227                ParameterType::String,
1228                0,
1229            )],
1230            ReturnType::Single(ParameterType::String),
1231        );
1232
1233        let args = HashMap::new();
1234        assert!(function.validate_arguments(&args).is_err());
1235    }
1236}