pub mod lexer;
pub mod parser;
#[cfg(test)]
mod tests;
pub use lexer::{TriGLexer, TriGToken};
pub use parser::TriGParser;
use std::io::{BufReader, Read};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum TriGParseError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Invalid token at line {line}: {message}")]
InvalidToken {
line: usize,
message: String,
},
#[error("Invalid triple at line {line}: {message}")]
InvalidTriple {
line: usize,
message: String,
},
#[error("Invalid graph at line {line}: {name}")]
InvalidGraph {
line: usize,
name: String,
},
#[error("Unclosed graph opened at line {opened_at}")]
UnclosedGraph {
opened_at: usize,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TriGLiteral {
pub value: String,
pub datatype: Option<String>,
pub language: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TriGTerm {
NamedNode(String),
BlankNode(String),
Literal(TriGLiteral),
}
impl TriGTerm {
pub fn as_iri(&self) -> Option<&str> {
match self {
Self::NamedNode(iri) => Some(iri),
_ => None,
}
}
pub fn as_blank(&self) -> Option<&str> {
match self {
Self::BlankNode(label) => Some(label),
_ => None,
}
}
pub fn as_literal(&self) -> Option<&TriGLiteral> {
match self {
Self::Literal(lit) => Some(lit),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StreamedQuad {
pub subject: TriGTerm,
pub predicate: TriGTerm,
pub object: TriGTerm,
pub graph_name: Option<TriGTerm>,
}
pub struct TriGStreamingParser<R: Read> {
inner: TriGParser<BufReader<R>>,
pending: Vec<StreamedQuad>,
done: bool,
}
impl<R: Read> TriGStreamingParser<R> {
pub fn new(reader: R) -> Self {
Self {
inner: TriGParser::new(BufReader::new(reader)),
pending: Vec::new(),
done: false,
}
}
}
impl<R: Read> Iterator for TriGStreamingParser<R> {
type Item = Result<StreamedQuad, TriGParseError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if !self.pending.is_empty() {
return Some(Ok(self.pending.remove(0)));
}
if self.done {
return None;
}
match self.inner.parse_statement() {
Ok(None) => {
self.done = true;
return None;
}
Ok(Some(quads)) => {
if quads.is_empty() {
continue; }
self.pending.extend(quads);
}
Err(e) => {
self.done = true;
return Some(Err(e));
}
}
}
}
}