Fork of https://github.com/oxigraph/oxigraph.git for the purpose of NextGraph project
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
oxigraph/lib/spargebra/src/query.rs

298 lines
9.2 KiB

use crate::algebra::*;
use crate::parser::{parse_query, ParseError};
use crate::term::*;
use oxiri::Iri;
use std::fmt;
use std::str::FromStr;
/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/).
///
/// ```
/// use spargebra::Query;
///
/// let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
/// let query = Query::parse(query_str, None)?;
/// assert_eq!(query.to_string(), query_str);
/// assert_eq!(query.to_sse(), "(project (?s ?p ?o) (bgp (triple ?s ?p ?o)))");
/// # Ok::<_, spargebra::ParseError>(())
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum Query {
/// [SELECT](https://www.w3.org/TR/sparql11-query/#select).
Select {
/// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset).
dataset: Option<QueryDataset>,
/// The query selection graph pattern.
pattern: GraphPattern,
/// The query base IRI.
base_iri: Option<Iri<String>>,
},
/// [CONSTRUCT](https://www.w3.org/TR/sparql11-query/#construct).
Construct {
/// The query construction template.
template: Vec<TriplePattern>,
/// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset).
dataset: Option<QueryDataset>,
/// The query selection graph pattern.
pattern: GraphPattern,
/// The query base IRI.
base_iri: Option<Iri<String>>,
},
/// [DESCRIBE](https://www.w3.org/TR/sparql11-query/#describe).
Describe {
/// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset).
dataset: Option<QueryDataset>,
/// The query selection graph pattern.
pattern: GraphPattern,
/// The query base IRI.
base_iri: Option<Iri<String>>,
},
/// [ASK](https://www.w3.org/TR/sparql11-query/#ask).
Ask {
/// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset).
dataset: Option<QueryDataset>,
/// The query selection graph pattern.
pattern: GraphPattern,
/// The query base IRI.
base_iri: Option<Iri<String>>,
},
}
impl Query {
/// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query.
pub fn parse(query: &str, base_iri: Option<&str>) -> Result<Self, ParseError> {
parse_query(query, base_iri)
}
/// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
pub fn to_sse(&self) -> String {
let mut buffer = String::new();
self.fmt_sse(&mut buffer).unwrap();
buffer
}
/// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
fn fmt_sse(&self, f: &mut impl fmt::Write) -> fmt::Result {
match self {
Self::Select {
dataset,
pattern,
base_iri,
} => {
if let Some(base_iri) = base_iri {
write!(f, "(base <{base_iri}> ")?;
}
if let Some(dataset) = dataset {
write!(f, "(dataset ")?;
dataset.fmt_sse(f)?;
write!(f, " ")?;
}
pattern.fmt_sse(f)?;
if dataset.is_some() {
write!(f, ")")?;
}
if base_iri.is_some() {
write!(f, ")")?;
}
Ok(())
}
Self::Construct {
template,
dataset,
pattern,
base_iri,
} => {
if let Some(base_iri) = base_iri {
write!(f, "(base <{base_iri}> ")?;
}
write!(f, "(construct (")?;
for (i, t) in template.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
t.fmt_sse(f)?;
}
write!(f, ") ")?;
if let Some(dataset) = dataset {
write!(f, "(dataset ")?;
dataset.fmt_sse(f)?;
write!(f, " ")?;
}
pattern.fmt_sse(f)?;
if dataset.is_some() {
write!(f, ")")?;
}
write!(f, ")")?;
if base_iri.is_some() {
write!(f, ")")?;
}
Ok(())
}
Self::Describe {
dataset,
pattern,
base_iri,
} => {
if let Some(base_iri) = base_iri {
write!(f, "(base <{base_iri}> ")?;
}
write!(f, "(describe ")?;
if let Some(dataset) = dataset {
write!(f, "(dataset ")?;
dataset.fmt_sse(f)?;
write!(f, " ")?;
}
pattern.fmt_sse(f)?;
if dataset.is_some() {
write!(f, ")")?;
}
write!(f, ")")?;
if base_iri.is_some() {
write!(f, ")")?;
}
Ok(())
}
Self::Ask {
dataset,
pattern,
base_iri,
} => {
if let Some(base_iri) = base_iri {
write!(f, "(base <{base_iri}> ")?;
}
write!(f, "(ask ")?;
if let Some(dataset) = dataset {
write!(f, "(dataset ")?;
dataset.fmt_sse(f)?;
write!(f, " ")?;
}
pattern.fmt_sse(f)?;
if dataset.is_some() {
write!(f, ")")?;
}
write!(f, ")")?;
if base_iri.is_some() {
write!(f, ")")?;
}
Ok(())
}
}
}
}
impl fmt::Display for Query {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Select {
dataset,
pattern,
base_iri,
} => {
if let Some(base_iri) = base_iri {
writeln!(f, "BASE <{base_iri}>")?;
}
write!(
f,
"{}",
SparqlGraphRootPattern {
pattern,
dataset: dataset.as_ref()
}
)
}
Self::Construct {
template,
dataset,
pattern,
base_iri,
} => {
if let Some(base_iri) = base_iri {
writeln!(f, "BASE <{base_iri}>")?;
}
write!(f, "CONSTRUCT {{ ")?;
for triple in template.iter() {
write!(f, "{triple} . ")?;
}
write!(f, "}}")?;
if let Some(dataset) = dataset {
dataset.fmt(f)?;
}
write!(
f,
" WHERE {{ {} }}",
SparqlGraphRootPattern {
pattern,
dataset: None
}
)
}
Self::Describe {
dataset,
pattern,
base_iri,
} => {
if let Some(base_iri) = base_iri {
writeln!(f, "BASE <{}>", base_iri.as_str())?;
}
write!(f, "DESCRIBE *")?;
if let Some(dataset) = dataset {
dataset.fmt(f)?;
}
write!(
f,
" WHERE {{ {} }}",
SparqlGraphRootPattern {
pattern,
dataset: None
}
)
}
Self::Ask {
dataset,
pattern,
base_iri,
} => {
if let Some(base_iri) = base_iri {
writeln!(f, "BASE <{base_iri}>")?;
}
write!(f, "ASK")?;
if let Some(dataset) = dataset {
dataset.fmt(f)?;
}
write!(
f,
" WHERE {{ {} }}",
SparqlGraphRootPattern {
pattern,
dataset: None
}
)
}
}
}
}
impl FromStr for Query {
type Err = ParseError;
fn from_str(query: &str) -> Result<Self, ParseError> {
Self::parse(query, None)
}
}
impl<'a> TryFrom<&'a str> for Query {
type Error = ParseError;
fn try_from(query: &str) -> Result<Self, ParseError> {
Self::from_str(query)
}
}
impl<'a> TryFrom<&'a String> for Query {
type Error = ParseError;
fn try_from(query: &String) -> Result<Self, ParseError> {
Self::from_str(query)
}
}