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, /// The query selection graph pattern. pattern: GraphPattern, /// The query base IRI. base_iri: Option>, }, /// [CONSTRUCT](https://www.w3.org/TR/sparql11-query/#construct). Construct { /// The query construction template. template: Vec, /// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset). dataset: Option, /// The query selection graph pattern. pattern: GraphPattern, /// The query base IRI. base_iri: Option>, }, /// [DESCRIBE](https://www.w3.org/TR/sparql11-query/#describe). Describe { /// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset). dataset: Option, /// The query selection graph pattern. pattern: GraphPattern, /// The query base IRI. base_iri: Option>, }, /// [ASK](https://www.w3.org/TR/sparql11-query/#ask). Ask { /// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset). dataset: Option, /// The query selection graph pattern. pattern: GraphPattern, /// The query base IRI. base_iri: Option>, }, } 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 { 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::parse(query, None) } } impl<'a> TryFrom<&'a str> for Query { type Error = ParseError; fn try_from(query: &str) -> Result { Self::from_str(query) } } impl<'a> TryFrom<&'a String> for Query { type Error = ParseError; fn try_from(query: &String) -> Result { Self::from_str(query) } }