1use super::sparql_algebra::{GraphPattern, TriplePattern};
8use crate::model::{NamedNode, Variable};
9use std::fmt;
10
11#[derive(Eq, PartialEq, Debug, Clone, Hash)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub enum Query {
15 Select {
17 dataset: Option<QueryDataset>,
19 pattern: GraphPattern,
21 base_iri: Option<String>,
23 },
24 Construct {
26 template: Vec<TriplePattern>,
28 dataset: Option<QueryDataset>,
30 pattern: GraphPattern,
32 base_iri: Option<String>,
34 },
35 Describe {
37 dataset: Option<QueryDataset>,
39 pattern: GraphPattern,
41 base_iri: Option<String>,
43 },
44 Ask {
46 dataset: Option<QueryDataset>,
48 pattern: GraphPattern,
50 base_iri: Option<String>,
52 },
53}
54
55impl Query {
56 pub fn select(pattern: GraphPattern) -> Self {
58 Self::Select {
59 dataset: None,
60 pattern,
61 base_iri: None,
62 }
63 }
64
65 pub fn construct(template: Vec<TriplePattern>, pattern: GraphPattern) -> Self {
67 Self::Construct {
68 template,
69 dataset: None,
70 pattern,
71 base_iri: None,
72 }
73 }
74
75 pub fn ask(pattern: GraphPattern) -> Self {
77 Self::Ask {
78 dataset: None,
79 pattern,
80 base_iri: None,
81 }
82 }
83
84 pub fn describe(pattern: GraphPattern) -> Self {
86 Self::Describe {
87 dataset: None,
88 pattern,
89 base_iri: None,
90 }
91 }
92
93 pub fn dataset(&self) -> Option<&QueryDataset> {
95 match self {
96 Query::Select { dataset, .. }
97 | Query::Construct { dataset, .. }
98 | Query::Describe { dataset, .. }
99 | Query::Ask { dataset, .. } => dataset.as_ref(),
100 }
101 }
102
103 pub fn dataset_mut(&mut self) -> Option<&mut QueryDataset> {
105 match self {
106 Query::Select { dataset, .. }
107 | Query::Construct { dataset, .. }
108 | Query::Describe { dataset, .. }
109 | Query::Ask { dataset, .. } => dataset.as_mut(),
110 }
111 }
112
113 pub fn base_iri(&self) -> Option<&str> {
115 match self {
116 Query::Select { base_iri, .. }
117 | Query::Construct { base_iri, .. }
118 | Query::Describe { base_iri, .. }
119 | Query::Ask { base_iri, .. } => base_iri.as_deref(),
120 }
121 }
122
123 pub fn pattern(&self) -> &GraphPattern {
125 match self {
126 Query::Select { pattern, .. }
127 | Query::Construct { pattern, .. }
128 | Query::Describe { pattern, .. }
129 | Query::Ask { pattern, .. } => pattern,
130 }
131 }
132
133 pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Self {
135 let base_iri = Some(base_iri.into());
136 match &mut self {
137 Query::Select { base_iri: iri, .. }
138 | Query::Construct { base_iri: iri, .. }
139 | Query::Describe { base_iri: iri, .. }
140 | Query::Ask { base_iri: iri, .. } => *iri = base_iri,
141 }
142 self
143 }
144
145 pub fn with_dataset(mut self, dataset: QueryDataset) -> Self {
147 let dataset = Some(dataset);
148 match &mut self {
149 Query::Select { dataset: ds, .. }
150 | Query::Construct { dataset: ds, .. }
151 | Query::Describe { dataset: ds, .. }
152 | Query::Ask { dataset: ds, .. } => *ds = dataset,
153 }
154 self
155 }
156
157 pub fn to_sse(&self) -> String {
159 let mut buffer = String::new();
160 self.fmt_sse(&mut buffer)
161 .expect("writing to String should not fail");
162 buffer
163 }
164
165 pub fn fmt_sse(&self, f: &mut impl fmt::Write) -> fmt::Result {
167 match self {
168 Self::Select {
169 dataset,
170 pattern,
171 base_iri,
172 } => {
173 if let Some(base_iri) = base_iri {
174 write!(f, "(base <{base_iri}> ")?;
175 }
176 if let Some(dataset) = dataset {
177 f.write_str("(dataset ")?;
178 dataset.fmt_sse(f)?;
179 f.write_str(" ")?;
180 }
181 pattern.fmt_sse(f)?;
182 if dataset.is_some() {
183 f.write_str(")")?;
184 }
185 if base_iri.is_some() {
186 f.write_str(")")?;
187 }
188 Ok(())
189 }
190 Self::Construct {
191 template,
192 dataset,
193 pattern,
194 base_iri,
195 } => {
196 if let Some(base_iri) = base_iri {
197 write!(f, "(base <{base_iri}> ")?;
198 }
199 f.write_str("(construct (")?;
200 for (i, triple) in template.iter().enumerate() {
201 if i > 0 {
202 f.write_str(" ")?;
203 }
204 triple.fmt_sse(f)?;
205 }
206 f.write_str(") ")?;
207 if let Some(dataset) = dataset {
208 f.write_str("(dataset ")?;
209 dataset.fmt_sse(f)?;
210 f.write_str(" ")?;
211 }
212 pattern.fmt_sse(f)?;
213 if dataset.is_some() {
214 f.write_str(")")?;
215 }
216 f.write_str(")")?;
217 if base_iri.is_some() {
218 f.write_str(")")?;
219 }
220 Ok(())
221 }
222 Self::Describe {
223 dataset,
224 pattern,
225 base_iri,
226 } => {
227 if let Some(base_iri) = base_iri {
228 write!(f, "(base <{base_iri}> ")?;
229 }
230 f.write_str("(describe ")?;
231 if let Some(dataset) = dataset {
232 f.write_str("(dataset ")?;
233 dataset.fmt_sse(f)?;
234 f.write_str(" ")?;
235 }
236 pattern.fmt_sse(f)?;
237 if dataset.is_some() {
238 f.write_str(")")?;
239 }
240 f.write_str(")")?;
241 if base_iri.is_some() {
242 f.write_str(")")?;
243 }
244 Ok(())
245 }
246 Self::Ask {
247 dataset,
248 pattern,
249 base_iri,
250 } => {
251 if let Some(base_iri) = base_iri {
252 write!(f, "(base <{base_iri}> ")?;
253 }
254 f.write_str("(ask ")?;
255 if let Some(dataset) = dataset {
256 f.write_str("(dataset ")?;
257 dataset.fmt_sse(f)?;
258 f.write_str(" ")?;
259 }
260 pattern.fmt_sse(f)?;
261 if dataset.is_some() {
262 f.write_str(")")?;
263 }
264 f.write_str(")")?;
265 if base_iri.is_some() {
266 f.write_str(")")?;
267 }
268 Ok(())
269 }
270 }
271 }
272}
273
274impl fmt::Display for Query {
275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276 match self {
277 Self::Select {
278 dataset,
279 pattern,
280 base_iri,
281 } => {
282 if let Some(base_iri) = base_iri {
283 writeln!(f, "BASE <{base_iri}>")?;
284 }
285 write!(
286 f,
287 "{}",
288 SparqlGraphRootPattern::new(pattern, dataset.as_ref())
289 )
290 }
291 Self::Construct {
292 template,
293 dataset,
294 pattern,
295 base_iri,
296 } => {
297 if let Some(base_iri) = base_iri {
298 writeln!(f, "BASE <{base_iri}>")?;
299 }
300 f.write_str("CONSTRUCT { ")?;
301 for triple in template {
302 write!(f, "{triple} . ")?;
303 }
304 f.write_str("}")?;
305 if let Some(dataset) = dataset {
306 dataset.fmt(f)?;
307 }
308 write!(
309 f,
310 " WHERE {{ {} }}",
311 SparqlGraphRootPattern::new(pattern, None)
312 )
313 }
314 Self::Describe {
315 dataset,
316 pattern,
317 base_iri,
318 } => {
319 if let Some(base_iri) = base_iri {
320 writeln!(f, "BASE <{base_iri}>")?;
321 }
322 f.write_str("DESCRIBE *")?;
323 if let Some(dataset) = dataset {
324 dataset.fmt(f)?;
325 }
326 write!(
327 f,
328 " WHERE {{ {} }}",
329 SparqlGraphRootPattern::new(pattern, None)
330 )
331 }
332 Self::Ask {
333 dataset,
334 pattern,
335 base_iri,
336 } => {
337 if let Some(base_iri) = base_iri {
338 writeln!(f, "BASE <{base_iri}>")?;
339 }
340 f.write_str("ASK")?;
341 if let Some(dataset) = dataset {
342 dataset.fmt(f)?;
343 }
344 write!(
345 f,
346 " WHERE {{ {} }}",
347 SparqlGraphRootPattern::new(pattern, None)
348 )
349 }
350 }
351 }
352}
353
354#[derive(Eq, PartialEq, Debug, Clone, Hash)]
356#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
357pub struct QueryDataset {
358 pub default: Vec<NamedNode>,
360 pub named: Option<Vec<NamedNode>>,
362}
363
364impl QueryDataset {
365 pub fn new() -> Self {
367 Self {
368 default: Vec::new(),
369 named: None,
370 }
371 }
372
373 pub fn with_default_graphs(graphs: Vec<NamedNode>) -> Self {
375 Self {
376 default: graphs,
377 named: None,
378 }
379 }
380
381 pub fn with_named_graphs(graphs: Vec<NamedNode>) -> Self {
383 Self {
384 default: Vec::new(),
385 named: Some(graphs),
386 }
387 }
388
389 pub fn add_default_graph(&mut self, graph: NamedNode) {
391 self.default.push(graph);
392 }
393
394 pub fn add_named_graph(&mut self, graph: NamedNode) {
396 if let Some(ref mut named) = self.named {
397 named.push(graph);
398 } else {
399 self.named = Some(vec![graph]);
400 }
401 }
402
403 pub fn is_empty(&self) -> bool {
405 self.default.is_empty() && self.named.as_ref().map_or(true, |v| v.is_empty())
406 }
407
408 pub fn fmt_sse(&self, f: &mut impl fmt::Write) -> fmt::Result {
410 f.write_str("(")?;
411 for (i, graph_name) in self.default.iter().enumerate() {
412 if i > 0 {
413 f.write_str(" ")?;
414 }
415 write!(f, "{graph_name}")?;
416 }
417 if let Some(named) = &self.named {
418 for (i, graph_name) in named.iter().enumerate() {
419 if !self.default.is_empty() || i > 0 {
420 f.write_str(" ")?;
421 }
422 write!(f, "(named {graph_name})")?;
423 }
424 }
425 f.write_str(")")
426 }
427}
428
429impl Default for QueryDataset {
430 fn default() -> Self {
431 Self::new()
432 }
433}
434
435impl fmt::Display for QueryDataset {
436 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437 for g in &self.default {
438 write!(f, " FROM {g}")?;
439 }
440 if let Some(named) = &self.named {
441 for g in named {
442 write!(f, " FROM NAMED {g}")?;
443 }
444 }
445 Ok(())
446 }
447}
448
449pub struct SparqlGraphRootPattern<'a> {
451 pattern: &'a GraphPattern,
452 dataset: Option<&'a QueryDataset>,
453}
454
455impl<'a> SparqlGraphRootPattern<'a> {
456 pub fn new(pattern: &'a GraphPattern, dataset: Option<&'a QueryDataset>) -> Self {
457 Self { pattern, dataset }
458 }
459}
460
461impl fmt::Display for SparqlGraphRootPattern<'_> {
462 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
463 let mut distinct = false;
464 let mut reduced = false;
465 let mut order = None;
466 let mut start = 0;
467 let mut length = None;
468 let mut project: &[Variable] = &[];
469
470 let mut child = self.pattern;
471 loop {
472 match child {
473 GraphPattern::OrderBy { inner, expression } => {
474 order = Some(expression);
475 child = inner;
476 }
477 GraphPattern::Project { inner, variables } => {
478 project = variables;
479 child = inner;
480 }
481 GraphPattern::Distinct { inner } => {
482 distinct = true;
483 child = inner;
484 }
485 GraphPattern::Reduced { inner } => {
486 reduced = true;
487 child = inner;
488 }
489 GraphPattern::Slice {
490 inner,
491 start: s,
492 length: l,
493 } => {
494 start = *s;
495 length = *l;
496 child = inner;
497 }
498 _ => break,
499 }
500 }
501
502 if project.is_empty() {
503 f.write_str("SELECT ")?;
504 } else {
505 f.write_str("SELECT ")?;
506 if distinct {
507 f.write_str("DISTINCT ")?;
508 } else if reduced {
509 f.write_str("REDUCED ")?;
510 }
511 for (i, var) in project.iter().enumerate() {
512 if i > 0 {
513 f.write_str(" ")?;
514 }
515 write!(f, "{var}")?;
516 }
517 }
518
519 if let Some(dataset) = self.dataset {
520 dataset.fmt(f)?;
521 }
522
523 write!(f, " WHERE {{ {} }}", SparqlInnerGraphPattern::new(child))?;
524
525 if let Some(order) = order {
526 f.write_str(" ORDER BY")?;
527 for expr in order {
528 write!(f, " {expr}")?;
529 }
530 }
531
532 if start > 0 {
533 write!(f, " OFFSET {start}")?;
534 }
535
536 if let Some(length) = length {
537 write!(f, " LIMIT {length}")?;
538 }
539
540 Ok(())
541 }
542}
543
544struct SparqlInnerGraphPattern<'a> {
546 pattern: &'a GraphPattern,
547}
548
549impl<'a> SparqlInnerGraphPattern<'a> {
550 fn new(pattern: &'a GraphPattern) -> Self {
551 Self { pattern }
552 }
553}
554
555impl fmt::Display for SparqlInnerGraphPattern<'_> {
556 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
557 match self.pattern {
558 GraphPattern::Bgp { patterns } => {
559 for (i, pattern) in patterns.iter().enumerate() {
560 if i > 0 {
561 f.write_str(" . ")?;
562 }
563 write!(f, "{pattern}")?;
564 }
565 Ok(())
566 }
567 GraphPattern::Path {
568 subject,
569 path,
570 object,
571 } => {
572 write!(f, "{subject} {path} {object}")
573 }
574 GraphPattern::Join { left, right } => {
575 write!(
576 f,
577 "{} . {}",
578 SparqlInnerGraphPattern::new(left),
579 SparqlInnerGraphPattern::new(right)
580 )
581 }
582 GraphPattern::LeftJoin {
583 left,
584 right,
585 expression,
586 } => {
587 write!(
588 f,
589 "{} OPTIONAL {{ {}",
590 SparqlInnerGraphPattern::new(left),
591 SparqlInnerGraphPattern::new(right)
592 )?;
593 if let Some(expr) = expression {
594 write!(f, " FILTER ({expr})")?;
595 }
596 f.write_str(" }")
597 }
598 GraphPattern::Filter { expr, inner } => {
599 write!(
600 f,
601 "{} FILTER ({})",
602 SparqlInnerGraphPattern::new(inner),
603 expr
604 )
605 }
606 GraphPattern::Union { left, right } => {
607 write!(
608 f,
609 "{{ {} }} UNION {{ {} }}",
610 SparqlInnerGraphPattern::new(left),
611 SparqlInnerGraphPattern::new(right)
612 )
613 }
614 GraphPattern::Graph { name, inner } => {
615 write!(
616 f,
617 "GRAPH {} {{ {} }}",
618 name,
619 SparqlInnerGraphPattern::new(inner)
620 )
621 }
622 GraphPattern::Extend {
623 inner,
624 variable,
625 expression,
626 } => {
627 write!(
628 f,
629 "{} BIND ({} AS {})",
630 SparqlInnerGraphPattern::new(inner),
631 expression,
632 variable
633 )
634 }
635 GraphPattern::Minus { left, right } => {
636 write!(
637 f,
638 "{} MINUS {{ {} }}",
639 SparqlInnerGraphPattern::new(left),
640 SparqlInnerGraphPattern::new(right)
641 )
642 }
643 GraphPattern::Values {
644 variables,
645 bindings,
646 } => {
647 f.write_str("VALUES ")?;
648 if variables.len() == 1 {
649 write!(f, "{}", variables[0])?;
650 } else {
651 f.write_str("(")?;
652 for (i, var) in variables.iter().enumerate() {
653 if i > 0 {
654 f.write_str(" ")?;
655 }
656 write!(f, "{var}")?;
657 }
658 f.write_str(")")?;
659 }
660 f.write_str(" { ")?;
661 for (i, binding) in bindings.iter().enumerate() {
662 if i > 0 {
663 f.write_str(" ")?;
664 }
665 if variables.len() == 1 {
666 if let Some(term) = &binding[0] {
667 write!(f, "{term}")?;
668 } else {
669 f.write_str("UNDEF")?;
670 }
671 } else {
672 f.write_str("(")?;
673 for (j, value) in binding.iter().enumerate() {
674 if j > 0 {
675 f.write_str(" ")?;
676 }
677 if let Some(term) = value {
678 write!(f, "{term}")?;
679 } else {
680 f.write_str("UNDEF")?;
681 }
682 }
683 f.write_str(")")?;
684 }
685 }
686 f.write_str(" }")
687 }
688 GraphPattern::Service {
689 name,
690 inner,
691 silent,
692 } => {
693 if *silent {
694 write!(
695 f,
696 "SERVICE SILENT {} {{ {} }}",
697 name,
698 SparqlInnerGraphPattern::new(inner)
699 )
700 } else {
701 write!(
702 f,
703 "SERVICE {} {{ {} }}",
704 name,
705 SparqlInnerGraphPattern::new(inner)
706 )
707 }
708 }
709 GraphPattern::Group {
710 inner,
711 variables: _,
712 aggregates: _,
713 } => {
714 write!(f, "{}", SparqlInnerGraphPattern::new(inner))
717 }
718 GraphPattern::Project { inner, .. }
720 | GraphPattern::Distinct { inner }
721 | GraphPattern::Reduced { inner }
722 | GraphPattern::Slice { inner, .. }
723 | GraphPattern::OrderBy { inner, .. } => {
724 write!(f, "{}", SparqlInnerGraphPattern::new(inner))
725 }
726 }
727 }
728}
729
730#[cfg(test)]
731mod tests {
732 use super::*;
733 use crate::model::{NamedNode, Variable};
734 use crate::query::sparql_algebra::TermPattern;
735
736 #[test]
737 fn test_query_creation() {
738 let var = Variable::new("s").expect("valid variable name");
739 let pattern = GraphPattern::Bgp {
740 patterns: vec![TriplePattern::new(
741 TermPattern::Variable(var.clone()),
742 TermPattern::Variable(Variable::new("p").expect("valid variable name")),
743 TermPattern::Variable(Variable::new("o").expect("valid variable name")),
744 )],
745 };
746
747 let query = Query::select(pattern);
748 assert!(matches!(query, Query::Select { .. }));
749
750 let sse = query.to_sse();
751 assert!(sse.contains("bgp"));
752 }
753
754 #[test]
755 fn test_dataset() {
756 let mut dataset = QueryDataset::new();
757 assert!(dataset.is_empty());
758
759 let graph = NamedNode::new("http://example.org/graph").expect("valid IRI");
760 dataset.add_default_graph(graph.clone());
761 dataset.add_named_graph(graph);
762
763 assert!(!dataset.is_empty());
764 assert_eq!(dataset.default.len(), 1);
765 assert_eq!(
766 dataset
767 .named
768 .as_ref()
769 .expect("operation should succeed")
770 .len(),
771 1
772 );
773 }
774
775 #[test]
776 fn test_query_display() {
777 let pattern = GraphPattern::Bgp {
778 patterns: vec![TriplePattern::new(
779 TermPattern::Variable(Variable::new("s").expect("valid variable name")),
780 TermPattern::Variable(Variable::new("p").expect("valid variable name")),
781 TermPattern::Variable(Variable::new("o").expect("valid variable name")),
782 )],
783 };
784
785 let query = Query::select(pattern);
786 let query_str = query.to_string();
787
788 assert!(query_str.contains("SELECT"));
789 assert!(query_str.contains("WHERE"));
790 assert!(query_str.contains("?s"));
791 }
792}