use crate::oxrdf::{GraphNameRef, Triple, TripleRef};
use crate::oxttl::terse::TriGRecognizer;
#[cfg(feature = "async-tokio")]
use crate::oxttl::toolkit::FromTokioAsyncReadIterator;
use crate::oxttl::toolkit::{FromReadIterator, Parser, TurtleParseError, TurtleSyntaxError};
#[cfg(feature = "async-tokio")]
use crate::oxttl::trig::ToTokioAsyncWriteTriGWriter;
use crate::oxttl::trig::{LowLevelTriGWriter, ToWriteTriGWriter, TriGSerializer};
use oxiri::{Iri, IriParseError};
use std::collections::hash_map::Iter;
use std::collections::HashMap;
use std::io::{self, Read, Write};
#[cfg(feature = "async-tokio")]
use tokio::io::{AsyncRead, AsyncWrite};
#[derive(Default)]
#[must_use]
pub struct TurtleParser {
unchecked: bool,
base: Option<Iri<String>>,
prefixes: HashMap<String, Iri<String>>,
#[cfg(feature = "rdf-star")]
with_quoted_triples: bool,
}
impl TurtleParser {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn unchecked(mut self) -> Self {
self.unchecked = true;
self
}
#[inline]
pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, IriParseError> {
self.base = Some(Iri::parse(base_iri.into())?);
Ok(self)
}
#[inline]
pub fn with_prefix(
mut self,
prefix_name: impl Into<String>,
prefix_iri: impl Into<String>,
) -> Result<Self, IriParseError> {
self.prefixes
.insert(prefix_name.into(), Iri::parse(prefix_iri.into())?);
Ok(self)
}
#[cfg(feature = "rdf-star")]
#[inline]
pub fn with_quoted_triples(mut self) -> Self {
self.with_quoted_triples = true;
self
}
pub fn parse_read<R: Read>(self, read: R) -> FromReadTurtleReader<R> {
FromReadTurtleReader {
inner: self.parse().parser.parse_read(read),
}
}
#[cfg(feature = "async-tokio")]
pub fn parse_tokio_async_read<R: AsyncRead + Unpin>(
self,
read: R,
) -> FromTokioAsyncReadTurtleReader<R> {
FromTokioAsyncReadTurtleReader {
inner: self.parse().parser.parse_tokio_async_read(read),
}
}
pub fn parse(self) -> LowLevelTurtleReader {
LowLevelTurtleReader {
parser: TriGRecognizer::new_parser(
false,
#[cfg(feature = "rdf-star")]
self.with_quoted_triples,
self.unchecked,
self.base,
self.prefixes,
),
}
}
}
#[must_use]
pub struct FromReadTurtleReader<R: Read> {
inner: FromReadIterator<R, TriGRecognizer>,
}
impl<R: Read> FromReadTurtleReader<R> {
pub fn prefixes(&self) -> TurtlePrefixesIter<'_> {
TurtlePrefixesIter {
inner: self.inner.parser.context.prefixes(),
}
}
pub fn base_iri(&self) -> Option<&str> {
self.inner
.parser
.context
.lexer_options
.base_iri
.as_ref()
.map(Iri::as_str)
}
}
impl<R: Read> Iterator for FromReadTurtleReader<R> {
type Item = Result<Triple, TurtleParseError>;
fn next(&mut self) -> Option<Self::Item> {
Some(self.inner.next()?.map(Into::into))
}
}
#[cfg(feature = "async-tokio")]
#[must_use]
pub struct FromTokioAsyncReadTurtleReader<R: AsyncRead + Unpin> {
inner: FromTokioAsyncReadIterator<R, TriGRecognizer>,
}
#[cfg(feature = "async-tokio")]
impl<R: AsyncRead + Unpin> FromTokioAsyncReadTurtleReader<R> {
pub async fn next(&mut self) -> Option<Result<Triple, TurtleParseError>> {
Some(self.inner.next().await?.map(Into::into))
}
pub fn prefixes(&self) -> TurtlePrefixesIter<'_> {
TurtlePrefixesIter {
inner: self.inner.parser.context.prefixes(),
}
}
pub fn base_iri(&self) -> Option<&str> {
self.inner
.parser
.context
.lexer_options
.base_iri
.as_ref()
.map(Iri::as_str)
}
}
pub struct LowLevelTurtleReader {
parser: Parser<TriGRecognizer>,
}
impl LowLevelTurtleReader {
pub fn extend_from_slice(&mut self, other: &[u8]) {
self.parser.extend_from_slice(other)
}
pub fn end(&mut self) {
self.parser.end()
}
pub fn is_end(&self) -> bool {
self.parser.is_end()
}
pub fn read_next(&mut self) -> Option<Result<Triple, TurtleSyntaxError>> {
Some(self.parser.read_next()?.map(Into::into))
}
pub fn prefixes(&self) -> TurtlePrefixesIter<'_> {
TurtlePrefixesIter {
inner: self.parser.context.prefixes(),
}
}
pub fn base_iri(&self) -> Option<&str> {
self.parser
.context
.lexer_options
.base_iri
.as_ref()
.map(Iri::as_str)
}
}
pub struct TurtlePrefixesIter<'a> {
inner: Iter<'a, String, Iri<String>>,
}
impl<'a> Iterator for TurtlePrefixesIter<'a> {
type Item = (&'a str, &'a str);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let (key, value) = self.inner.next()?;
Some((key.as_str(), value.as_str()))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
#[derive(Default)]
#[must_use]
pub struct TurtleSerializer {
inner: TriGSerializer,
}
impl TurtleSerializer {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn with_prefix(
mut self,
prefix_name: impl Into<String>,
prefix_iri: impl Into<String>,
) -> Result<Self, IriParseError> {
self.inner = self.inner.with_prefix(prefix_name, prefix_iri)?;
Ok(self)
}
pub fn serialize_to_write<W: Write>(self, write: W) -> ToWriteTurtleWriter<W> {
ToWriteTurtleWriter {
inner: self.inner.serialize_to_write(write),
}
}
#[cfg(feature = "async-tokio")]
pub fn serialize_to_tokio_async_write<W: AsyncWrite + Unpin>(
self,
write: W,
) -> ToTokioAsyncWriteTurtleWriter<W> {
ToTokioAsyncWriteTurtleWriter {
inner: self.inner.serialize_to_tokio_async_write(write),
}
}
pub fn serialize(self) -> LowLevelTurtleWriter {
LowLevelTurtleWriter {
inner: self.inner.serialize(),
}
}
}
#[must_use]
pub struct ToWriteTurtleWriter<W: Write> {
inner: ToWriteTriGWriter<W>,
}
impl<W: Write> ToWriteTurtleWriter<W> {
pub fn write_triple<'a>(&mut self, t: impl Into<TripleRef<'a>>) -> io::Result<()> {
self.inner
.write_quad(t.into().in_graph(GraphNameRef::DefaultGraph))
}
pub fn finish(self) -> io::Result<W> {
self.inner.finish()
}
}
#[cfg(feature = "async-tokio")]
#[must_use]
pub struct ToTokioAsyncWriteTurtleWriter<W: AsyncWrite + Unpin> {
inner: ToTokioAsyncWriteTriGWriter<W>,
}
#[cfg(feature = "async-tokio")]
impl<W: AsyncWrite + Unpin> ToTokioAsyncWriteTurtleWriter<W> {
pub async fn write_triple<'a>(&mut self, t: impl Into<TripleRef<'a>>) -> io::Result<()> {
self.inner
.write_quad(t.into().in_graph(GraphNameRef::DefaultGraph))
.await
}
pub async fn finish(self) -> io::Result<W> {
self.inner.finish().await
}
}
pub struct LowLevelTurtleWriter {
inner: LowLevelTriGWriter,
}
impl LowLevelTurtleWriter {
pub fn write_triple<'a>(
&mut self,
t: impl Into<TripleRef<'a>>,
write: impl Write,
) -> io::Result<()> {
self.inner
.write_quad(t.into().in_graph(GraphNameRef::DefaultGraph), write)
}
pub fn finish(&mut self, write: impl Write) -> io::Result<()> {
self.inner.finish(write)
}
}
#[cfg(test)]
#[allow(clippy::panic_in_result_fn)]
mod tests {
use super::*;
use crate::oxrdf::{BlankNodeRef, LiteralRef, NamedNodeRef};
#[test]
fn test_write() -> io::Result<()> {
let mut writer = TurtleSerializer::new().serialize_to_write(Vec::new());
writer.write_triple(TripleRef::new(
NamedNodeRef::new_unchecked("http://example.com/s"),
NamedNodeRef::new_unchecked("http://example.com/p"),
NamedNodeRef::new_unchecked("http://example.com/o"),
))?;
writer.write_triple(TripleRef::new(
NamedNodeRef::new_unchecked("http://example.com/s"),
NamedNodeRef::new_unchecked("http://example.com/p"),
LiteralRef::new_simple_literal("foo"),
))?;
writer.write_triple(TripleRef::new(
NamedNodeRef::new_unchecked("http://example.com/s"),
NamedNodeRef::new_unchecked("http://example.com/p2"),
LiteralRef::new_language_tagged_literal_unchecked("foo", "en"),
))?;
writer.write_triple(TripleRef::new(
BlankNodeRef::new_unchecked("b"),
NamedNodeRef::new_unchecked("http://example.com/p2"),
BlankNodeRef::new_unchecked("b2"),
))?;
assert_eq!(String::from_utf8(writer.finish()?).unwrap(), "<http://example.com/s> <http://example.com/p> <http://example.com/o> , \"foo\" ;\n\t<http://example.com/p2> \"foo\"@en .\n_:b <http://example.com/p2> _:b2 .\n");
Ok(())
}
}