1use crate::algebra::{Term, TriplePattern};
9use crate::extensions::{CustomFunction, ExecutionContext, Value, ValueType};
10use anyhow::{bail, Result};
11
12fn value_to_term(value: &Value) -> Result<Term> {
14 match value {
15 Value::Iri(iri) => {
16 use oxirs_core::model::NamedNode;
17 let node = NamedNode::new(iri)?;
18 Ok(Term::Iri(node))
19 }
20 Value::Literal {
21 value,
22 language,
23 datatype,
24 } => {
25 use oxirs_core::model::NamedNode;
26 let dt = if let Some(dt_str) = datatype {
27 Some(NamedNode::new(dt_str)?)
28 } else {
29 None
30 };
31 Ok(Term::Literal(crate::algebra::Literal {
32 value: value.clone(),
33 language: language.clone(),
34 datatype: dt,
35 }))
36 }
37 Value::BlankNode(id) => Ok(Term::BlankNode(id.clone())),
38 _ => bail!("Cannot convert {} to RDF term", value.type_name()),
39 }
40}
41
42#[allow(dead_code)]
44fn term_to_value(term: &Term) -> Value {
45 match term {
46 Term::Iri(iri) => Value::Iri(iri.as_str().to_string()),
47 Term::Literal(lit) => Value::Literal {
48 value: lit.value.clone(),
49 language: lit.language.clone(),
50 datatype: lit.datatype.as_ref().map(|dt| dt.as_str().to_string()),
51 },
52 Term::BlankNode(id) => Value::BlankNode(id.clone()),
53 Term::Variable(_v) => {
54 Value::String(format!("{}", term))
57 }
58 Term::QuotedTriple(triple) => {
59 Value::String(format!(
62 "<<{} {} {}>>",
63 triple.subject, triple.predicate, triple.object
64 ))
65 }
66 Term::PropertyPath(path) => Value::String(format!("{}", path)),
67 }
68}
69
70#[derive(Debug, Clone)]
84pub struct TripleFunction;
85
86impl CustomFunction for TripleFunction {
87 fn name(&self) -> &str {
88 "http://www.w3.org/2001/sw/DataAccess/rq23#triple"
89 }
90
91 fn arity(&self) -> Option<usize> {
92 Some(3)
93 }
94
95 fn parameter_types(&self) -> Vec<ValueType> {
96 vec![
97 ValueType::Union(vec![
98 ValueType::Iri,
99 ValueType::BlankNode,
100 ValueType::Custom("QuotedTriple".to_string()),
101 ]),
102 ValueType::Iri,
103 ValueType::Union(vec![
104 ValueType::Iri,
105 ValueType::BlankNode,
106 ValueType::Literal,
107 ValueType::Custom("QuotedTriple".to_string()),
108 ]),
109 ]
110 }
111
112 fn return_type(&self) -> ValueType {
113 ValueType::Custom("QuotedTriple".to_string())
114 }
115
116 fn documentation(&self) -> &str {
117 "Constructs a quoted triple (RDF-star) from subject, predicate, and object"
118 }
119
120 fn clone_function(&self) -> Box<dyn CustomFunction> {
121 Box::new(self.clone())
122 }
123
124 fn execute(&self, args: &[Value], _context: &ExecutionContext) -> Result<Value> {
125 if args.len() != 3 {
126 bail!("TRIPLE() requires exactly 3 arguments (subject, predicate, object)");
127 }
128
129 let subject = value_to_term(&args[0])?;
130 let predicate = value_to_term(&args[1])?;
131 let object = value_to_term(&args[2])?;
132
133 match &subject {
135 Term::Literal(_) => bail!("TRIPLE subject cannot be a literal"),
136 Term::PropertyPath(_) => bail!("TRIPLE subject cannot be a property path"),
137 _ => {}
138 }
139
140 match &predicate {
141 Term::Literal(_) => bail!("TRIPLE predicate cannot be a literal"),
142 Term::BlankNode(_) => bail!("TRIPLE predicate cannot be a blank node"),
143 Term::QuotedTriple(_) => bail!("TRIPLE predicate cannot be a quoted triple"),
144 Term::PropertyPath(_) => bail!("TRIPLE predicate cannot be a property path"),
145 _ => {}
146 }
147
148 let triple = TriplePattern {
150 subject,
151 predicate,
152 object,
153 };
154
155 Ok(Value::String(format!(
157 "<<{} {} {}>>",
158 triple.subject, triple.predicate, triple.object
159 )))
160 }
161}
162
163#[derive(Debug, Clone)]
176pub struct SubjectFunction;
177
178impl CustomFunction for SubjectFunction {
179 fn name(&self) -> &str {
180 "http://www.w3.org/2001/sw/DataAccess/rq23#subject"
181 }
182
183 fn arity(&self) -> Option<usize> {
184 Some(1)
185 }
186
187 fn parameter_types(&self) -> Vec<ValueType> {
188 vec![ValueType::Custom("QuotedTriple".to_string())]
189 }
190
191 fn return_type(&self) -> ValueType {
192 ValueType::Union(vec![
193 ValueType::Iri,
194 ValueType::BlankNode,
195 ValueType::Custom("QuotedTriple".to_string()),
196 ])
197 }
198
199 fn documentation(&self) -> &str {
200 "Extracts the subject from a quoted triple (RDF-star)"
201 }
202
203 fn clone_function(&self) -> Box<dyn CustomFunction> {
204 Box::new(self.clone())
205 }
206
207 fn execute(&self, args: &[Value], _context: &ExecutionContext) -> Result<Value> {
208 if args.len() != 1 {
209 bail!("SUBJECT() requires exactly 1 argument");
210 }
211
212 match &args[0] {
215 Value::String(s) if s.starts_with("<<") && s.ends_with(">>") => {
216 let inner = &s[2..s.len() - 2];
219 let parts: Vec<&str> = inner.split_whitespace().collect();
220 if parts.len() >= 3 {
221 Ok(Value::Iri(parts[0].to_string()))
222 } else {
223 bail!("Invalid quoted triple format");
224 }
225 }
226 _ => bail!("SUBJECT() requires a quoted triple argument"),
227 }
228 }
229}
230
231#[derive(Debug, Clone)]
244pub struct PredicateFunction;
245
246impl CustomFunction for PredicateFunction {
247 fn name(&self) -> &str {
248 "http://www.w3.org/2001/sw/DataAccess/rq23#predicate"
249 }
250
251 fn arity(&self) -> Option<usize> {
252 Some(1)
253 }
254
255 fn parameter_types(&self) -> Vec<ValueType> {
256 vec![ValueType::Custom("QuotedTriple".to_string())]
257 }
258
259 fn return_type(&self) -> ValueType {
260 ValueType::Iri
261 }
262
263 fn documentation(&self) -> &str {
264 "Extracts the predicate from a quoted triple (RDF-star)"
265 }
266
267 fn clone_function(&self) -> Box<dyn CustomFunction> {
268 Box::new(self.clone())
269 }
270
271 fn execute(&self, args: &[Value], _context: &ExecutionContext) -> Result<Value> {
272 if args.len() != 1 {
273 bail!("PREDICATE() requires exactly 1 argument");
274 }
275
276 match &args[0] {
277 Value::String(s) if s.starts_with("<<") && s.ends_with(">>") => {
278 let inner = &s[2..s.len() - 2];
279 let parts: Vec<&str> = inner.split_whitespace().collect();
280 if parts.len() >= 3 {
281 Ok(Value::Iri(parts[1].to_string()))
282 } else {
283 bail!("Invalid quoted triple format");
284 }
285 }
286 _ => bail!("PREDICATE() requires a quoted triple argument"),
287 }
288 }
289}
290
291#[derive(Debug, Clone)]
304pub struct ObjectFunction;
305
306impl CustomFunction for ObjectFunction {
307 fn name(&self) -> &str {
308 "http://www.w3.org/2001/sw/DataAccess/rq23#object"
309 }
310
311 fn arity(&self) -> Option<usize> {
312 Some(1)
313 }
314
315 fn parameter_types(&self) -> Vec<ValueType> {
316 vec![ValueType::Custom("QuotedTriple".to_string())]
317 }
318
319 fn return_type(&self) -> ValueType {
320 ValueType::Union(vec![
321 ValueType::Iri,
322 ValueType::BlankNode,
323 ValueType::Literal,
324 ValueType::Custom("QuotedTriple".to_string()),
325 ])
326 }
327
328 fn documentation(&self) -> &str {
329 "Extracts the object from a quoted triple (RDF-star)"
330 }
331
332 fn clone_function(&self) -> Box<dyn CustomFunction> {
333 Box::new(self.clone())
334 }
335
336 fn execute(&self, args: &[Value], _context: &ExecutionContext) -> Result<Value> {
337 if args.len() != 1 {
338 bail!("OBJECT() requires exactly 1 argument");
339 }
340
341 match &args[0] {
342 Value::String(s) if s.starts_with("<<") && s.ends_with(">>") => {
343 let inner = &s[2..s.len() - 2];
344 let parts: Vec<&str> = inner.split_whitespace().collect();
345 if parts.len() >= 3 {
346 Ok(Value::Iri(parts[2..].join(" ")))
348 } else {
349 bail!("Invalid quoted triple format");
350 }
351 }
352 _ => bail!("OBJECT() requires a quoted triple argument"),
353 }
354 }
355}
356
357#[derive(Debug, Clone)]
372pub struct IsTripleFunction;
373
374impl CustomFunction for IsTripleFunction {
375 fn name(&self) -> &str {
376 "http://www.w3.org/2001/sw/DataAccess/rq23#isTRIPLE"
377 }
378
379 fn arity(&self) -> Option<usize> {
380 Some(1)
381 }
382
383 fn parameter_types(&self) -> Vec<ValueType> {
384 vec![ValueType::Union(vec![
385 ValueType::Iri,
386 ValueType::BlankNode,
387 ValueType::Literal,
388 ValueType::Custom("QuotedTriple".to_string()),
389 ])]
390 }
391
392 fn return_type(&self) -> ValueType {
393 ValueType::Boolean
394 }
395
396 fn documentation(&self) -> &str {
397 "Returns true if the argument is a quoted triple (RDF-star), false otherwise"
398 }
399
400 fn clone_function(&self) -> Box<dyn CustomFunction> {
401 Box::new(self.clone())
402 }
403
404 fn execute(&self, args: &[Value], _context: &ExecutionContext) -> Result<Value> {
405 if args.len() != 1 {
406 bail!("isTRIPLE() requires exactly 1 argument");
407 }
408
409 let is_triple = match &args[0] {
410 Value::String(s) => s.starts_with("<<") && s.ends_with(">>"),
411 _ => false,
412 };
413
414 Ok(Value::Boolean(is_triple))
415 }
416}
417
418#[cfg(test)]
419mod tests {
420 use super::*;
421 use std::collections::HashMap;
422
423 fn create_test_context() -> ExecutionContext {
424 ExecutionContext {
425 variables: HashMap::new(),
426 namespaces: HashMap::new(),
427 base_iri: None,
428 dataset_context: None,
429 query_time: chrono::Utc::now(),
430 optimization_level: crate::extensions::OptimizationLevel::None,
431 memory_limit: None,
432 time_limit: None,
433 }
434 }
435
436 #[test]
437 fn test_triple_function_construction() {
438 let func = TripleFunction;
439 let ctx = create_test_context();
440
441 let subject = Value::Iri("http://example.org/subject".to_string());
442 let predicate = Value::Iri("http://example.org/predicate".to_string());
443 let object = Value::Iri("http://example.org/object".to_string());
444
445 let result = func.execute(&[subject, predicate, object], &ctx);
446 assert!(result.is_ok());
447
448 if let Ok(Value::String(s)) = result {
449 assert!(s.starts_with("<<"));
450 assert!(s.ends_with(">>"));
451 } else {
452 panic!("Expected string result");
453 }
454 }
455
456 #[test]
457 fn test_triple_function_invalid_subject() {
458 let func = TripleFunction;
459 let ctx = create_test_context();
460
461 let subject = Value::Literal {
463 value: "literal".to_string(),
464 language: None,
465 datatype: None,
466 };
467 let predicate = Value::Iri("http://example.org/predicate".to_string());
468 let object = Value::Iri("http://example.org/object".to_string());
469
470 let result = func.execute(&[subject, predicate, object], &ctx);
471 assert!(result.is_err());
472 }
473
474 #[test]
475 fn test_subject_function() {
476 let func = SubjectFunction;
477 let ctx = create_test_context();
478
479 let triple = Value::String(
480 "<<http://example.org/s http://example.org/p http://example.org/o>>".to_string(),
481 );
482
483 let result = func.execute(&[triple], &ctx);
484 assert!(result.is_ok());
485
486 if let Ok(Value::Iri(iri)) = result {
487 assert_eq!(iri, "http://example.org/s");
488 } else {
489 panic!("Expected IRI result");
490 }
491 }
492
493 #[test]
494 fn test_predicate_function() {
495 let func = PredicateFunction;
496 let ctx = create_test_context();
497
498 let triple = Value::String(
499 "<<http://example.org/s http://example.org/p http://example.org/o>>".to_string(),
500 );
501
502 let result = func.execute(&[triple], &ctx);
503 assert!(result.is_ok());
504
505 if let Ok(Value::Iri(iri)) = result {
506 assert_eq!(iri, "http://example.org/p");
507 } else {
508 panic!("Expected IRI result");
509 }
510 }
511
512 #[test]
513 fn test_object_function() {
514 let func = ObjectFunction;
515 let ctx = create_test_context();
516
517 let triple = Value::String(
518 "<<http://example.org/s http://example.org/p http://example.org/o>>".to_string(),
519 );
520
521 let result = func.execute(&[triple], &ctx);
522 assert!(result.is_ok());
523
524 if let Ok(Value::Iri(iri)) = result {
525 assert_eq!(iri, "http://example.org/o");
526 } else {
527 panic!("Expected IRI result");
528 }
529 }
530
531 #[test]
532 fn test_is_triple_function() {
533 let func = IsTripleFunction;
534 let ctx = create_test_context();
535
536 let triple = Value::String(
538 "<<http://example.org/s http://example.org/p http://example.org/o>>".to_string(),
539 );
540 let result = func.execute(&[triple], &ctx).unwrap();
541 assert_eq!(result, Value::Boolean(true));
542
543 let non_triple = Value::Iri("http://example.org/resource".to_string());
545 let result = func.execute(&[non_triple], &ctx).unwrap();
546 assert_eq!(result, Value::Boolean(false));
547 }
548
549 #[test]
550 fn test_triple_function_arity() {
551 let func = TripleFunction;
552 assert_eq!(func.arity(), Some(3));
553 assert_eq!(
554 func.name(),
555 "http://www.w3.org/2001/sw/DataAccess/rq23#triple"
556 );
557 }
558
559 #[test]
560 fn test_accessor_function_arities() {
561 assert_eq!(SubjectFunction.arity(), Some(1));
562 assert_eq!(PredicateFunction.arity(), Some(1));
563 assert_eq!(ObjectFunction.arity(), Some(1));
564 assert_eq!(IsTripleFunction.arity(), Some(1));
565 }
566}