Skip to main content

oxirs_core/format/
jsonld.rs

1//! JSON-LD Format Parser and Serializer
2//!
3//! Wrapper around the full JSON-LD implementation in oxirs-core/jsonld module.
4//! Based on W3C JSON-LD specifications: <https://www.w3.org/TR/json-ld/>
5
6use super::error::SerializeResult;
7use super::error::{ParseResult, RdfParseError};
8use super::format::JsonLdProfileSet;
9use super::serializer::QuadSerializer;
10use crate::jsonld;
11use crate::model::{Quad, QuadRef};
12use std::io::{Read, Write};
13
14/// JSON-LD parser implementation
15///
16/// This wraps the full JSON-LD parser from the jsonld module and provides
17/// a simplified interface for the format abstraction layer.
18// Removed Debug, Clone - inner types don't implement them
19pub struct JsonLdParser {
20    inner: jsonld::JsonLdParser,
21    profile: JsonLdProfileSet,
22}
23
24impl JsonLdParser {
25    /// Create a new JSON-LD parser
26    pub fn new() -> Self {
27        Self {
28            inner: jsonld::JsonLdParser::new(),
29            profile: JsonLdProfileSet::empty(),
30        }
31    }
32
33    /// Set the JSON-LD processing profile
34    pub fn with_profile(mut self, profile: JsonLdProfileSet) -> Self {
35        self.profile = profile.clone();
36        // Convert format::JsonLdProfileSet to jsonld::JsonLdProfileSet
37        let jsonld_profile_set = profile.to_jsonld_profile_set();
38        self.inner = self.inner.with_profile(jsonld_profile_set);
39        self
40    }
41
42    /// Set base IRI for resolving relative IRIs
43    pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, RdfParseError> {
44        let base_iri_str = base_iri.into();
45        self.inner = self
46            .inner
47            .with_base_iri(base_iri_str.clone())
48            .map_err(|e| RdfParseError::syntax(format!("Invalid base IRI: {e}")))?;
49        Ok(self)
50    }
51
52    /// Assume lenient parsing (skip some validations)
53    pub fn lenient(mut self) -> Self {
54        self.inner = self.inner.lenient();
55        self
56    }
57
58    /// Parse JSON-LD from a reader
59    pub fn parse_reader<R: Read>(&self, reader: R) -> ParseResult<Vec<Quad>> {
60        // Use the actual JSON-LD parser implementation
61        self.inner
62            .clone()
63            .for_reader(reader)
64            .collect::<Result<Vec<_>, _>>()
65            .map_err(|e| RdfParseError::syntax(format!("JSON-LD parse error: {e}")))
66    }
67
68    /// Parse JSON-LD from a byte slice
69    pub fn parse_slice(&self, slice: &[u8]) -> ParseResult<Vec<Quad>> {
70        // Use the actual JSON-LD parser implementation
71        self.inner
72            .clone()
73            .for_slice(slice)
74            .collect::<Result<Vec<_>, _>>()
75            .map_err(|e| RdfParseError::syntax(format!("JSON-LD parse error: {e}")))
76    }
77
78    /// Parse JSON-LD from a string
79    pub fn parse_str(&self, input: &str) -> ParseResult<Vec<Quad>> {
80        self.parse_slice(input.as_bytes())
81    }
82
83    /// Get the processing profile
84    pub fn profile(&self) -> &JsonLdProfileSet {
85        &self.profile
86    }
87}
88
89impl Default for JsonLdParser {
90    fn default() -> Self {
91        Self::new()
92    }
93}
94
95/// JSON-LD serializer implementation
96///
97/// This wraps the full JSON-LD serializer from the jsonld module and provides
98/// a simplified interface for the format abstraction layer.
99#[derive(Clone)]
100pub struct JsonLdSerializer {
101    inner: jsonld::JsonLdSerializer,
102    profile: JsonLdProfileSet,
103}
104
105impl JsonLdSerializer {
106    /// Create a new JSON-LD serializer
107    pub fn new() -> Self {
108        Self {
109            inner: jsonld::JsonLdSerializer::new(),
110            profile: JsonLdProfileSet::empty(),
111        }
112    }
113
114    /// Set the JSON-LD processing profile
115    pub fn with_profile(mut self, profile: JsonLdProfileSet) -> Self {
116        self.profile = profile;
117        self
118    }
119
120    /// Add a prefix to the serializer
121    pub fn with_prefix(
122        mut self,
123        prefix: impl Into<String>,
124        iri: impl Into<String>,
125    ) -> Result<Self, RdfParseError> {
126        self.inner = self
127            .inner
128            .with_prefix(prefix, iri)
129            .map_err(|e| RdfParseError::syntax(format!("Invalid prefix IRI: {e}")))?;
130        Ok(self)
131    }
132
133    /// Set base IRI for generating relative IRIs
134    pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, RdfParseError> {
135        self.inner = self
136            .inner
137            .with_base_iri(base_iri)
138            .map_err(|e| RdfParseError::syntax(format!("Invalid base IRI: {e}")))?;
139        Ok(self)
140    }
141
142    /// Enable pretty formatting (no-op for JSON-LD streaming serializer)
143    ///
144    /// The underlying JSON-LD serializer always produces compact streaming output.
145    /// This method is provided for API compatibility with other serializers.
146    pub fn pretty(self) -> Self {
147        // JSON-LD streaming serializer doesn't support pretty printing
148        self
149    }
150
151    /// Create a writer-based serializer
152    pub fn for_writer<W: Write>(self, writer: W) -> WriterJsonLdSerializer<W> {
153        WriterJsonLdSerializer::new(writer, self)
154    }
155
156    /// Serialize quads to a JSON-LD string
157    pub fn serialize_to_string(&self, quads: &[Quad]) -> SerializeResult<String> {
158        let mut buffer = Vec::new();
159        {
160            let mut serializer = self.clone().for_writer(&mut buffer);
161            for quad in quads {
162                serializer.serialize_quad(quad.as_ref())?;
163            }
164            serializer.finish()?;
165        }
166        String::from_utf8(buffer)
167            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
168    }
169
170    /// Get the processing profile
171    pub fn profile(&self) -> &JsonLdProfileSet {
172        &self.profile
173    }
174}
175
176impl Default for JsonLdSerializer {
177    fn default() -> Self {
178        Self::new()
179    }
180}
181
182/// Writer-based JSON-LD serializer
183///
184/// This wraps the actual JSON-LD writer serializer implementation
185pub struct WriterJsonLdSerializer<W: Write> {
186    inner: jsonld::WriterJsonLdSerializer<W>,
187}
188
189impl<W: Write> WriterJsonLdSerializer<W> {
190    /// Create a new writer serializer
191    pub fn new(writer: W, config: JsonLdSerializer) -> Self {
192        Self {
193            inner: config.inner.for_writer(writer),
194        }
195    }
196
197    /// Serialize a quad
198    pub fn serialize_quad(&mut self, quad: QuadRef<'_>) -> SerializeResult<()> {
199        self.inner
200            .serialize_quad(quad)
201            .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
202    }
203
204    /// Finish serialization and return the writer
205    pub fn finish(self) -> SerializeResult<W> {
206        Box::new(self.inner)
207            .finish()
208            .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
209    }
210}
211
212impl<W: Write> QuadSerializer<W> for WriterJsonLdSerializer<W> {
213    fn serialize_quad(&mut self, quad: QuadRef<'_>) -> SerializeResult<()> {
214        self.serialize_quad(quad)
215    }
216
217    fn finish(self: Box<Self>) -> SerializeResult<W> {
218        (*self).finish()
219    }
220}
221
222/// JSON-LD context management utilities
223///
224/// For advanced context operations, use the full jsonld module directly.
225/// This module provides basic re-exports for convenience.
226pub mod context {
227    /// Re-export context types from the main jsonld module
228    pub use crate::jsonld::{JsonLdLoadDocumentOptions, JsonLdRemoteDocument};
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234    use crate::format::format::{JsonLdProfile, JsonLdProfileSet};
235    use crate::model::*;
236    use crate::vocab::rdf;
237
238    #[test]
239    fn test_jsonld_parser_creation() {
240        let parser = JsonLdParser::new();
241        assert!(parser.profile().profiles().is_empty());
242    }
243
244    #[test]
245    fn test_jsonld_parser_configuration() {
246        let profile = JsonLdProfileSet::from_profile(JsonLdProfile::Expanded);
247
248        let parser = JsonLdParser::new()
249            .with_profile(profile.clone())
250            .with_base_iri("http://example.org/")
251            .expect("operation should succeed");
252
253        assert_eq!(parser.profile(), &profile);
254    }
255
256    #[test]
257    fn test_jsonld_serializer_creation() {
258        let serializer = JsonLdSerializer::new();
259        assert!(serializer.profile().profiles().is_empty());
260    }
261
262    #[test]
263    fn test_jsonld_serializer_configuration() {
264        let profile = JsonLdProfileSet::from_profile(JsonLdProfile::Compacted);
265
266        let serializer = JsonLdSerializer::new()
267            .with_profile(profile.clone())
268            .with_prefix("schema", "http://schema.org/")
269            .expect("operation should succeed")
270            .with_base_iri("http://example.org/")
271            .expect("operation should succeed");
272
273        assert_eq!(serializer.profile(), &profile);
274    }
275
276    #[test]
277    fn test_empty_json_parsing() {
278        let parser = JsonLdParser::new();
279        let result = parser.parse_str("{}");
280        assert!(result.is_ok());
281        // Empty JSON-LD documents might produce no quads
282    }
283
284    #[test]
285    fn test_invalid_json_parsing() {
286        let parser = JsonLdParser::new();
287        let result = parser.parse_str("invalid json");
288        assert!(result.is_err());
289    }
290
291    #[test]
292    fn test_jsonld_roundtrip() {
293        // Create a simple quad
294        let subject = NamedNode::new("http://example.org/subject").expect("valid IRI");
295        let predicate = rdf::TYPE.clone();
296        let object = NamedNode::new("http://example.org/Type").expect("valid IRI");
297        let quad = Quad::new(subject, predicate, object, GraphName::DefaultGraph);
298
299        // Serialize to JSON-LD
300        let serializer = JsonLdSerializer::new()
301            .with_prefix("ex", "http://example.org/")
302            .expect("operation should succeed");
303        let json_ld = serializer.serialize_to_string(std::slice::from_ref(&quad));
304        assert!(json_ld.is_ok());
305
306        // Parse back
307        let parser = JsonLdParser::new();
308        let parsed_quads = parser.parse_str(&json_ld.expect("JSON-LD should be valid"));
309        assert!(parsed_quads.is_ok());
310
311        // Verify we got at least one quad back
312        let quads = parsed_quads.expect("quad parsing should succeed");
313        assert!(!quads.is_empty());
314    }
315}