parent
e4f97bafb3
commit
be4a5b0b6b
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,19 @@ |
|||||||
|
[package] |
||||||
|
name = "spargebra" |
||||||
|
version = "0.1.0" |
||||||
|
authors = ["Tpt <thomas@pellissier-tanon.fr>"] |
||||||
|
license = "MIT OR Apache-2.0" |
||||||
|
readme = "README.md" |
||||||
|
keywords = ["SPARQL"] |
||||||
|
repository = "https://github.com/oxigraph/oxigraph/tree/master/spargebra" |
||||||
|
homepage = "https://oxigraph.org/" |
||||||
|
description = """ |
||||||
|
A SPARQL parser |
||||||
|
""" |
||||||
|
edition = "2018" |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
peg = "0.7" |
||||||
|
rand = "0.8" |
||||||
|
oxiri = "0.1" |
||||||
|
oxilangtag = "0.1" |
@ -0,0 +1,40 @@ |
|||||||
|
Spargebra |
||||||
|
======== |
||||||
|
|
||||||
|
[![Latest Version](https://img.shields.io/crates/v/spargebra.svg)](https://crates.io/crates/spargebra) |
||||||
|
[![Released API docs](https://docs.rs/spargebra/badge.svg)](https://docs.rs/spargebra) |
||||||
|
[![Crates.io downloads](https://img.shields.io/crates/d/spargebra)](https://crates.io/crates/spargebra) |
||||||
|
[![actions status](https://github.com/oxigraph/oxigraph/workflows/build/badge.svg)](https://github.com/oxigraph/oxigraph/actions) |
||||||
|
[![Gitter](https://badges.gitter.im/oxigraph/community.svg)](https://gitter.im/oxigraph/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) |
||||||
|
|
||||||
|
Spargebra is a [SPARQL](https://www.w3.org/TR/sparql11-overview/) parser. |
||||||
|
|
||||||
|
It supports both [SPARQL 1.1 Query](https://www.w3.org/TR/sparql11-query/) and [SPARQL 1.1 Update](https://www.w3.org/TR/sparql11-update/). |
||||||
|
|
||||||
|
This crate is intended to be a building piece for SPARQL implementations in Rust like [Oxigraph](https://oxigraph.org). |
||||||
|
|
||||||
|
Usage example: |
||||||
|
|
||||||
|
```rust |
||||||
|
use spargebra::Query; |
||||||
|
|
||||||
|
let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }"; |
||||||
|
let mut query = Query::parse(query_str, None)?; |
||||||
|
assert_eq!(query.to_string(), query_str); |
||||||
|
``` |
||||||
|
|
||||||
|
## License |
||||||
|
|
||||||
|
This project is licensed under either of |
||||||
|
|
||||||
|
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or |
||||||
|
http://www.apache.org/licenses/LICENSE-2.0) |
||||||
|
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or |
||||||
|
http://opensource.org/licenses/MIT) |
||||||
|
|
||||||
|
at your option. |
||||||
|
|
||||||
|
|
||||||
|
### Contribution |
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Futures by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,36 @@ |
|||||||
|
//! This crate provides [SPARQL 1.1](http://www.w3.org/TR/sparql11-overview/) query and update parsers.
|
||||||
|
//! The emitted tree is based on [SPARQL 1.1 Query Algebra](https://www.w3.org/TR/sparql11-query/#sparqlQuery) objects.
|
||||||
|
//!
|
||||||
|
//! The API entry point for SPARQL queries is [`Query`] and the API entry point for SPARQL updates is [`Update`].
|
||||||
|
//!
|
||||||
|
//! This crate is intended to be a building piece for SPARQL implementations in Rust like [Oxigraph](https://oxigraph.org).
|
||||||
|
//!
|
||||||
|
//! Usage example:
|
||||||
|
//! ```
|
||||||
|
//! use spargebra::Query;
|
||||||
|
//!
|
||||||
|
//! let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
|
||||||
|
//! let mut query = Query::parse(query_str, None)?;
|
||||||
|
//! assert_eq!(query.to_string(), query_str);
|
||||||
|
//! # Result::Ok::<_, spargebra::ParseError>(())
|
||||||
|
//! ```
|
||||||
|
#![deny(
|
||||||
|
future_incompatible, |
||||||
|
nonstandard_style, |
||||||
|
rust_2018_idioms, |
||||||
|
missing_copy_implementations, |
||||||
|
trivial_casts, |
||||||
|
trivial_numeric_casts, |
||||||
|
unsafe_code, |
||||||
|
unused_qualifications |
||||||
|
)] |
||||||
|
|
||||||
|
pub mod algebra; |
||||||
|
mod parser; |
||||||
|
mod query; |
||||||
|
pub mod term; |
||||||
|
mod update; |
||||||
|
|
||||||
|
pub use parser::ParseError; |
||||||
|
pub use query::*; |
||||||
|
pub use update::*; |
@ -0,0 +1,181 @@ |
|||||||
|
use crate::algebra::*; |
||||||
|
use crate::parser::{parse_query, ParseError}; |
||||||
|
use oxiri::Iri; |
||||||
|
use std::convert::TryFrom; |
||||||
|
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 mut query = Query::parse(query_str, None)?;
|
||||||
|
/// assert_eq!(query.to_string(), query_str);
|
||||||
|
/// # Result::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) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for Query { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
match self { |
||||||
|
Query::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() |
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
Query::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, "{} ", SparqlTriplePattern(triple))?; |
||||||
|
} |
||||||
|
write!(f, "}}")?; |
||||||
|
if let Some(dataset) = dataset { |
||||||
|
dataset.fmt(f)?; |
||||||
|
} |
||||||
|
write!( |
||||||
|
f, |
||||||
|
" WHERE {{ {} }}", |
||||||
|
SparqlGraphRootPattern { |
||||||
|
pattern, |
||||||
|
dataset: None |
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
Query::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 |
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
Query::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) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,426 @@ |
|||||||
|
//! Data structures for [RDF 1.1 Concepts](https://www.w3.org/TR/rdf11-concepts/) like IRI, literal or triples.
|
||||||
|
|
||||||
|
use std::fmt; |
||||||
|
use std::fmt::Write; |
||||||
|
|
||||||
|
/// An RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri).
|
||||||
|
///
|
||||||
|
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use spargebra::term::NamedNode;
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// "<http://example.com/foo>",
|
||||||
|
/// NamedNode { iri: "http://example.com/foo".into() }.to_string()
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] |
||||||
|
pub struct NamedNode { |
||||||
|
/// The [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) itself.
|
||||||
|
pub iri: String, |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for NamedNode { |
||||||
|
#[inline] |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
write!(f, "<{}>", self.iri) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// An RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node).
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use spargebra::term::BlankNode;
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// "_:a1",
|
||||||
|
/// BlankNode { id: "a1".into() }.to_string()
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
||||||
|
pub struct BlankNode { |
||||||
|
/// The [blank node identifier](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node-identifier).
|
||||||
|
pub id: String, |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for BlankNode { |
||||||
|
#[inline] |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
write!(f, "_:{}", self.id) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// An RDF [literal](https://www.w3.org/TR/rdf11-concepts/#dfn-literal).
|
||||||
|
///
|
||||||
|
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
|
||||||
|
///
|
||||||
|
/// The language tags should be lowercased [as suggested by the RDF specification](https://www.w3.org/TR/rdf11-concepts/#dfn-language-tagged-string).
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use spargebra::term::NamedNode;
|
||||||
|
/// use spargebra::term::Literal;
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// "\"foo\\nbar\"",
|
||||||
|
/// Literal::Simple { value: "foo\nbar".into() }.to_string()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// "\"1999-01-01\"^^<http://www.w3.org/2001/XMLSchema#date>",
|
||||||
|
/// Literal::Typed { value: "1999-01-01".into(), datatype: NamedNode { iri: "http://www.w3.org/2001/XMLSchema#date".into() }}.to_string()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// "\"foo\"@en",
|
||||||
|
/// Literal::LanguageTaggedString { value: "foo".into(), language: "en".into() }.to_string()
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
||||||
|
pub enum Literal { |
||||||
|
/// A [simple literal](https://www.w3.org/TR/rdf11-concepts/#dfn-simple-literal) without datatype or language form.
|
||||||
|
Simple { |
||||||
|
/// The [lexical form](https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form).
|
||||||
|
value: String, |
||||||
|
}, |
||||||
|
/// A [language-tagged string](https://www.w3.org/TR/rdf11-concepts/#dfn-language-tagged-string)
|
||||||
|
LanguageTaggedString { |
||||||
|
/// The [lexical form](https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form).
|
||||||
|
value: String, |
||||||
|
/// The [language tag](https://www.w3.org/TR/rdf11-concepts/#dfn-language-tag).
|
||||||
|
language: String, |
||||||
|
}, |
||||||
|
/// A literal with an explicit datatype
|
||||||
|
Typed { |
||||||
|
/// The [lexical form](https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form).
|
||||||
|
value: String, |
||||||
|
/// The [datatype IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-datatype-iri).
|
||||||
|
datatype: NamedNode, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for Literal { |
||||||
|
#[inline] |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
match self { |
||||||
|
Literal::Simple { value } => print_quoted_str(value, f), |
||||||
|
Literal::LanguageTaggedString { value, language } => { |
||||||
|
print_quoted_str(value, f)?; |
||||||
|
write!(f, "@{}", language) |
||||||
|
} |
||||||
|
Literal::Typed { value, datatype } => { |
||||||
|
print_quoted_str(value, f)?; |
||||||
|
write!(f, "^^{}", datatype) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// A [SPARQL query variable](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables).
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use spargebra::term::Variable;
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// "?foo",
|
||||||
|
/// Variable { name: "foo".into() }.to_string()
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] |
||||||
|
pub struct Variable { |
||||||
|
pub name: String, |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for Variable { |
||||||
|
#[inline] |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
write!(f, "?{}", self.name) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node).
|
||||||
|
///
|
||||||
|
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
||||||
|
pub enum NamedOrBlankNode { |
||||||
|
NamedNode(NamedNode), |
||||||
|
BlankNode(BlankNode), |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for NamedOrBlankNode { |
||||||
|
#[inline] |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
match self { |
||||||
|
NamedOrBlankNode::NamedNode(node) => node.fmt(f), |
||||||
|
NamedOrBlankNode::BlankNode(node) => node.fmt(f), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NamedNode> for NamedOrBlankNode { |
||||||
|
#[inline] |
||||||
|
fn from(node: NamedNode) -> Self { |
||||||
|
NamedOrBlankNode::NamedNode(node) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<BlankNode> for NamedOrBlankNode { |
||||||
|
#[inline] |
||||||
|
fn from(node: BlankNode) -> Self { |
||||||
|
NamedOrBlankNode::BlankNode(node) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal).
|
||||||
|
///
|
||||||
|
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
||||||
|
pub enum NamedNodeOrLiteral { |
||||||
|
NamedNode(NamedNode), |
||||||
|
Literal(Literal), |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for NamedNodeOrLiteral { |
||||||
|
#[inline] |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
match self { |
||||||
|
NamedNodeOrLiteral::NamedNode(node) => node.fmt(f), |
||||||
|
NamedNodeOrLiteral::Literal(literal) => literal.fmt(f), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NamedNode> for NamedNodeOrLiteral { |
||||||
|
#[inline] |
||||||
|
fn from(node: NamedNode) -> Self { |
||||||
|
NamedNodeOrLiteral::NamedNode(node) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<Literal> for NamedNodeOrLiteral { |
||||||
|
#[inline] |
||||||
|
fn from(literal: Literal) -> Self { |
||||||
|
NamedNodeOrLiteral::Literal(literal) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// An RDF [term](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-term).
|
||||||
|
///
|
||||||
|
/// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node) and [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal).
|
||||||
|
///
|
||||||
|
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
||||||
|
pub enum Term { |
||||||
|
NamedNode(NamedNode), |
||||||
|
BlankNode(BlankNode), |
||||||
|
Literal(Literal), |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for Term { |
||||||
|
#[inline] |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
match self { |
||||||
|
Term::NamedNode(node) => node.fmt(f), |
||||||
|
Term::BlankNode(node) => node.fmt(f), |
||||||
|
Term::Literal(literal) => literal.fmt(f), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NamedNode> for Term { |
||||||
|
#[inline] |
||||||
|
fn from(node: NamedNode) -> Self { |
||||||
|
Term::NamedNode(node) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<BlankNode> for Term { |
||||||
|
#[inline] |
||||||
|
fn from(node: BlankNode) -> Self { |
||||||
|
Term::BlankNode(node) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<Literal> for Term { |
||||||
|
#[inline] |
||||||
|
fn from(literal: Literal) -> Self { |
||||||
|
Term::Literal(literal) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NamedOrBlankNode> for Term { |
||||||
|
#[inline] |
||||||
|
fn from(resource: NamedOrBlankNode) -> Self { |
||||||
|
match resource { |
||||||
|
NamedOrBlankNode::NamedNode(node) => Term::NamedNode(node), |
||||||
|
NamedOrBlankNode::BlankNode(node) => Term::BlankNode(node), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// A possible graph name.
|
||||||
|
/// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node), and the [default graph name](https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph).
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
||||||
|
pub enum GraphName { |
||||||
|
NamedNode(NamedNode), |
||||||
|
DefaultGraph, |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for GraphName { |
||||||
|
#[inline] |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
match self { |
||||||
|
GraphName::NamedNode(node) => node.fmt(f), |
||||||
|
GraphName::DefaultGraph => write!(f, "DEFAULT"), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NamedNode> for GraphName { |
||||||
|
#[inline] |
||||||
|
fn from(node: NamedNode) -> Self { |
||||||
|
GraphName::NamedNode(node) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple) in a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset).
|
||||||
|
///
|
||||||
|
/// The default string formatter is returning a N-Quads representation.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use spargebra::term::NamedNode;
|
||||||
|
/// use spargebra::term::Quad;
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// "<http://example.com/foo> <http://schema.org/sameAs> <http://example.com/foo> <http://example.com/> .",
|
||||||
|
/// Quad {
|
||||||
|
/// subject: NamedNode { iri: "http://example.com/foo".into() }.into(),
|
||||||
|
/// predicate: NamedNode { iri: "http://schema.org/sameAs".into() },
|
||||||
|
/// object: NamedNode { iri: "http://example.com/foo".into() }.into(),
|
||||||
|
/// graph_name: NamedNode { iri: "http://example.com/".into() }.into(),
|
||||||
|
/// }.to_string()
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
||||||
|
pub struct Quad { |
||||||
|
pub subject: NamedOrBlankNode, |
||||||
|
pub predicate: NamedNode, |
||||||
|
pub object: Term, |
||||||
|
pub graph_name: GraphName, |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for Quad { |
||||||
|
#[inline] |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
if self.graph_name == GraphName::DefaultGraph { |
||||||
|
write!(f, "{} {} {} .", self.subject, self.predicate, self.object) |
||||||
|
} else { |
||||||
|
write!( |
||||||
|
f, |
||||||
|
"{} {} {} {} .", |
||||||
|
self.subject, self.predicate, self.object, self.graph_name |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [variables](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables).
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
||||||
|
pub enum NamedNodeOrVariable { |
||||||
|
NamedNode(NamedNode), |
||||||
|
Variable(Variable), |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for NamedNodeOrVariable { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
match self { |
||||||
|
NamedNodeOrVariable::NamedNode(node) => node.fmt(f), |
||||||
|
NamedNodeOrVariable::Variable(var) => var.fmt(f), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NamedNode> for NamedNodeOrVariable { |
||||||
|
fn from(node: NamedNode) -> Self { |
||||||
|
NamedNodeOrVariable::NamedNode(node) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<Variable> for NamedNodeOrVariable { |
||||||
|
fn from(var: Variable) -> Self { |
||||||
|
NamedNodeOrVariable::Variable(var) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// The union of [terms](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-term) and [variables](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables).
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
||||||
|
pub enum TermOrVariable { |
||||||
|
Term(Term), |
||||||
|
Variable(Variable), |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for TermOrVariable { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
match self { |
||||||
|
TermOrVariable::Term(term) => term.fmt(f), |
||||||
|
TermOrVariable::Variable(var) => var.fmt(f), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NamedNode> for TermOrVariable { |
||||||
|
fn from(node: NamedNode) -> Self { |
||||||
|
TermOrVariable::Term(node.into()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<BlankNode> for TermOrVariable { |
||||||
|
fn from(node: BlankNode) -> Self { |
||||||
|
TermOrVariable::Term(node.into()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<Literal> for TermOrVariable { |
||||||
|
fn from(literal: Literal) -> Self { |
||||||
|
TermOrVariable::Term(literal.into()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<Variable> for TermOrVariable { |
||||||
|
fn from(var: Variable) -> Self { |
||||||
|
TermOrVariable::Variable(var) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<Term> for TermOrVariable { |
||||||
|
fn from(term: Term) -> Self { |
||||||
|
TermOrVariable::Term(term) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NamedNodeOrVariable> for TermOrVariable { |
||||||
|
fn from(element: NamedNodeOrVariable) -> Self { |
||||||
|
match element { |
||||||
|
NamedNodeOrVariable::NamedNode(node) => TermOrVariable::Term(node.into()), |
||||||
|
NamedNodeOrVariable::Variable(var) => TermOrVariable::Variable(var), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub(crate) fn print_quoted_str(string: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
f.write_char('"')?; |
||||||
|
for c in string.chars() { |
||||||
|
match c { |
||||||
|
'\n' => f.write_str("\\n"), |
||||||
|
'\r' => f.write_str("\\r"), |
||||||
|
'"' => f.write_str("\\\""), |
||||||
|
'\\' => f.write_str("\\\\"), |
||||||
|
c => f.write_char(c), |
||||||
|
}?; |
||||||
|
} |
||||||
|
f.write_char('"') |
||||||
|
} |
@ -0,0 +1,199 @@ |
|||||||
|
use crate::algebra::*; |
||||||
|
use crate::parser::{parse_update, ParseError}; |
||||||
|
use crate::term::*; |
||||||
|
use oxiri::Iri; |
||||||
|
use std::convert::TryFrom; |
||||||
|
use std::fmt; |
||||||
|
use std::str::FromStr; |
||||||
|
|
||||||
|
/// A parsed [SPARQL update](https://www.w3.org/TR/sparql11-update/)
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use spargebra::Update;
|
||||||
|
///
|
||||||
|
/// let update_str = "CLEAR ALL ;";
|
||||||
|
/// let update = Update::parse(update_str, None)?;
|
||||||
|
/// assert_eq!(update.to_string().trim(), update_str);
|
||||||
|
/// # Result::Ok::<_, spargebra::ParseError>(())
|
||||||
|
/// ```
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
||||||
|
pub struct Update { |
||||||
|
/// The update base IRI
|
||||||
|
pub base_iri: Option<Iri<String>>, |
||||||
|
/// The [update operations](https://www.w3.org/TR/sparql11-update/#formalModelGraphUpdate)
|
||||||
|
pub operations: Vec<GraphUpdateOperation>, |
||||||
|
} |
||||||
|
|
||||||
|
impl Update { |
||||||
|
/// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query
|
||||||
|
pub fn parse(update: &str, base_iri: Option<&str>) -> Result<Self, ParseError> { |
||||||
|
parse_update(update, base_iri) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for Update { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
if let Some(base_iri) = &self.base_iri { |
||||||
|
writeln!(f, "BASE <{}>", base_iri)?; |
||||||
|
} |
||||||
|
for update in &self.operations { |
||||||
|
writeln!(f, "{} ;", update)?; |
||||||
|
} |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl FromStr for Update { |
||||||
|
type Err = ParseError; |
||||||
|
|
||||||
|
fn from_str(update: &str) -> Result<Self, ParseError> { |
||||||
|
Self::parse(update, None) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a str> for Update { |
||||||
|
type Error = ParseError; |
||||||
|
|
||||||
|
fn try_from(update: &str) -> Result<Self, ParseError> { |
||||||
|
Self::from_str(update) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a String> for Update { |
||||||
|
type Error = ParseError; |
||||||
|
|
||||||
|
fn try_from(update: &String) -> Result<Self, ParseError> { |
||||||
|
Self::from_str(update) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// The [graph update operations](https://www.w3.org/TR/sparql11-update/#formalModelGraphUpdate)
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
||||||
|
pub enum GraphUpdateOperation { |
||||||
|
/// [insert data](https://www.w3.org/TR/sparql11-update/#def_insertdataoperation)
|
||||||
|
InsertData { data: Vec<Quad> }, |
||||||
|
/// [delete data](https://www.w3.org/TR/sparql11-update/#def_deletedataoperation)
|
||||||
|
DeleteData { data: Vec<Quad> }, |
||||||
|
/// [delete insert](https://www.w3.org/TR/sparql11-update/#def_deleteinsertoperation)
|
||||||
|
DeleteInsert { |
||||||
|
delete: Vec<QuadPattern>, |
||||||
|
insert: Vec<QuadPattern>, |
||||||
|
using: Option<QueryDataset>, |
||||||
|
pattern: Box<GraphPattern>, |
||||||
|
}, |
||||||
|
/// [load](https://www.w3.org/TR/sparql11-update/#def_loadoperation)
|
||||||
|
Load { |
||||||
|
silent: bool, |
||||||
|
from: NamedNode, |
||||||
|
to: Option<NamedNode>, |
||||||
|
}, |
||||||
|
/// [clear](https://www.w3.org/TR/sparql11-update/#def_clearoperation)
|
||||||
|
Clear { silent: bool, graph: GraphTarget }, |
||||||
|
/// [create](https://www.w3.org/TR/sparql11-update/#def_createoperation)
|
||||||
|
Create { silent: bool, graph: NamedNode }, |
||||||
|
/// [drop](https://www.w3.org/TR/sparql11-update/#def_dropoperation)
|
||||||
|
Drop { silent: bool, graph: GraphTarget }, |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for GraphUpdateOperation { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
match self { |
||||||
|
GraphUpdateOperation::InsertData { data } => { |
||||||
|
writeln!(f, "INSERT DATA {{")?; |
||||||
|
write_quads(data, f)?; |
||||||
|
write!(f, "}}") |
||||||
|
} |
||||||
|
GraphUpdateOperation::DeleteData { data } => { |
||||||
|
writeln!(f, "DELETE DATA {{")?; |
||||||
|
write_quads(data, f)?; |
||||||
|
write!(f, "}}") |
||||||
|
} |
||||||
|
GraphUpdateOperation::DeleteInsert { |
||||||
|
delete, |
||||||
|
insert, |
||||||
|
using, |
||||||
|
pattern, |
||||||
|
} => { |
||||||
|
if !delete.is_empty() { |
||||||
|
writeln!(f, "DELETE {{")?; |
||||||
|
for quad in delete { |
||||||
|
writeln!(f, "\t{}", SparqlQuadPattern(quad))?; |
||||||
|
} |
||||||
|
writeln!(f, "}}")?; |
||||||
|
} |
||||||
|
if !insert.is_empty() { |
||||||
|
writeln!(f, "INSERT {{")?; |
||||||
|
for quad in insert { |
||||||
|
writeln!(f, "\t{}", SparqlQuadPattern(quad))?; |
||||||
|
} |
||||||
|
writeln!(f, "}}")?; |
||||||
|
} |
||||||
|
if let Some(using) = using { |
||||||
|
for g in &using.default { |
||||||
|
writeln!(f, "USING {}", g)?; |
||||||
|
} |
||||||
|
if let Some(named) = &using.named { |
||||||
|
for g in named { |
||||||
|
writeln!(f, "USING NAMED {}", g)?; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
write!( |
||||||
|
f, |
||||||
|
"WHERE {{ {} }}", |
||||||
|
SparqlGraphRootPattern { |
||||||
|
pattern, |
||||||
|
dataset: None |
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
GraphUpdateOperation::Load { silent, from, to } => { |
||||||
|
write!(f, "LOAD ")?; |
||||||
|
if *silent { |
||||||
|
write!(f, "SILENT ")?; |
||||||
|
} |
||||||
|
write!(f, "{}", from)?; |
||||||
|
if let Some(to) = to { |
||||||
|
write!(f, " INTO GRAPH {}", to)?; |
||||||
|
} |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
GraphUpdateOperation::Clear { silent, graph } => { |
||||||
|
write!(f, "CLEAR ")?; |
||||||
|
if *silent { |
||||||
|
write!(f, "SILENT ")?; |
||||||
|
} |
||||||
|
write!(f, "{}", graph) |
||||||
|
} |
||||||
|
GraphUpdateOperation::Create { silent, graph } => { |
||||||
|
write!(f, "CREATE ")?; |
||||||
|
if *silent { |
||||||
|
write!(f, "SILENT ")?; |
||||||
|
} |
||||||
|
write!(f, "GRAPH {}", graph) |
||||||
|
} |
||||||
|
GraphUpdateOperation::Drop { silent, graph } => { |
||||||
|
write!(f, "DROP ")?; |
||||||
|
if *silent { |
||||||
|
write!(f, "SILENT ")?; |
||||||
|
} |
||||||
|
write!(f, "{}", graph) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn write_quads(quads: &[Quad], f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
for quad in quads { |
||||||
|
if quad.graph_name == GraphName::DefaultGraph { |
||||||
|
writeln!(f, "\t{} {} {} .", quad.subject, quad.predicate, quad.object)?; |
||||||
|
} else { |
||||||
|
writeln!( |
||||||
|
f, |
||||||
|
"\tGRAPH {} {{ {} {} {} }}", |
||||||
|
quad.graph_name, quad.subject, quad.predicate, quad.object |
||||||
|
)?; |
||||||
|
} |
||||||
|
} |
||||||
|
Ok(()) |
||||||
|
} |
Loading…
Reference in new issue