spargebra/
query.rs

1use crate::algebra::*;
2use crate::parser::{parse_query, SparqlSyntaxError};
3use crate::term::*;
4use oxiri::Iri;
5use std::fmt;
6use std::str::FromStr;
7
8/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/).
9///
10/// ```
11/// use spargebra::Query;
12///
13/// let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
14/// let query = Query::parse(query_str, None)?;
15/// assert_eq!(query.to_string(), query_str);
16/// assert_eq!(
17///     query.to_sse(),
18///     "(project (?s ?p ?o) (bgp (triple ?s ?p ?o)))"
19/// );
20/// # Ok::<_, spargebra::SparqlSyntaxError>(())
21/// ```
22#[derive(Eq, PartialEq, Debug, Clone, Hash)]
23pub enum Query {
24    /// [SELECT](https://www.w3.org/TR/sparql11-query/#select).
25    Select {
26        /// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset).
27        dataset: Option<QueryDataset>,
28        /// The query selection graph pattern.
29        pattern: GraphPattern,
30        /// The query base IRI.
31        base_iri: Option<Iri<String>>,
32    },
33    /// [CONSTRUCT](https://www.w3.org/TR/sparql11-query/#construct).
34    Construct {
35        /// The query construction template.
36        template: Vec<TriplePattern>,
37        /// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset).
38        dataset: Option<QueryDataset>,
39        /// The query selection graph pattern.
40        pattern: GraphPattern,
41        /// The query base IRI.
42        base_iri: Option<Iri<String>>,
43    },
44    /// [DESCRIBE](https://www.w3.org/TR/sparql11-query/#describe).
45    Describe {
46        /// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset).
47        dataset: Option<QueryDataset>,
48        /// The query selection graph pattern.
49        pattern: GraphPattern,
50        /// The query base IRI.
51        base_iri: Option<Iri<String>>,
52    },
53    /// [ASK](https://www.w3.org/TR/sparql11-query/#ask).
54    Ask {
55        /// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset).
56        dataset: Option<QueryDataset>,
57        /// The query selection graph pattern.
58        pattern: GraphPattern,
59        /// The query base IRI.
60        base_iri: Option<Iri<String>>,
61    },
62}
63
64impl Query {
65    /// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query.
66    pub fn parse(query: &str, base_iri: Option<&str>) -> Result<Self, SparqlSyntaxError> {
67        parse_query(query, base_iri)
68    }
69
70    #[inline]
71    pub fn dataset(&self) -> Option<&QueryDataset> {
72        match self {
73            Query::Select { dataset, .. }
74            | Query::Construct { dataset, .. }
75            | Query::Describe { dataset, .. }
76            | Query::Ask { dataset, .. } => dataset.as_ref(),
77        }
78    }
79
80    #[inline]
81    pub fn dataset_mut(&mut self) -> Option<&mut QueryDataset> {
82        match self {
83            Query::Select { dataset, .. }
84            | Query::Construct { dataset, .. }
85            | Query::Describe { dataset, .. }
86            | Query::Ask { dataset, .. } => dataset.as_mut(),
87        }
88    }
89
90    #[inline]
91    pub fn base_iri(&self) -> Option<&Iri<String>> {
92        match self {
93            Query::Select { base_iri, .. }
94            | Query::Construct { base_iri, .. }
95            | Query::Describe { base_iri, .. }
96            | Query::Ask { base_iri, .. } => base_iri.as_ref(),
97        }
98    }
99
100    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
101    pub fn to_sse(&self) -> String {
102        let mut buffer = String::new();
103        self.fmt_sse(&mut buffer).unwrap();
104        buffer
105    }
106
107    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
108    fn fmt_sse(&self, f: &mut impl fmt::Write) -> fmt::Result {
109        match self {
110            Self::Select {
111                dataset,
112                pattern,
113                base_iri,
114            } => {
115                if let Some(base_iri) = base_iri {
116                    write!(f, "(base <{base_iri}> ")?;
117                }
118                if let Some(dataset) = dataset {
119                    f.write_str("(dataset ")?;
120                    dataset.fmt_sse(f)?;
121                    f.write_str(" ")?;
122                }
123                pattern.fmt_sse(f)?;
124                if dataset.is_some() {
125                    f.write_str(")")?;
126                }
127                if base_iri.is_some() {
128                    f.write_str(")")?;
129                }
130                Ok(())
131            }
132            Self::Construct {
133                template,
134                dataset,
135                pattern,
136                base_iri,
137            } => {
138                if let Some(base_iri) = base_iri {
139                    write!(f, "(base <{base_iri}> ")?;
140                }
141                f.write_str("(construct (")?;
142                for (i, t) in template.iter().enumerate() {
143                    if i > 0 {
144                        f.write_str(" ")?;
145                    }
146                    t.fmt_sse(f)?;
147                }
148                f.write_str(") ")?;
149                if let Some(dataset) = dataset {
150                    f.write_str("(dataset ")?;
151                    dataset.fmt_sse(f)?;
152                    f.write_str(" ")?;
153                }
154                pattern.fmt_sse(f)?;
155                if dataset.is_some() {
156                    f.write_str(")")?;
157                }
158                f.write_str(")")?;
159                if base_iri.is_some() {
160                    f.write_str(")")?;
161                }
162                Ok(())
163            }
164            Self::Describe {
165                dataset,
166                pattern,
167                base_iri,
168            } => {
169                if let Some(base_iri) = base_iri {
170                    write!(f, "(base <{base_iri}> ")?;
171                }
172                f.write_str("(describe ")?;
173                if let Some(dataset) = dataset {
174                    f.write_str("(dataset ")?;
175                    dataset.fmt_sse(f)?;
176                    f.write_str(" ")?;
177                }
178                pattern.fmt_sse(f)?;
179                if dataset.is_some() {
180                    f.write_str(")")?;
181                }
182                f.write_str(")")?;
183                if base_iri.is_some() {
184                    f.write_str(")")?;
185                }
186                Ok(())
187            }
188            Self::Ask {
189                dataset,
190                pattern,
191                base_iri,
192            } => {
193                if let Some(base_iri) = base_iri {
194                    write!(f, "(base <{base_iri}> ")?;
195                }
196                f.write_str("(ask ")?;
197                if let Some(dataset) = dataset {
198                    f.write_str("(dataset ")?;
199                    dataset.fmt_sse(f)?;
200                    f.write_str(" ")?;
201                }
202                pattern.fmt_sse(f)?;
203                if dataset.is_some() {
204                    f.write_str(")")?;
205                }
206                f.write_str(")")?;
207                if base_iri.is_some() {
208                    f.write_str(")")?;
209                }
210                Ok(())
211            }
212        }
213    }
214}
215
216impl fmt::Display for Query {
217    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218        match self {
219            Self::Select {
220                dataset,
221                pattern,
222                base_iri,
223            } => {
224                if let Some(base_iri) = base_iri {
225                    writeln!(f, "BASE <{base_iri}>")?;
226                }
227                write!(
228                    f,
229                    "{}",
230                    SparqlGraphRootPattern::new(pattern, dataset.as_ref())
231                )
232            }
233            Self::Construct {
234                template,
235                dataset,
236                pattern,
237                base_iri,
238            } => {
239                if let Some(base_iri) = base_iri {
240                    writeln!(f, "BASE <{base_iri}>")?;
241                }
242                f.write_str("CONSTRUCT { ")?;
243                for triple in template {
244                    write!(f, "{triple} . ")?;
245                }
246                f.write_str("}")?;
247                if let Some(dataset) = dataset {
248                    dataset.fmt(f)?;
249                }
250                write!(
251                    f,
252                    " WHERE {{ {} }}",
253                    SparqlGraphRootPattern::new(pattern, None)
254                )
255            }
256            Self::Describe {
257                dataset,
258                pattern,
259                base_iri,
260            } => {
261                if let Some(base_iri) = base_iri {
262                    writeln!(f, "BASE <{}>", base_iri.as_str())?;
263                }
264                f.write_str("DESCRIBE *")?;
265                if let Some(dataset) = dataset {
266                    dataset.fmt(f)?;
267                }
268                write!(
269                    f,
270                    " WHERE {{ {} }}",
271                    SparqlGraphRootPattern::new(pattern, None)
272                )
273            }
274            Self::Ask {
275                dataset,
276                pattern,
277                base_iri,
278            } => {
279                if let Some(base_iri) = base_iri {
280                    writeln!(f, "BASE <{base_iri}>")?;
281                }
282                f.write_str("ASK")?;
283                if let Some(dataset) = dataset {
284                    dataset.fmt(f)?;
285                }
286                write!(
287                    f,
288                    " WHERE {{ {} }}",
289                    SparqlGraphRootPattern::new(pattern, None)
290                )
291            }
292        }
293    }
294}
295
296impl FromStr for Query {
297    type Err = SparqlSyntaxError;
298
299    fn from_str(query: &str) -> Result<Self, Self::Err> {
300        Self::parse(query, None)
301    }
302}
303
304impl TryFrom<&str> for Query {
305    type Error = SparqlSyntaxError;
306
307    fn try_from(query: &str) -> Result<Self, Self::Error> {
308        Self::from_str(query)
309    }
310}
311
312impl TryFrom<&String> for Query {
313    type Error = SparqlSyntaxError;
314
315    fn try_from(query: &String) -> Result<Self, Self::Error> {
316        Self::from_str(query)
317    }
318}