Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion lib/rdf-borsh/src/borsh_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,15 @@ impl<R: Read> Reader for BorshReader<R> {
}

impl<R: Read> Source for BorshReader<R> {}
impl<R: Read> Enumerable for BorshReader<R> {}

impl<R: Read> Enumerable for BorshReader<R> {
fn grep(&self, pattern: &impl PartialEq<Box<dyn Statement>>) -> Self
where
Self: Sized,
{
todo!()
Copy link

Copilot AI May 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The grep method is left as a stub using todo!(), which may cause runtime issues when called. Consider implementing the method or providing an explicit indication that it is intentionally unimplemented.

Suggested change
todo!()
// Filter the quads that match the provided pattern
let filtered_quads: Vec<_> = self
.decompressor
.filter_map(|quad| {
if pattern == &Box::new(quad) {
Some(quad)
} else {
None
}
})
.collect();
// Create a new BorshReader with the filtered quads
BorshReader {
decompressor: self.decompressor.clone(), // Assuming decompressor is clonable
term_dict: self.term_dict.clone(),
quad_count: filtered_quads.len(),
read_count: 0,
}

Copilot uses AI. Check for mistakes.
}
}
impl<R: Read> MaybeDurable for BorshReader<R> {}
impl<R: Read> MaybeIndexed for BorshReader<R> {}
impl<R: Read> MaybeMutable for BorshReader<R> {}
Expand Down
3 changes: 0 additions & 3 deletions lib/rdf-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ mod traits {

mod maybe_mutable;
pub use maybe_mutable::*;

mod queryable;
pub use queryable::*;
}
pub use traits::*;

Expand Down
3 changes: 3 additions & 0 deletions lib/rdf-model/src/traits/enumerable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ use core::error::Error;
pub trait Enumerable:
Countable + Iterator<Item = Result<Box<dyn Statement>, Box<dyn Error>>>
{
fn grep(&self, pattern: &impl PartialEq<Box<dyn Statement>>) -> Self
Copy link
Copy Markdown

@imunproductive imunproductive Feb 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grep is not implemented for OxrdfReader and BorshReader

where
Self: Sized;
}
5 changes: 0 additions & 5 deletions lib/rdf-model/src/traits/queryable.rs

This file was deleted.

18 changes: 18 additions & 0 deletions lib/rdf-query/src/graph_pattern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::{pattern::Pattern, query::Query};

pub enum GraphPattern {
BasicGraphPattern(Query),
TriplePattern(Pattern),
}

impl From<Query> for GraphPattern {
fn from(query: Query) -> Self {
Self::BasicGraphPattern(query)
}
}

impl From<Pattern> for GraphPattern {
fn from(pattern: Pattern) -> Self {
Self::TriplePattern(pattern)
}
}
12 changes: 12 additions & 0 deletions lib/rdf-query/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,15 @@

#![no_std]
#![deny(unsafe_code)]

pub mod graph_pattern;
pub mod matcher;
pub mod pattern;
pub mod query;
pub mod solution;
pub mod solutions;
pub mod variable;

mod traits {
pub mod queryable;
}
56 changes: 56 additions & 0 deletions lib/rdf-query/src/matcher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
extern crate alloc;

use alloc::boxed::Box;
use rdf_model::Term;

use crate::variable::Variable;

pub enum Matcher {
Variable(Variable),
Term(Box<dyn Term>),
}

impl Matcher {
pub fn as_variable(&self) -> Option<&Variable> {
match self {
Self::Variable(var) => Some(var),
_ => None,
}
}
}

impl PartialEq<&dyn Term> for Matcher {
fn eq(&self, term: &&dyn Term) -> bool {
match self {
Self::Variable(_) => true,
Self::Term(t) => t.as_str() == term.as_str(),
}
}
}

impl From<Variable> for Matcher {
fn from(var: Variable) -> Self {
Self::Variable(var)
}
}

impl From<&str> for Matcher {
fn from(name: &str) -> Self {
name.into()
}
}
Comment on lines +37 to +41
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation is self-recursing.

warning: function cannot return without recursing
  --> lib/rdf-query/src/matcher.rs:38:5
   |
38 | /     fn from(name: &str) -> Self {
39 | |         name.into()
40 | |     }
   | |_____^
   |
note: recursive call site
  --> lib/rdf-query/src/matcher.rs:39:9
   |
39 |         name.into()
   |         ^^^^^^^^^^^
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unconditional_recursion
   = note: `#[warn(clippy::unconditional_recursion)]` on by default
Suggested change
impl From<&str> for Matcher {
fn from(name: &str) -> Self {
name.into()
}
}
impl From<&str> for Matcher {
fn from(name: &str) -> Self {
Variable::from(name).into()
}
}


impl From<Box<dyn Term>> for Matcher {
fn from(term: Box<dyn Term>) -> Self {
Self::Term(term)
}
}

impl core::fmt::Debug for Matcher {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Self::Variable(var) => write!(f, "{:?}", var),
Self::Term(t) => f.write_str(&t.as_str()),
}
}
}
83 changes: 83 additions & 0 deletions lib/rdf-query/src/pattern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
extern crate alloc;

use alloc::{boxed::Box, collections::BTreeMap, vec};
use rdf_model::{Statement, Term};

use crate::{
matcher::Matcher, solution::Solution, traits::queryable::Queryable, variable::Variable,
};

pub struct Pattern {
subject: Matcher,
predicate: Matcher,
object: Matcher,
graph_name: Option<Box<dyn Term>>,
}

impl Pattern {
pub fn new(
subject: impl Into<Matcher>,
predicate: impl Into<Matcher>,
object: impl Into<Matcher>,
graph_name: Option<Box<dyn Term>>,
) -> Self {
Self {
subject: subject.into(),
predicate: predicate.into(),
object: object.into(),
graph_name,
}
}

pub(crate) fn execute<Q: Queryable>(&self, queryable: &Q) -> Q
where
Self: Sized,
{
queryable.query_pattern(self)
}

/// Returns a query solution constructed by binding any variables in this
/// pattern with the corresponding terms in the given statement.
pub fn solution(&self, statement: Box<dyn Statement>) -> Solution {
let mut ans = BTreeMap::new();

let bindings = vec![
(self.subject.as_variable(), statement.subject()),
(self.predicate.as_variable(), statement.predicate()),
(self.object.as_variable(), statement.object()),
];

for (variable, term) in bindings {
if let Some(variable) = variable.map(Variable::name).map(Variable::unbound) {
ans.insert(variable, term);
}
}

Solution::new(ans)
}
}

impl PartialEq<Box<dyn Statement>> for Pattern {
fn eq(&self, statement: &Box<dyn Statement>) -> bool {
let (s, p, o) = (
statement.subject(),
statement.predicate(),
statement.object(),
);
self.subject == s && self.predicate == p && self.object == o
}
}

impl core::fmt::Debug for Pattern {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("Pattern")
.field("subject", &self.subject)
.field("predicate", &self.predicate)
.field("object", &self.object)
.field(
"graph_name",
&self.graph_name.as_ref().map(|gn| gn.as_str()),
)
.finish()
}
}
41 changes: 41 additions & 0 deletions lib/rdf-query/src/query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
extern crate alloc;

use alloc::vec::Vec;

use crate::{pattern::Pattern, solutions::Solutions, traits::queryable::Queryable};

pub struct Query {
patterns: Vec<Pattern>,
}

impl Query {
pub fn new(patterns: Vec<Pattern>) -> Self {
Self { patterns }
}

/// Executes the query on the given graph.
///
/// If the query nas no patterns, it returns a single empty solution as per
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// If the query nas no patterns, it returns a single empty solution as per
/// If the query has no patterns, it returns a single empty solution as per

Copy link

Copilot AI May 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in the documentation comment: 'nas' should be 'has'.

Suggested change
/// If the query nas no patterns, it returns a single empty solution as per
/// If the query has no patterns, it returns a single empty solution as per

Copilot uses AI. Check for mistakes.
/// SPARQL 1.1 Empty Group Pattern.
pub fn execute<Q: Queryable>(&self, queryable: &Q) -> Solutions {
if self.empty() {
return Solutions::empty();
}

let solutions: Vec<_> = self
.patterns
.iter()
.flat_map(|pattern| {
let statements = pattern.execute(queryable);
statements.filter_map(|res| res.ok().map(|stmt| pattern.solution(stmt)))
})
.collect();

Solutions::new(solutions.into_iter())
}

/// Returns true if the query has no patterns.
pub fn empty(&self) -> bool {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: I think this would be good to rename to match the idiomatic format rust uses. Also you're using empty elsewhere with different semantics (Solutions::empty() -> Solutions).

Suggested change
pub fn empty(&self) -> bool {
pub fn is_empty(&self) -> bool {

self.patterns.is_empty()
}
}
43 changes: 43 additions & 0 deletions lib/rdf-query/src/solution.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
extern crate alloc;

use alloc::collections::BTreeMap;
use rdf_model::HeapTerm;

use crate::variable::Variable;

pub struct Solution {
bindings: BTreeMap<Variable, HeapTerm>,
}

impl Solution {
pub fn new(bindings: BTreeMap<Variable, impl Into<HeapTerm>>) -> Self {
let bindings = bindings
.into_iter()
.map(|(var, term)| (var, term.into()))
.collect();

Self { bindings }
}

pub fn binding(&self, var: &Variable) -> Option<&HeapTerm> {
self.bindings.get(var)
}

pub fn each_binding(&self) -> impl Iterator<Item = (&Variable, &HeapTerm)> {
self.bindings.iter()
}

pub fn each_name(&self) -> impl Iterator<Item = &Variable> {
self.bindings.keys()
}

pub fn each_value(&self) -> impl Iterator<Item = &HeapTerm> {
self.bindings.values()
}
}

impl core::fmt::Debug for Solution {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("Solution").finish()
}
}
37 changes: 37 additions & 0 deletions lib/rdf-query/src/solutions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
extern crate alloc;

use alloc::boxed::Box;

use crate::solution::Solution;

pub struct Solutions {
iter: Box<dyn Iterator<Item = Solution>>,
}

impl Solutions {
pub fn new(iter: impl Iterator<Item = Solution> + 'static) -> Self {
Self {
iter: Box::new(iter),
}
}

pub fn empty() -> Self {
Self {
iter: Box::new(core::iter::empty()),
}
}
}

impl Iterator for Solutions {
type Item = Solution;

fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}

impl core::fmt::Debug for Solutions {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("Solutions").finish()
}
}
33 changes: 33 additions & 0 deletions lib/rdf-query/src/traits/queryable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// This is free and unencumbered software released into the public domain.
extern crate alloc;

use alloc::vec;
use rdf_model::Enumerable;

use crate::{graph_pattern::GraphPattern, pattern::Pattern, query::Query, solutions::Solutions};

pub trait Queryable: Enumerable {
fn query(&self, pattern: impl Into<GraphPattern>) -> Solutions
where
Self: Sized,
{
match pattern.into() {
GraphPattern::BasicGraphPattern(query) => self.query_execute(query),
GraphPattern::TriplePattern(pattern) => self.query_execute(Query::new(vec![pattern])),
}
}

fn query_execute(&self, query: Query) -> Solutions
where
Self: Sized,
{
query.execute(self)
}

fn query_pattern(&self, pattern: &Pattern) -> Self
where
Self: Sized,
{
self.grep(pattern)
}
}
Loading