use super::error::SerializeResult;
use super::error::{ParseResult, RdfParseError};
use super::format::JsonLdProfileSet;
use super::serializer::QuadSerializer;
use crate::jsonld;
use crate::model::{Quad, QuadRef};
use std::io::{Read, Write};
pub struct JsonLdParser {
inner: jsonld::JsonLdParser,
profile: JsonLdProfileSet,
}
impl JsonLdParser {
pub fn new() -> Self {
Self {
inner: jsonld::JsonLdParser::new(),
profile: JsonLdProfileSet::empty(),
}
}
pub fn with_profile(mut self, profile: JsonLdProfileSet) -> Self {
self.profile = profile.clone();
let jsonld_profile_set = profile.to_jsonld_profile_set();
self.inner = self.inner.with_profile(jsonld_profile_set);
self
}
pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, RdfParseError> {
let base_iri_str = base_iri.into();
self.inner = self
.inner
.with_base_iri(base_iri_str.clone())
.map_err(|e| RdfParseError::syntax(format!("Invalid base IRI: {e}")))?;
Ok(self)
}
pub fn lenient(mut self) -> Self {
self.inner = self.inner.lenient();
self
}
pub fn parse_reader<R: Read>(&self, reader: R) -> ParseResult<Vec<Quad>> {
self.inner
.clone()
.for_reader(reader)
.collect::<Result<Vec<_>, _>>()
.map_err(|e| RdfParseError::syntax(format!("JSON-LD parse error: {e}")))
}
pub fn parse_slice(&self, slice: &[u8]) -> ParseResult<Vec<Quad>> {
self.inner
.clone()
.for_slice(slice)
.collect::<Result<Vec<_>, _>>()
.map_err(|e| RdfParseError::syntax(format!("JSON-LD parse error: {e}")))
}
pub fn parse_str(&self, input: &str) -> ParseResult<Vec<Quad>> {
self.parse_slice(input.as_bytes())
}
pub fn profile(&self) -> &JsonLdProfileSet {
&self.profile
}
}
impl Default for JsonLdParser {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone)]
pub struct JsonLdSerializer {
inner: jsonld::JsonLdSerializer,
profile: JsonLdProfileSet,
}
impl JsonLdSerializer {
pub fn new() -> Self {
Self {
inner: jsonld::JsonLdSerializer::new(),
profile: JsonLdProfileSet::empty(),
}
}
pub fn with_profile(mut self, profile: JsonLdProfileSet) -> Self {
self.profile = profile;
self
}
pub fn with_prefix(
mut self,
prefix: impl Into<String>,
iri: impl Into<String>,
) -> Result<Self, RdfParseError> {
self.inner = self
.inner
.with_prefix(prefix, iri)
.map_err(|e| RdfParseError::syntax(format!("Invalid prefix IRI: {e}")))?;
Ok(self)
}
pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, RdfParseError> {
self.inner = self
.inner
.with_base_iri(base_iri)
.map_err(|e| RdfParseError::syntax(format!("Invalid base IRI: {e}")))?;
Ok(self)
}
pub fn pretty(self) -> Self {
self
}
pub fn for_writer<W: Write>(self, writer: W) -> WriterJsonLdSerializer<W> {
WriterJsonLdSerializer::new(writer, self)
}
pub fn serialize_to_string(&self, quads: &[Quad]) -> SerializeResult<String> {
let mut buffer = Vec::new();
{
let mut serializer = self.clone().for_writer(&mut buffer);
for quad in quads {
serializer.serialize_quad(quad.as_ref())?;
}
serializer.finish()?;
}
String::from_utf8(buffer)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
}
pub fn profile(&self) -> &JsonLdProfileSet {
&self.profile
}
}
impl Default for JsonLdSerializer {
fn default() -> Self {
Self::new()
}
}
pub struct WriterJsonLdSerializer<W: Write> {
inner: jsonld::WriterJsonLdSerializer<W>,
}
impl<W: Write> WriterJsonLdSerializer<W> {
pub fn new(writer: W, config: JsonLdSerializer) -> Self {
Self {
inner: config.inner.for_writer(writer),
}
}
pub fn serialize_quad(&mut self, quad: QuadRef<'_>) -> SerializeResult<()> {
self.inner
.serialize_quad(quad)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
}
pub fn finish(self) -> SerializeResult<W> {
Box::new(self.inner)
.finish()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
}
}
impl<W: Write> QuadSerializer<W> for WriterJsonLdSerializer<W> {
fn serialize_quad(&mut self, quad: QuadRef<'_>) -> SerializeResult<()> {
self.serialize_quad(quad)
}
fn finish(self: Box<Self>) -> SerializeResult<W> {
(*self).finish()
}
}
pub mod context {
pub use crate::jsonld::{JsonLdLoadDocumentOptions, JsonLdRemoteDocument};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::format::format::{JsonLdProfile, JsonLdProfileSet};
use crate::model::*;
use crate::vocab::rdf;
#[test]
fn test_jsonld_parser_creation() {
let parser = JsonLdParser::new();
assert!(parser.profile().profiles().is_empty());
}
#[test]
fn test_jsonld_parser_configuration() {
let profile = JsonLdProfileSet::from_profile(JsonLdProfile::Expanded);
let parser = JsonLdParser::new()
.with_profile(profile.clone())
.with_base_iri("http://example.org/")
.expect("operation should succeed");
assert_eq!(parser.profile(), &profile);
}
#[test]
fn test_jsonld_serializer_creation() {
let serializer = JsonLdSerializer::new();
assert!(serializer.profile().profiles().is_empty());
}
#[test]
fn test_jsonld_serializer_configuration() {
let profile = JsonLdProfileSet::from_profile(JsonLdProfile::Compacted);
let serializer = JsonLdSerializer::new()
.with_profile(profile.clone())
.with_prefix("schema", "http://schema.org/")
.expect("operation should succeed")
.with_base_iri("http://example.org/")
.expect("operation should succeed");
assert_eq!(serializer.profile(), &profile);
}
#[test]
fn test_empty_json_parsing() {
let parser = JsonLdParser::new();
let result = parser.parse_str("{}");
assert!(result.is_ok());
}
#[test]
fn test_invalid_json_parsing() {
let parser = JsonLdParser::new();
let result = parser.parse_str("invalid json");
assert!(result.is_err());
}
#[test]
fn test_jsonld_roundtrip() {
let subject = NamedNode::new("http://example.org/subject").expect("valid IRI");
let predicate = rdf::TYPE.clone();
let object = NamedNode::new("http://example.org/Type").expect("valid IRI");
let quad = Quad::new(subject, predicate, object, GraphName::DefaultGraph);
let serializer = JsonLdSerializer::new()
.with_prefix("ex", "http://example.org/")
.expect("operation should succeed");
let json_ld = serializer.serialize_to_string(std::slice::from_ref(&quad));
assert!(json_ld.is_ok());
let parser = JsonLdParser::new();
let parsed_quads = parser.parse_str(&json_ld.expect("JSON-LD should be valid"));
assert!(parsed_quads.is_ok());
let quads = parsed_quads.expect("quad parsing should succeed");
assert!(!quads.is_empty());
}
}