pull/785/merge
Yuri Astrakhan 1 year ago committed by GitHub
commit 7c66717ca3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 50
      Cargo.lock
  2. 1
      Cargo.toml
  3. 5
      lib/oxttl/Cargo.toml
  4. 51
      lib/oxttl/src/n3.rs
  5. 1
      lib/spargebra/Cargo.toml
  6. 244
      lib/spargebra/src/algebra.rs

50
Cargo.lock generated

@ -1191,6 +1191,7 @@ dependencies = [
"oxilangtag", "oxilangtag",
"oxiri", "oxiri",
"oxrdf", "oxrdf",
"parse-display",
"thiserror", "thiserror",
"tokio", "tokio",
] ]
@ -1218,6 +1219,31 @@ dependencies = [
"windows-targets 0.48.5", "windows-targets 0.48.5",
] ]
[[package]]
name = "parse-display"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06af5f9333eb47bd9ba8462d612e37a8328a5cb80b13f0af4de4c3b89f52dee5"
dependencies = [
"parse-display-derive",
"regex",
"regex-syntax",
]
[[package]]
name = "parse-display-derive"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc9252f259500ee570c75adcc4e317fa6f57a1e47747d622e0bf838002a7b790"
dependencies = [
"proc-macro2",
"quote",
"regex",
"regex-syntax",
"structmeta",
"syn",
]
[[package]] [[package]]
name = "peg" name = "peg"
version = "0.8.2" version = "0.8.2"
@ -1773,6 +1799,7 @@ dependencies = [
"oxilangtag", "oxilangtag",
"oxiri", "oxiri",
"oxrdf", "oxrdf",
"parse-display",
"peg", "peg",
"rand", "rand",
"thiserror", "thiserror",
@ -1806,6 +1833,29 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]]
name = "structmeta"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329"
dependencies = [
"proc-macro2",
"quote",
"structmeta-derive",
"syn",
]
[[package]]
name = "structmeta-derive"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.5.0" version = "2.5.0"

@ -48,6 +48,7 @@ memchr = "2.5"
oxhttp = "0.2.0-alpha.4" oxhttp = "0.2.0-alpha.4"
oxilangtag = "0.1" oxilangtag = "0.1"
oxiri = "0.2.3-alpha.1" oxiri = "0.2.3-alpha.1"
parse-display = "0.9.0"
peg = "0.8" peg = "0.8"
pkg-config = "0.3.25" pkg-config = "0.3.25"
predicates = ">=2.0, <4.0" predicates = ">=2.0, <4.0"

@ -20,9 +20,10 @@ async-tokio = ["dep:tokio"]
[dependencies] [dependencies]
memchr.workspace = true memchr.workspace = true
oxrdf.workspace = true
oxiri.workspace = true
oxilangtag.workspace = true oxilangtag.workspace = true
oxiri.workspace = true
oxrdf.workspace = true
parse-display.workspace = true
thiserror.workspace = true thiserror.workspace = true
tokio = { workspace = true, optional = true, features = ["io-util"] } tokio = { workspace = true, optional = true, features = ["io-util"] }

@ -23,7 +23,8 @@ use std::io::Read;
use tokio::io::AsyncRead; use tokio::io::AsyncRead;
/// A N3 term i.e. a RDF `Term` or a `Variable`. /// A N3 term i.e. a RDF `Term` or a `Variable`.
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash, parse_display::Display)]
#[display("{0}")]
pub enum N3Term { pub enum N3Term {
NamedNode(NamedNode), NamedNode(NamedNode),
BlankNode(BlankNode), BlankNode(BlankNode),
@ -33,17 +34,45 @@ pub enum N3Term {
Variable(Variable), Variable(Variable),
} }
impl fmt::Display for N3Term { #[cfg(test)]
#[inline] mod test_n3term {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use super::*;
match self { #[test]
Self::NamedNode(term) => term.fmt(f), fn display() {
Self::BlankNode(term) => term.fmt(f), // This is a temporary migration test - we can remove most of these before merging
Self::Literal(term) => term.fmt(f), assert_eq!(
N3Term::NamedNode(NamedNode::new_unchecked("named")).to_string(),
"<named>"
);
assert_eq!(
N3Term::BlankNode(BlankNode::new_unchecked("blank")).to_string(),
"_:blank"
);
assert_eq!(
N3Term::Literal(Literal::new_simple_literal("literal")).to_string(),
r#""literal""#
);
#[cfg(feature = "rdf-star")] #[cfg(feature = "rdf-star")]
Self::Triple(term) => term.fmt(f), assert_eq!(
Self::Variable(term) => term.fmt(f), N3Term::Triple(Box::new(
} Triple::new(
NamedNode::new_unchecked("http://example.com/s"),
NamedNode::new_unchecked("http://example.com/p"),
Triple::new(
NamedNode::new_unchecked("http://example.com/os"),
NamedNode::new_unchecked("http://example.com/op"),
NamedNode::new_unchecked("http://example.com/oo"),
),
)
))
.to_string(),
"<http://example.com/s> <http://example.com/p> <<<http://example.com/os> <http://example.com/op> <http://example.com/oo>>>"
);
assert_eq!(
N3Term::Variable(Variable::new_unchecked("var")).to_string(),
"?var"
);
} }
} }

@ -23,6 +23,7 @@ sep-0006 = []
oxilangtag.workspace = true oxilangtag.workspace = true
oxiri.workspace = true oxiri.workspace = true
oxrdf.workspace = true oxrdf.workspace = true
parse-display.workspace = true
peg.workspace = true peg.workspace = true
rand.workspace = true rand.workspace = true
thiserror.workspace = true thiserror.workspace = true

@ -91,6 +91,30 @@ impl fmt::Display for PropertyPathExpression {
} }
} }
/// The `display(with = ...)` will not work until this PR lands:
/// https://github.com/frozenlib/parse-display/issues/36#issuecomment-1925367731
///
#[cfg(skip)]
#[derive(Eq, PartialEq, Debug, Clone, Hash, parse_display::Display)]
pub enum PropertyPathExpression {
#[display("{0}")]
NamedNode(NamedNode),
#[display("^({0})")]
Reverse(Box<Self>),
#[display("({0} / {1})")]
Sequence(Box<Self>, Box<Self>),
#[display("({0} | {1})")]
Alternative(Box<Self>, Box<Self>),
#[display("({0})*")]
ZeroOrMore(Box<Self>),
#[display("({0})+")]
OneOrMore(Box<Self>),
#[display("({0})?")]
ZeroOrOne(Box<Self>),
#[display("!({0})")]
NegatedPropertySet(#[display(with = delimiter(" | "))] Vec<NamedNode>),
}
impl From<NamedNode> for PropertyPathExpression { impl From<NamedNode> for PropertyPathExpression {
fn from(p: NamedNode) -> Self { fn from(p: NamedNode) -> Self {
Self::NamedNode(p) Self::NamedNode(p)
@ -318,7 +342,8 @@ fn write_arg_list(
} }
/// A function name. /// A function name.
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash, parse_display::Display)]
#[display(style = "UPPERCASE")]
pub enum Function { pub enum Function {
Str, Str,
Lang, Lang,
@ -337,6 +362,7 @@ pub enum Function {
Replace, Replace,
UCase, UCase,
LCase, LCase,
#[display("ENCODE_FOR_URI")]
EncodeForUri, EncodeForUri,
Contains, Contains,
StrStarts, StrStarts,
@ -361,9 +387,13 @@ pub enum Function {
Sha512, Sha512,
StrLang, StrLang,
StrDt, StrDt,
#[display("isIRI")]
IsIri, IsIri,
#[display("isBLANK")]
IsBlank, IsBlank,
#[display("isLITERAL")]
IsLiteral, IsLiteral,
#[display("isNUMERIC")]
IsNumeric, IsNumeric,
Regex, Regex,
#[cfg(feature = "rdf-star")] #[cfg(feature = "rdf-star")]
@ -375,9 +405,11 @@ pub enum Function {
#[cfg(feature = "rdf-star")] #[cfg(feature = "rdf-star")]
Object, Object,
#[cfg(feature = "rdf-star")] #[cfg(feature = "rdf-star")]
#[display("isTRIPLE")]
IsTriple, IsTriple,
#[cfg(feature = "sep-0002")] #[cfg(feature = "sep-0002")]
Adjust, Adjust,
#[display("{0}")]
Custom(NamedNode), Custom(NamedNode),
} }
@ -448,69 +480,74 @@ impl Function {
} }
} }
impl fmt::Display for Function { #[cfg(test)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { mod test_func {
match self { use super::*;
Self::Str => f.write_str("STR"), #[test]
Self::Lang => f.write_str("LANG"), fn display() {
Self::LangMatches => f.write_str("LANGMATCHES"), // This is a temporary migration test - we can remove most of these before merging
Self::Datatype => f.write_str("DATATYPE"), assert_eq!(Function::Str.to_string(), "STR");
Self::Iri => f.write_str("IRI"), assert_eq!(Function::Lang.to_string(), "LANG");
Self::BNode => f.write_str("BNODE"), assert_eq!(Function::LangMatches.to_string(), "LANGMATCHES");
Self::Rand => f.write_str("RAND"), assert_eq!(Function::Datatype.to_string(), "DATATYPE");
Self::Abs => f.write_str("ABS"), assert_eq!(Function::Iri.to_string(), "IRI");
Self::Ceil => f.write_str("CEIL"), assert_eq!(Function::BNode.to_string(), "BNODE");
Self::Floor => f.write_str("FLOOR"), assert_eq!(Function::Rand.to_string(), "RAND");
Self::Round => f.write_str("ROUND"), assert_eq!(Function::Abs.to_string(), "ABS");
Self::Concat => f.write_str("CONCAT"), assert_eq!(Function::Ceil.to_string(), "CEIL");
Self::SubStr => f.write_str("SUBSTR"), assert_eq!(Function::Floor.to_string(), "FLOOR");
Self::StrLen => f.write_str("STRLEN"), assert_eq!(Function::Round.to_string(), "ROUND");
Self::Replace => f.write_str("REPLACE"), assert_eq!(Function::Concat.to_string(), "CONCAT");
Self::UCase => f.write_str("UCASE"), assert_eq!(Function::SubStr.to_string(), "SUBSTR");
Self::LCase => f.write_str("LCASE"), assert_eq!(Function::StrLen.to_string(), "STRLEN");
Self::EncodeForUri => f.write_str("ENCODE_FOR_URI"), assert_eq!(Function::Replace.to_string(), "REPLACE");
Self::Contains => f.write_str("CONTAINS"), assert_eq!(Function::UCase.to_string(), "UCASE");
Self::StrStarts => f.write_str("STRSTARTS"), assert_eq!(Function::LCase.to_string(), "LCASE");
Self::StrEnds => f.write_str("STRENDS"), assert_eq!(Function::EncodeForUri.to_string(), "ENCODE_FOR_URI");
Self::StrBefore => f.write_str("STRBEFORE"), assert_eq!(Function::Contains.to_string(), "CONTAINS");
Self::StrAfter => f.write_str("STRAFTER"), assert_eq!(Function::StrStarts.to_string(), "STRSTARTS");
Self::Year => f.write_str("YEAR"), assert_eq!(Function::StrEnds.to_string(), "STRENDS");
Self::Month => f.write_str("MONTH"), assert_eq!(Function::StrBefore.to_string(), "STRBEFORE");
Self::Day => f.write_str("DAY"), assert_eq!(Function::StrAfter.to_string(), "STRAFTER");
Self::Hours => f.write_str("HOURS"), assert_eq!(Function::Year.to_string(), "YEAR");
Self::Minutes => f.write_str("MINUTES"), assert_eq!(Function::Month.to_string(), "MONTH");
Self::Seconds => f.write_str("SECONDS"), assert_eq!(Function::Day.to_string(), "DAY");
Self::Timezone => f.write_str("TIMEZONE"), assert_eq!(Function::Hours.to_string(), "HOURS");
Self::Tz => f.write_str("TZ"), assert_eq!(Function::Minutes.to_string(), "MINUTES");
Self::Now => f.write_str("NOW"), assert_eq!(Function::Seconds.to_string(), "SECONDS");
Self::Uuid => f.write_str("UUID"), assert_eq!(Function::Timezone.to_string(), "TIMEZONE");
Self::StrUuid => f.write_str("STRUUID"), assert_eq!(Function::Tz.to_string(), "TZ");
Self::Md5 => f.write_str("MD5"), assert_eq!(Function::Now.to_string(), "NOW");
Self::Sha1 => f.write_str("SHA1"), assert_eq!(Function::Uuid.to_string(), "UUID");
Self::Sha256 => f.write_str("SHA256"), assert_eq!(Function::StrUuid.to_string(), "STRUUID");
Self::Sha384 => f.write_str("SHA384"), assert_eq!(Function::Md5.to_string(), "MD5");
Self::Sha512 => f.write_str("SHA512"), assert_eq!(Function::Sha1.to_string(), "SHA1");
Self::StrLang => f.write_str("STRLANG"), assert_eq!(Function::Sha256.to_string(), "SHA256");
Self::StrDt => f.write_str("STRDT"), assert_eq!(Function::Sha384.to_string(), "SHA384");
Self::IsIri => f.write_str("isIRI"), assert_eq!(Function::Sha512.to_string(), "SHA512");
Self::IsBlank => f.write_str("isBLANK"), assert_eq!(Function::StrLang.to_string(), "STRLANG");
Self::IsLiteral => f.write_str("isLITERAL"), assert_eq!(Function::StrDt.to_string(), "STRDT");
Self::IsNumeric => f.write_str("isNUMERIC"), assert_eq!(Function::IsIri.to_string(), "isIRI");
Self::Regex => f.write_str("REGEX"), assert_eq!(Function::IsBlank.to_string(), "isBLANK");
assert_eq!(Function::IsLiteral.to_string(), "isLITERAL");
assert_eq!(Function::IsNumeric.to_string(), "isNUMERIC");
assert_eq!(Function::Regex.to_string(), "REGEX");
#[cfg(feature = "rdf-star")] #[cfg(feature = "rdf-star")]
Self::Triple => f.write_str("TRIPLE"), assert_eq!(Function::Triple.to_string(), "TRIPLE");
#[cfg(feature = "rdf-star")] #[cfg(feature = "rdf-star")]
Self::Subject => f.write_str("SUBJECT"), assert_eq!(Function::Subject.to_string(), "SUBJECT");
#[cfg(feature = "rdf-star")] #[cfg(feature = "rdf-star")]
Self::Predicate => f.write_str("PREDICATE"), assert_eq!(Function::Predicate.to_string(), "PREDICATE");
#[cfg(feature = "rdf-star")] #[cfg(feature = "rdf-star")]
Self::Object => f.write_str("OBJECT"), assert_eq!(Function::Object.to_string(), "OBJECT");
#[cfg(feature = "rdf-star")] #[cfg(feature = "rdf-star")]
Self::IsTriple => f.write_str("isTRIPLE"), assert_eq!(Function::IsTriple.to_string(), "isTRIPLE");
#[cfg(feature = "sep-0002")] #[cfg(feature = "sep-0002")]
Self::Adjust => f.write_str("ADJUST"), assert_eq!(Function::Adjust.to_string(), "ADJUST");
Self::Custom(iri) => iri.fmt(f), assert_eq!(
} Function::Custom(NamedNode::new("http://example.com/foo").unwrap()).to_string(),
"<http://example.com/foo>"
);
} }
} }
@ -1217,7 +1254,8 @@ impl fmt::Display for AggregateExpression {
} }
/// An aggregate function name. /// An aggregate function name.
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash, parse_display::Display)]
#[display(style = "SNAKE_CASE")]
pub enum AggregateFunction { pub enum AggregateFunction {
/// [Count](https://www.w3.org/TR/sparql11-query/#defn_aggCount) with *. /// [Count](https://www.w3.org/TR/sparql11-query/#defn_aggCount) with *.
Count, Count,
@ -1230,11 +1268,11 @@ pub enum AggregateFunction {
/// [Max](https://www.w3.org/TR/sparql11-query/#defn_aggMax). /// [Max](https://www.w3.org/TR/sparql11-query/#defn_aggMax).
Max, Max,
/// [GroupConcat](https://www.w3.org/TR/sparql11-query/#defn_aggGroupConcat). /// [GroupConcat](https://www.w3.org/TR/sparql11-query/#defn_aggGroupConcat).
GroupConcat { #[display("{}")]
separator: Option<String>, GroupConcat { separator: Option<String> },
},
/// [Sample](https://www.w3.org/TR/sparql11-query/#defn_aggSample). /// [Sample](https://www.w3.org/TR/sparql11-query/#defn_aggSample).
Sample, Sample,
#[display("{0}")]
Custom(NamedNode), Custom(NamedNode),
} }
@ -1254,23 +1292,36 @@ impl AggregateFunction {
} }
} }
impl fmt::Display for AggregateFunction { #[cfg(test)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { mod test_agg {
match self { use super::*;
Self::Count => f.write_str("COUNT"), #[test]
Self::Sum => f.write_str("SUM"), fn display() {
Self::Avg => f.write_str("AVG"), // This is a temporary migration test - we can remove most of these before merging
Self::Min => f.write_str("MIN"), assert_eq!(AggregateFunction::Count.to_string(), "COUNT");
Self::Max => f.write_str("MAX"), assert_eq!(AggregateFunction::Sum.to_string(), "SUM");
Self::GroupConcat { .. } => f.write_str("GROUP_CONCAT"), assert_eq!(AggregateFunction::Avg.to_string(), "AVG");
Self::Sample => f.write_str("SAMPLE"), assert_eq!(AggregateFunction::Min.to_string(), "MIN");
Self::Custom(iri) => iri.fmt(f), assert_eq!(AggregateFunction::Max.to_string(), "MAX");
} assert_eq!(
AggregateFunction::GroupConcat {
separator: Some("foo".to_owned())
}
.to_string(),
"GROUP_CONCAT"
);
assert_eq!(AggregateFunction::Sample.to_string(), "SAMPLE");
assert_eq!(
AggregateFunction::Custom(NamedNode::new("http://example.com/foo").unwrap())
.to_string(),
"<http://example.com/foo>"
);
} }
} }
/// An ordering comparator used by [`GraphPattern::OrderBy`]. /// An ordering comparator used by [`GraphPattern::OrderBy`].
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash, parse_display::Display)]
#[display("{}({0})", style = "UPPERCASE")]
pub enum OrderExpression { pub enum OrderExpression {
/// Ascending order /// Ascending order
Asc(Expression), Asc(Expression),
@ -1296,12 +1347,26 @@ impl OrderExpression {
} }
} }
impl fmt::Display for OrderExpression { #[cfg(test)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { mod test_order_expr {
match self { use super::*;
Self::Asc(e) => write!(f, "ASC({e})"), #[test]
Self::Desc(e) => write!(f, "DESC({e})"), fn display() {
} // This is a temporary migration test - we can remove most of these before merging
assert_eq!(
OrderExpression::Asc(Expression::NamedNode(
NamedNode::new("http://example.com/foo").unwrap()
))
.to_string(),
"ASC(<http://example.com/foo>)"
);
assert_eq!(
OrderExpression::Desc(Expression::NamedNode(
NamedNode::new("http://example.com/bar").unwrap()
))
.to_string(),
"DESC(<http://example.com/bar>)"
);
} }
} }
@ -1351,11 +1416,15 @@ impl fmt::Display for QueryDataset {
/// A target RDF graph for update operations. /// A target RDF graph for update operations.
/// ///
/// Could be a specific graph, all named graphs or the complete dataset. /// Could be a specific graph, all named graphs or the complete dataset.
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash, parse_display::Display)]
pub enum GraphTarget { pub enum GraphTarget {
#[display("GRAPH {0}")]
NamedNode(NamedNode), NamedNode(NamedNode),
#[display("DEFAULT")]
DefaultGraph, DefaultGraph,
#[display("NAMED")]
NamedGraphs, NamedGraphs,
#[display("ALL")]
AllGraphs, AllGraphs,
} }
@ -1371,17 +1440,6 @@ impl GraphTarget {
} }
} }
impl fmt::Display for GraphTarget {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NamedNode(node) => write!(f, "GRAPH {node}"),
Self::DefaultGraph => f.write_str("DEFAULT"),
Self::NamedGraphs => f.write_str("NAMED"),
Self::AllGraphs => f.write_str("ALL"),
}
}
}
impl From<NamedNode> for GraphTarget { impl From<NamedNode> for GraphTarget {
fn from(node: NamedNode) -> Self { fn from(node: NamedNode) -> Self {
Self::NamedNode(node) Self::NamedNode(node)

Loading…
Cancel
Save