Skip to main content

spargebra/
query.rs

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