oxrdfio/parser.rs
1//! Utilities to read RDF graphs and datasets.
2
3pub use crate::error::RdfParseError;
4use crate::format::RdfFormat;
5use crate::{LoadedDocument, RdfSyntaxError};
6#[cfg(feature = "async-tokio")]
7use oxjsonld::TokioAsyncReaderJsonLdParser;
8use oxjsonld::{
9 JsonLdParser, JsonLdPrefixesIter, JsonLdProfileSet, JsonLdRemoteDocument, ReaderJsonLdParser,
10 SliceJsonLdParser,
11};
12use oxrdf::{BlankNode, GraphName, IriParseError, NamedOrBlankNode, Quad, Term, Triple};
13#[cfg(feature = "async-tokio")]
14use oxrdfxml::TokioAsyncReaderRdfXmlParser;
15use oxrdfxml::{RdfXmlParser, RdfXmlPrefixesIter, ReaderRdfXmlParser, SliceRdfXmlParser};
16#[cfg(feature = "async-tokio")]
17use oxttl::n3::TokioAsyncReaderN3Parser;
18use oxttl::n3::{N3Parser, N3PrefixesIter, N3Quad, N3Term, ReaderN3Parser, SliceN3Parser};
19#[cfg(feature = "async-tokio")]
20use oxttl::nquads::TokioAsyncReaderNQuadsParser;
21use oxttl::nquads::{NQuadsParser, ReaderNQuadsParser, SliceNQuadsParser};
22#[cfg(feature = "async-tokio")]
23use oxttl::ntriples::TokioAsyncReaderNTriplesParser;
24use oxttl::ntriples::{NTriplesParser, ReaderNTriplesParser, SliceNTriplesParser};
25#[cfg(feature = "async-tokio")]
26use oxttl::trig::TokioAsyncReaderTriGParser;
27use oxttl::trig::{ReaderTriGParser, SliceTriGParser, TriGParser, TriGPrefixesIter};
28#[cfg(feature = "async-tokio")]
29use oxttl::turtle::TokioAsyncReaderTurtleParser;
30use oxttl::turtle::{ReaderTurtleParser, SliceTurtleParser, TurtleParser, TurtlePrefixesIter};
31use std::collections::HashMap;
32use std::error::Error;
33use std::fs::File;
34use std::io;
35use std::io::{Read, Take};
36use std::panic::{RefUnwindSafe, UnwindSafe};
37use std::path::Path;
38#[cfg(feature = "async-tokio")]
39use tokio::io::AsyncRead;
40
41/// Parsers for RDF serialization formats.
42///
43/// It currently supports the following formats:
44/// * [JSON-LD](https://www.w3.org/TR/json-ld/) ([`RdfFormat::JsonLd`])
45/// * [N3](https://w3c.github.io/N3/spec/) ([`RdfFormat::N3`])
46/// * [N-Quads](https://www.w3.org/TR/n-quads/) ([`RdfFormat::NQuads`])
47/// * [N-Triples](https://www.w3.org/TR/n-triples/) ([`RdfFormat::NTriples`])
48/// * [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/) ([`RdfFormat::RdfXml`])
49/// * [TriG](https://www.w3.org/TR/trig/) ([`RdfFormat::TriG`])
50/// * [Turtle](https://www.w3.org/TR/turtle/) ([`RdfFormat::Turtle`])
51///
52/// Note the useful options:
53/// - [`with_base_iri`](Self::with_base_iri) to resolve the relative IRIs.
54/// - [`rename_blank_nodes`](Self::rename_blank_nodes) to rename the blank nodes to auto-generated numbers to avoid conflicts when merging RDF graphs together.
55/// - [`without_named_graphs`](Self::without_named_graphs) to parse a single graph.
56/// - [`unchecked`](Self::unchecked) to skip some validations if the file is already known to be valid.
57///
58/// ```
59/// use oxrdfio::{RdfFormat, RdfParser};
60///
61/// let file = "<http://example.com/s> <http://example.com/p> <http://example.com/o> .";
62///
63/// let quads = RdfParser::from_format(RdfFormat::NTriples)
64/// .for_reader(file.as_bytes())
65/// .collect::<Result<Vec<_>, _>>()?;
66///
67/// assert_eq!(quads.len(), 1);
68/// assert_eq!(quads[0].subject.to_string(), "<http://example.com/s>");
69/// # std::io::Result::Ok(())
70/// ```
71#[must_use]
72#[derive(Clone)]
73pub struct RdfParser {
74 inner: RdfParserKind,
75 default_graph: GraphName,
76 without_named_graphs: bool,
77 rename_blank_nodes: bool,
78}
79
80#[derive(Clone)]
81enum RdfParserKind {
82 JsonLd(JsonLdParser, JsonLdProfileSet),
83 N3(N3Parser),
84 NQuads(NQuadsParser),
85 NTriples(NTriplesParser),
86 RdfXml(RdfXmlParser),
87 TriG(TriGParser),
88 Turtle(TurtleParser),
89}
90
91impl RdfParser {
92 /// Builds a parser for the given format.
93 #[inline]
94 pub fn from_format(format: RdfFormat) -> Self {
95 Self {
96 inner: match format {
97 RdfFormat::JsonLd { profile } => {
98 RdfParserKind::JsonLd(JsonLdParser::new().with_profile(profile), profile)
99 }
100 RdfFormat::N3 => RdfParserKind::N3(N3Parser::new()),
101 RdfFormat::NQuads => RdfParserKind::NQuads(NQuadsParser::new()),
102 RdfFormat::NTriples => RdfParserKind::NTriples(NTriplesParser::new()),
103 RdfFormat::RdfXml => RdfParserKind::RdfXml(RdfXmlParser::new()),
104 RdfFormat::TriG => RdfParserKind::TriG(TriGParser::new()),
105 RdfFormat::Turtle => RdfParserKind::Turtle(TurtleParser::new()),
106 },
107 default_graph: GraphName::DefaultGraph,
108 without_named_graphs: false,
109 rename_blank_nodes: false,
110 }
111 }
112
113 /// The format the parser uses.
114 ///
115 /// ```
116 /// use oxrdfio::{RdfFormat, RdfParser};
117 ///
118 /// assert_eq!(
119 /// RdfParser::from_format(RdfFormat::Turtle).format(),
120 /// RdfFormat::Turtle
121 /// );
122 /// ```
123 pub fn format(&self) -> RdfFormat {
124 match &self.inner {
125 RdfParserKind::JsonLd(_, profile) => RdfFormat::JsonLd { profile: *profile },
126 RdfParserKind::N3(_) => RdfFormat::N3,
127 RdfParserKind::NQuads(_) => RdfFormat::NQuads,
128 RdfParserKind::NTriples(_) => RdfFormat::NTriples,
129 RdfParserKind::RdfXml(_) => RdfFormat::RdfXml,
130 RdfParserKind::TriG(_) => RdfFormat::TriG,
131 RdfParserKind::Turtle(_) => RdfFormat::Turtle,
132 }
133 }
134
135 /// Provides an IRI that could be used to resolve the file relative IRIs.
136 ///
137 /// ```
138 /// use oxrdfio::{RdfFormat, RdfParser};
139 ///
140 /// let file = "</s> </p> </o> .";
141 ///
142 /// let quads = RdfParser::from_format(RdfFormat::Turtle)
143 /// .with_base_iri("http://example.com")?
144 /// .for_reader(file.as_bytes())
145 /// .collect::<Result<Vec<_>, _>>()?;
146 ///
147 /// assert_eq!(quads.len(), 1);
148 /// assert_eq!(quads[0].subject.to_string(), "<http://example.com/s>");
149 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
150 /// ```
151 #[inline]
152 pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, IriParseError> {
153 self.inner = match self.inner {
154 RdfParserKind::JsonLd(p, f) => RdfParserKind::JsonLd(p.with_base_iri(base_iri)?, f),
155 RdfParserKind::N3(p) => RdfParserKind::N3(p.with_base_iri(base_iri)?),
156 RdfParserKind::NTriples(p) => RdfParserKind::NTriples(p),
157 RdfParserKind::NQuads(p) => RdfParserKind::NQuads(p),
158 RdfParserKind::RdfXml(p) => RdfParserKind::RdfXml(p.with_base_iri(base_iri)?),
159 RdfParserKind::TriG(p) => RdfParserKind::TriG(p.with_base_iri(base_iri)?),
160 RdfParserKind::Turtle(p) => RdfParserKind::Turtle(p.with_base_iri(base_iri)?),
161 };
162 Ok(self)
163 }
164
165 /// Provides the name graph name that should replace the default graph in the returned quads.
166 ///
167 /// ```
168 /// use oxrdf::NamedNode;
169 /// use oxrdfio::{RdfFormat, RdfParser};
170 ///
171 /// let file = "<http://example.com/s> <http://example.com/p> <http://example.com/o> .";
172 ///
173 /// let quads = RdfParser::from_format(RdfFormat::Turtle)
174 /// .with_default_graph(NamedNode::new("http://example.com/g")?)
175 /// .for_reader(file.as_bytes())
176 /// .collect::<Result<Vec<_>, _>>()?;
177 ///
178 /// assert_eq!(quads.len(), 1);
179 /// assert_eq!(quads[0].graph_name.to_string(), "<http://example.com/g>");
180 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
181 /// ```
182 #[inline]
183 pub fn with_default_graph(mut self, default_graph: impl Into<GraphName>) -> Self {
184 self.default_graph = default_graph.into();
185 self
186 }
187
188 /// Sets that the parser must fail if parsing a named graph.
189 ///
190 /// This function restricts the parser to only parse a single [RDF graph](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-graph) and not an [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset).
191 ///
192 /// ```
193 /// use oxrdfio::{RdfFormat, RdfParser};
194 ///
195 /// let file = "<http://example.com/s> <http://example.com/p> <http://example.com/o> <http://example.com/g> .";
196 ///
197 /// let parser = RdfParser::from_format(RdfFormat::NQuads).without_named_graphs();
198 /// assert!(parser.for_reader(file.as_bytes()).next().unwrap().is_err());
199 /// ```
200 #[inline]
201 pub fn without_named_graphs(mut self) -> Self {
202 self.without_named_graphs = true;
203 self
204 }
205
206 /// Renames the blank nodes ids from the ones set in the serialization to random ids.
207 ///
208 /// This allows to avoid id conflicts when merging graphs together.
209 ///
210 /// ```
211 /// use oxrdfio::{RdfFormat, RdfParser};
212 ///
213 /// let file = "_:a <http://example.com/p> <http://example.com/o> .";
214 ///
215 /// let result1 = RdfParser::from_format(RdfFormat::NQuads)
216 /// .rename_blank_nodes()
217 /// .for_reader(file.as_bytes())
218 /// .collect::<Result<Vec<_>, _>>()?;
219 /// let result2 = RdfParser::from_format(RdfFormat::NQuads)
220 /// .rename_blank_nodes()
221 /// .for_reader(file.as_bytes())
222 /// .collect::<Result<Vec<_>, _>>()?;
223 /// assert_ne!(result1, result2);
224 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
225 /// ```
226 #[inline]
227 pub fn rename_blank_nodes(mut self) -> Self {
228 self.rename_blank_nodes = true;
229 self
230 }
231
232 /// Assumes the file is valid to make parsing faster.
233 ///
234 /// It will skip some validations.
235 ///
236 /// Note that if the file is actually not valid, the parser might emit broken RDF.
237 #[inline]
238 pub fn lenient(mut self) -> Self {
239 self.inner = match self.inner {
240 RdfParserKind::JsonLd(p, f) => RdfParserKind::JsonLd(p.lenient(), f),
241 RdfParserKind::N3(p) => RdfParserKind::N3(p.lenient()),
242 RdfParserKind::NTriples(p) => RdfParserKind::NTriples(p.lenient()),
243 RdfParserKind::NQuads(p) => RdfParserKind::NQuads(p.lenient()),
244 RdfParserKind::RdfXml(p) => RdfParserKind::RdfXml(p.lenient()),
245 RdfParserKind::TriG(p) => RdfParserKind::TriG(p.lenient()),
246 RdfParserKind::Turtle(p) => RdfParserKind::Turtle(p.lenient()),
247 };
248 self
249 }
250
251 #[deprecated(note = "Use `lenient()` instead", since = "0.2.0")]
252 #[inline]
253 pub fn unchecked(self) -> Self {
254 self.lenient()
255 }
256
257 /// Parses from a [`Read`] implementation and returns an iterator of quads.
258 ///
259 /// Reads are buffered.
260 ///
261 /// ```
262 /// use oxrdfio::{RdfFormat, RdfParser};
263 ///
264 /// let file = "<http://example.com/s> <http://example.com/p> <http://example.com/o> .";
265 ///
266 /// let quads = RdfParser::from_format(RdfFormat::NTriples)
267 /// .for_reader(file.as_bytes())
268 /// .collect::<Result<Vec<_>, _>>()?;
269 ///
270 /// assert_eq!(quads.len(), 1);
271 /// assert_eq!(quads[0].subject.to_string(), "<http://example.com/s>");
272 /// # std::io::Result::Ok(())
273 /// ```
274 pub fn for_reader<R: Read>(self, reader: R) -> ReaderQuadParser<R> {
275 ReaderQuadParser {
276 inner: match self.inner {
277 RdfParserKind::JsonLd(p, _) => ReaderQuadParserKind::JsonLd(p.for_reader(reader)),
278 RdfParserKind::N3(p) => ReaderQuadParserKind::N3(p.for_reader(reader)),
279 RdfParserKind::NQuads(p) => ReaderQuadParserKind::NQuads(p.for_reader(reader)),
280 RdfParserKind::NTriples(p) => ReaderQuadParserKind::NTriples(p.for_reader(reader)),
281 RdfParserKind::RdfXml(p) => ReaderQuadParserKind::RdfXml(p.for_reader(reader)),
282 RdfParserKind::TriG(p) => ReaderQuadParserKind::TriG(p.for_reader(reader)),
283 RdfParserKind::Turtle(p) => ReaderQuadParserKind::Turtle(p.for_reader(reader)),
284 },
285 mapper: QuadMapper {
286 default_graph: self.default_graph,
287 without_named_graphs: self.without_named_graphs,
288 blank_node_map: self.rename_blank_nodes.then(HashMap::new),
289 },
290 }
291 }
292
293 /// Parses from a Tokio [`AsyncRead`] implementation and returns an async iterator of quads.
294 ///
295 /// Reads are buffered.
296 ///
297 /// ```
298 /// # #[tokio::main(flavor = "current_thread")]
299 /// # async fn main() -> Result<(), oxrdfio::RdfParseError> {
300 /// use oxrdfio::{RdfFormat, RdfParser};
301 ///
302 /// let file = "<http://example.com/s> <http://example.com/p> <http://example.com/o> .";
303 ///
304 /// let mut parser =
305 /// RdfParser::from_format(RdfFormat::NTriples).for_tokio_async_reader(file.as_bytes());
306 /// if let Some(quad) = parser.next().await {
307 /// assert_eq!(quad?.subject.to_string(), "<http://example.com/s>");
308 /// }
309 /// # Ok(())
310 /// # }
311 /// ```
312 #[cfg(feature = "async-tokio")]
313 pub fn for_tokio_async_reader<R: AsyncRead + Unpin>(
314 self,
315 reader: R,
316 ) -> TokioAsyncReaderQuadParser<R> {
317 TokioAsyncReaderQuadParser {
318 inner: match self.inner {
319 RdfParserKind::JsonLd(p, _) => {
320 TokioAsyncReaderQuadParserKind::JsonLd(p.for_tokio_async_reader(reader))
321 }
322 RdfParserKind::N3(p) => {
323 TokioAsyncReaderQuadParserKind::N3(p.for_tokio_async_reader(reader))
324 }
325 RdfParserKind::NQuads(p) => {
326 TokioAsyncReaderQuadParserKind::NQuads(p.for_tokio_async_reader(reader))
327 }
328 RdfParserKind::NTriples(p) => {
329 TokioAsyncReaderQuadParserKind::NTriples(p.for_tokio_async_reader(reader))
330 }
331 RdfParserKind::RdfXml(p) => {
332 TokioAsyncReaderQuadParserKind::RdfXml(p.for_tokio_async_reader(reader))
333 }
334 RdfParserKind::TriG(p) => {
335 TokioAsyncReaderQuadParserKind::TriG(p.for_tokio_async_reader(reader))
336 }
337 RdfParserKind::Turtle(p) => {
338 TokioAsyncReaderQuadParserKind::Turtle(p.for_tokio_async_reader(reader))
339 }
340 },
341 mapper: QuadMapper {
342 default_graph: self.default_graph,
343 without_named_graphs: self.without_named_graphs,
344 blank_node_map: self.rename_blank_nodes.then(HashMap::new),
345 },
346 }
347 }
348
349 /// Parses from a byte slice and returns an iterator of quads.
350 ///
351 /// ```
352 /// use oxrdfio::{RdfFormat, RdfParser};
353 ///
354 /// let file = "<http://example.com/s> <http://example.com/p> <http://example.com/o> .";
355 ///
356 /// let quads = RdfParser::from_format(RdfFormat::NTriples)
357 /// .for_slice(file)
358 /// .collect::<Result<Vec<_>, _>>()?;
359 ///
360 /// assert_eq!(quads.len(), 1);
361 /// assert_eq!(quads[0].subject.to_string(), "<http://example.com/s>");
362 /// # std::io::Result::Ok(())
363 /// ```
364 pub fn for_slice(self, slice: &(impl AsRef<[u8]> + ?Sized)) -> SliceQuadParser<'_> {
365 SliceQuadParser {
366 inner: match self.inner {
367 RdfParserKind::JsonLd(p, _) => SliceQuadParserKind::JsonLd(p.for_slice(slice)),
368 RdfParserKind::N3(p) => SliceQuadParserKind::N3(p.for_slice(slice)),
369 RdfParserKind::NQuads(p) => SliceQuadParserKind::NQuads(p.for_slice(slice)),
370 RdfParserKind::NTriples(p) => SliceQuadParserKind::NTriples(p.for_slice(slice)),
371 RdfParserKind::RdfXml(p) => SliceQuadParserKind::RdfXml(p.for_slice(slice)),
372 RdfParserKind::TriG(p) => SliceQuadParserKind::TriG(p.for_slice(slice)),
373 RdfParserKind::Turtle(p) => SliceQuadParserKind::Turtle(p.for_slice(slice)),
374 },
375 mapper: QuadMapper {
376 default_graph: self.default_graph,
377 without_named_graphs: self.without_named_graphs,
378 blank_node_map: self.rename_blank_nodes.then(HashMap::new),
379 },
380 }
381 }
382
383 /// Creates a vector of parsers that may be used to parse the document slice in parallel.
384 /// To dynamically specify target_parallelism, use e.g. [`std::thread::available_parallelism`].
385 ///
386 /// This only works for N-Triples and N-Quads and is only interesting on large documents.
387 ///
388 ///
389 /// ```
390 /// use oxrdfio::{RdfFormat, RdfParser};
391 ///
392 /// let file = "<http://example.com/s> <http://example.com/p> <http://example.com/o> .";
393 ///
394 /// let quads = RdfParser::from_format(RdfFormat::NTriples)
395 /// .split_slice_for_parallel_parsing(file, 4)
396 /// .into_iter()
397 /// .flatten()
398 /// .collect::<Result<Vec<_>, _>>()?;
399 ///
400 /// assert_eq!(quads.len(), 1);
401 /// assert_eq!(quads[0].subject.to_string(), "<http://example.com/s>");
402 /// # std::io::Result::Ok(())
403 /// ```
404 pub fn split_slice_for_parallel_parsing(
405 self,
406 slice: &(impl AsRef<[u8]> + ?Sized),
407 target_parallelism: usize,
408 ) -> Vec<SliceQuadParser<'_>> {
409 match self.inner {
410 RdfParserKind::NTriples(p) => p
411 .split_slice_for_parallel_parsing(slice, target_parallelism)
412 .into_iter()
413 .map(|p| SliceQuadParser {
414 inner: SliceQuadParserKind::NTriples(p),
415 mapper: QuadMapper {
416 default_graph: self.default_graph.clone(),
417 without_named_graphs: self.without_named_graphs,
418 blank_node_map: self.rename_blank_nodes.then(HashMap::new),
419 },
420 })
421 .collect(),
422 RdfParserKind::NQuads(p) => p
423 .split_slice_for_parallel_parsing(slice, target_parallelism)
424 .into_iter()
425 .map(|p| SliceQuadParser {
426 inner: SliceQuadParserKind::NQuads(p),
427 mapper: QuadMapper {
428 default_graph: self.default_graph.clone(),
429 without_named_graphs: self.without_named_graphs,
430 blank_node_map: self.rename_blank_nodes.then(HashMap::new),
431 },
432 })
433 .collect(),
434 _ => vec![self.for_slice(slice)],
435 }
436 }
437
438 /// Creates a vector of parsers that may be used to parse the file in parallel.
439 /// To dynamically specify target_parallelism, use e.g. [`std::thread::available_parallelism`].
440 ///
441 /// This only works for N-Triples and N-Quads and is only interesting on large documents.
442 ///
443 /// ```no_run
444 /// use oxrdfio::{RdfFormat, RdfParser};
445 /// # let path = tempfile::NamedTempFile::new()?;
446 /// # std::fs::write(&path, "<http://example.com/s> <http://example.com/p> <http://example.com/o> .")?;
447 ///
448 /// let quads = RdfParser::from_format(RdfFormat::NTriples)
449 /// .split_file_for_parallel_parsing(&path, 4)?
450 /// .into_iter()
451 /// .flatten()
452 /// .collect::<Result<Vec<_>, _>>()?;
453 ///
454 /// assert_eq!(quads.len(), 1);
455 /// assert_eq!(quads[0].subject.to_string(), "<http://example.com/s>");
456 /// # std::io::Result::Ok(())
457 /// ```
458 pub fn split_file_for_parallel_parsing(
459 self,
460 path: impl AsRef<Path>,
461 target_parallelism: usize,
462 ) -> io::Result<Vec<ReaderQuadParser<Take<File>>>> {
463 Ok(match self.inner {
464 RdfParserKind::NTriples(p) => p
465 .split_file_for_parallel_parsing(path, target_parallelism)?
466 .into_iter()
467 .map(|p| ReaderQuadParser {
468 inner: ReaderQuadParserKind::NTriples(p),
469 mapper: QuadMapper {
470 default_graph: self.default_graph.clone(),
471 without_named_graphs: self.without_named_graphs,
472 blank_node_map: self.rename_blank_nodes.then(HashMap::new),
473 },
474 })
475 .collect(),
476 RdfParserKind::NQuads(p) => p
477 .split_file_for_parallel_parsing(path, target_parallelism)?
478 .into_iter()
479 .map(|p| ReaderQuadParser {
480 inner: ReaderQuadParserKind::NQuads(p),
481 mapper: QuadMapper {
482 default_graph: self.default_graph.clone(),
483 without_named_graphs: self.without_named_graphs,
484 blank_node_map: self.rename_blank_nodes.then(HashMap::new),
485 },
486 })
487 .collect(),
488 _ => vec![self.for_reader(File::open(path)?.take(u64::MAX))],
489 })
490 }
491}
492
493impl From<RdfFormat> for RdfParser {
494 fn from(format: RdfFormat) -> Self {
495 Self::from_format(format)
496 }
497}
498
499/// Parses a RDF file from a [`Read`] implementation.
500///
501/// Can be built using [`RdfParser::for_reader`].
502///
503/// Reads are buffered.
504///
505/// ```
506/// use oxrdfio::{RdfFormat, RdfParser};
507///
508/// let file = "<http://example.com/s> <http://example.com/p> <http://example.com/o> .";
509///
510/// let quads = RdfParser::from_format(RdfFormat::NTriples)
511/// .for_reader(file.as_bytes())
512/// .collect::<Result<Vec<_>, _>>()?;
513///
514/// assert_eq!(quads.len(), 1);
515/// assert_eq!(quads[0].subject.to_string(), "<http://example.com/s>");
516/// # std::io::Result::Ok(())
517/// ```
518#[must_use]
519pub struct ReaderQuadParser<R: Read> {
520 inner: ReaderQuadParserKind<R>,
521 mapper: QuadMapper,
522}
523
524enum ReaderQuadParserKind<R: Read> {
525 JsonLd(ReaderJsonLdParser<R>),
526 N3(ReaderN3Parser<R>),
527 NQuads(ReaderNQuadsParser<R>),
528 NTriples(ReaderNTriplesParser<R>),
529 RdfXml(ReaderRdfXmlParser<R>),
530 TriG(ReaderTriGParser<R>),
531 Turtle(ReaderTurtleParser<R>),
532}
533
534impl<R: Read> Iterator for ReaderQuadParser<R> {
535 type Item = Result<Quad, RdfParseError>;
536
537 fn next(&mut self) -> Option<Self::Item> {
538 Some(match &mut self.inner {
539 ReaderQuadParserKind::JsonLd(parser) => match parser.next()? {
540 Ok(quad) => self.mapper.map_quad(quad).map_err(Into::into),
541 Err(e) => Err(e.into()),
542 },
543 ReaderQuadParserKind::N3(parser) => match parser.next()? {
544 Ok(quad) => self.mapper.map_n3_quad(quad).map_err(Into::into),
545 Err(e) => Err(e.into()),
546 },
547 ReaderQuadParserKind::NQuads(parser) => match parser.next()? {
548 Ok(quad) => self.mapper.map_quad(quad).map_err(Into::into),
549 Err(e) => Err(e.into()),
550 },
551 ReaderQuadParserKind::NTriples(parser) => match parser.next()? {
552 Ok(triple) => Ok(self.mapper.map_triple_to_quad(triple)),
553 Err(e) => Err(e.into()),
554 },
555 ReaderQuadParserKind::RdfXml(parser) => match parser.next()? {
556 Ok(triple) => Ok(self.mapper.map_triple_to_quad(triple)),
557 Err(e) => Err(e.into()),
558 },
559 ReaderQuadParserKind::TriG(parser) => match parser.next()? {
560 Ok(quad) => self.mapper.map_quad(quad).map_err(Into::into),
561 Err(e) => Err(e.into()),
562 },
563 ReaderQuadParserKind::Turtle(parser) => match parser.next()? {
564 Ok(triple) => Ok(self.mapper.map_triple_to_quad(triple)),
565 Err(e) => Err(e.into()),
566 },
567 })
568 }
569}
570
571impl<R: Read> ReaderQuadParser<R> {
572 /// The list of IRI prefixes considered at the current step of the parsing.
573 ///
574 /// This method returns (prefix name, prefix value) tuples.
575 /// It is empty at the beginning of the parsing and gets updated when prefixes are encountered.
576 /// It should be full at the end of the parsing (but if a prefix is overridden, only the latest version will be returned).
577 ///
578 /// An empty iterator is return if the format does not support prefixes.
579 ///
580 /// ```
581 /// use oxrdfio::{RdfFormat, RdfParser};
582 ///
583 /// let file = r#"@base <http://example.com/> .
584 /// @prefix schema: <http://schema.org/> .
585 /// <foo> a schema:Person ;
586 /// schema:name "Foo" ."#;
587 ///
588 /// let mut parser = RdfParser::from_format(RdfFormat::Turtle).for_reader(file.as_bytes());
589 /// assert!(parser.prefixes().collect::<Vec<_>>().is_empty()); // No prefix at the beginning
590 ///
591 /// parser.next().unwrap()?; // We read the first triple
592 /// assert_eq!(
593 /// parser.prefixes().collect::<Vec<_>>(),
594 /// [("schema", "http://schema.org/")]
595 /// ); // There are now prefixes
596 /// //
597 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
598 /// ```
599 pub fn prefixes(&self) -> PrefixesIter<'_> {
600 PrefixesIter {
601 inner: match &self.inner {
602 ReaderQuadParserKind::JsonLd(p) => PrefixesIterKind::JsonLd(p.prefixes()),
603 ReaderQuadParserKind::N3(p) => PrefixesIterKind::N3(p.prefixes()),
604 ReaderQuadParserKind::TriG(p) => PrefixesIterKind::TriG(p.prefixes()),
605 ReaderQuadParserKind::Turtle(p) => PrefixesIterKind::Turtle(p.prefixes()),
606 ReaderQuadParserKind::RdfXml(p) => PrefixesIterKind::RdfXml(p.prefixes()),
607 ReaderQuadParserKind::NQuads(_) | ReaderQuadParserKind::NTriples(_) => {
608 PrefixesIterKind::None
609 }
610 },
611 }
612 }
613
614 /// The base IRI considered at the current step of the parsing.
615 ///
616 /// `None` is returned if no base IRI is set or the format does not support base IRIs.
617 ///
618 /// ```
619 /// use oxrdfio::{RdfFormat, RdfParser};
620 ///
621 /// let file = r#"@base <http://example.com/> .
622 /// @prefix schema: <http://schema.org/> .
623 /// <foo> a schema:Person ;
624 /// schema:name "Foo" ."#;
625 ///
626 /// let mut parser = RdfParser::from_format(RdfFormat::Turtle).for_reader(file.as_bytes());
627 /// assert!(parser.base_iri().is_none()); // No base at the beginning because none has been given to the parser.
628 ///
629 /// parser.next().unwrap()?; // We read the first triple
630 /// assert_eq!(parser.base_iri(), Some("http://example.com/")); // There is now a base IRI.
631 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
632 /// ```
633 pub fn base_iri(&self) -> Option<&str> {
634 match &self.inner {
635 ReaderQuadParserKind::JsonLd(p) => p.base_iri(),
636 ReaderQuadParserKind::N3(p) => p.base_iri(),
637 ReaderQuadParserKind::TriG(p) => p.base_iri(),
638 ReaderQuadParserKind::Turtle(p) => p.base_iri(),
639 ReaderQuadParserKind::RdfXml(p) => p.base_iri(),
640 ReaderQuadParserKind::NQuads(_) | ReaderQuadParserKind::NTriples(_) => None,
641 }
642 }
643
644 /// A callback to load remote documents during parsing like JSON-LD contexts.
645 ///
646 /// ```
647 /// use oxrdf::NamedNodeRef;
648 /// use oxrdf::vocab::rdf;
649 /// use oxrdfio::{JsonLdProfile, JsonLdProfileSet, LoadedDocument, RdfFormat, RdfParser};
650 ///
651 /// let file = r#"{
652 /// "@context": "file://context.jsonld",
653 /// "@type": "schema:Person",
654 /// "@id": "http://example.com/foo",
655 /// "schema:name": "Foo"
656 /// }"#;
657 ///
658 /// let schema_person = NamedNodeRef::new("http://schema.org/Person")?;
659 /// let mut count = 0;
660 /// for quad in RdfParser::from_format(RdfFormat::JsonLd {
661 /// profile: JsonLdProfileSet::empty(),
662 /// })
663 /// .for_reader(file.as_bytes())
664 /// .with_document_loader(|url| {
665 /// assert_eq!(url, "file://context.jsonld");
666 /// Ok(LoadedDocument {
667 /// url: "file://context.jsonld".into(),
668 /// content: br#"{"@context":{"schema": "http://schema.org/"}}"#.to_vec(),
669 /// format: RdfFormat::JsonLd {
670 /// profile: JsonLdProfile::Context.into(),
671 /// },
672 /// })
673 /// }) {
674 /// let quad = quad?;
675 /// if quad.predicate == rdf::TYPE && quad.object == schema_person.into() {
676 /// count += 1;
677 /// }
678 /// }
679 /// assert_eq!(1, count);
680 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
681 /// ```
682 pub fn with_document_loader(
683 mut self,
684 loader: impl Fn(&str) -> Result<LoadedDocument, Box<dyn Error + Send + Sync>>
685 + Send
686 + Sync
687 + UnwindSafe
688 + RefUnwindSafe
689 + 'static,
690 ) -> Self {
691 self.inner = match self.inner {
692 ReaderQuadParserKind::JsonLd(p) => {
693 ReaderQuadParserKind::JsonLd(p.with_load_document_callback(move |iri, _| {
694 let response = loader(iri)?;
695 if !matches!(response.format, RdfFormat::JsonLd { .. }) {
696 return Err(format!(
697 "The JSON-LD context format must be JSON-LD, {} found",
698 response.format
699 )
700 .into());
701 }
702 Ok(JsonLdRemoteDocument {
703 document: response.content,
704 document_url: response.url,
705 })
706 }))
707 }
708 i => i,
709 };
710 self
711 }
712}
713
714/// Parses an RDF file from a Tokio [`AsyncRead`] implementation.
715///
716/// Can be built using [`RdfParser::for_tokio_async_reader`].
717///
718/// Reads are buffered.
719///
720/// ```
721/// # #[tokio::main(flavor = "current_thread")]
722/// # async fn main() -> Result<(), oxrdfio::RdfParseError> {
723/// use oxrdfio::{RdfFormat, RdfParser};
724///
725/// let file = "<http://example.com/s> <http://example.com/p> <http://example.com/o> .";
726///
727/// let mut parser =
728/// RdfParser::from_format(RdfFormat::NTriples).for_tokio_async_reader(file.as_bytes());
729/// if let Some(quad) = parser.next().await {
730/// assert_eq!(quad?.subject.to_string(), "<http://example.com/s>");
731/// }
732/// # Ok(())
733/// # }
734/// ```
735#[must_use]
736#[cfg(feature = "async-tokio")]
737pub struct TokioAsyncReaderQuadParser<R: AsyncRead + Unpin> {
738 inner: TokioAsyncReaderQuadParserKind<R>,
739 mapper: QuadMapper,
740}
741
742#[cfg(feature = "async-tokio")]
743enum TokioAsyncReaderQuadParserKind<R: AsyncRead + Unpin> {
744 JsonLd(TokioAsyncReaderJsonLdParser<R>),
745 N3(TokioAsyncReaderN3Parser<R>),
746 NQuads(TokioAsyncReaderNQuadsParser<R>),
747 NTriples(TokioAsyncReaderNTriplesParser<R>),
748 RdfXml(TokioAsyncReaderRdfXmlParser<R>),
749 TriG(TokioAsyncReaderTriGParser<R>),
750 Turtle(TokioAsyncReaderTurtleParser<R>),
751}
752
753#[cfg(feature = "async-tokio")]
754impl<R: AsyncRead + Unpin> TokioAsyncReaderQuadParser<R> {
755 pub async fn next(&mut self) -> Option<Result<Quad, RdfParseError>> {
756 Some(match &mut self.inner {
757 TokioAsyncReaderQuadParserKind::JsonLd(parser) => match parser.next().await? {
758 Ok(quad) => self.mapper.map_quad(quad).map_err(Into::into),
759 Err(e) => Err(e.into()),
760 },
761 TokioAsyncReaderQuadParserKind::N3(parser) => match parser.next().await? {
762 Ok(quad) => self.mapper.map_n3_quad(quad).map_err(Into::into),
763 Err(e) => Err(e.into()),
764 },
765 TokioAsyncReaderQuadParserKind::NQuads(parser) => match parser.next().await? {
766 Ok(quad) => self.mapper.map_quad(quad).map_err(Into::into),
767 Err(e) => Err(e.into()),
768 },
769 TokioAsyncReaderQuadParserKind::NTriples(parser) => match parser.next().await? {
770 Ok(triple) => Ok(self.mapper.map_triple_to_quad(triple)),
771 Err(e) => Err(e.into()),
772 },
773 TokioAsyncReaderQuadParserKind::RdfXml(parser) => match parser.next().await? {
774 Ok(triple) => Ok(self.mapper.map_triple_to_quad(triple)),
775 Err(e) => Err(e.into()),
776 },
777 TokioAsyncReaderQuadParserKind::TriG(parser) => match parser.next().await? {
778 Ok(quad) => self.mapper.map_quad(quad).map_err(Into::into),
779 Err(e) => Err(e.into()),
780 },
781 TokioAsyncReaderQuadParserKind::Turtle(parser) => match parser.next().await? {
782 Ok(triple) => Ok(self.mapper.map_triple_to_quad(triple)),
783 Err(e) => Err(e.into()),
784 },
785 })
786 }
787
788 /// The list of IRI prefixes considered at the current step of the parsing.
789 ///
790 /// This method returns (prefix name, prefix value) tuples.
791 /// It is empty at the beginning of the parsing and gets updated when prefixes are encountered.
792 /// It should be full at the end of the parsing (but if a prefix is overridden, only the latest version will be returned).
793 ///
794 /// An empty iterator is return if the format does not support prefixes.
795 ///
796 /// ```
797 /// # #[tokio::main(flavor = "current_thread")]
798 /// # async fn main() -> Result<(), oxrdfio::RdfParseError> {
799 /// use oxrdfio::{RdfFormat, RdfParser};
800 ///
801 /// let file = r#"@base <http://example.com/> .
802 /// @prefix schema: <http://schema.org/> .
803 /// <foo> a schema:Person ;
804 /// schema:name "Foo" ."#;
805 ///
806 /// let mut parser =
807 /// RdfParser::from_format(RdfFormat::Turtle).for_tokio_async_reader(file.as_bytes());
808 /// assert_eq!(parser.prefixes().collect::<Vec<_>>(), []); // No prefix at the beginning
809 ///
810 /// parser.next().await.unwrap()?; // We read the first triple
811 /// assert_eq!(
812 /// parser.prefixes().collect::<Vec<_>>(),
813 /// [("schema", "http://schema.org/")]
814 /// ); // There are now prefixes
815 /// //
816 /// # Ok(())
817 /// # }
818 /// ```
819 pub fn prefixes(&self) -> PrefixesIter<'_> {
820 PrefixesIter {
821 inner: match &self.inner {
822 TokioAsyncReaderQuadParserKind::JsonLd(p) => PrefixesIterKind::JsonLd(p.prefixes()),
823 TokioAsyncReaderQuadParserKind::N3(p) => PrefixesIterKind::N3(p.prefixes()),
824 TokioAsyncReaderQuadParserKind::TriG(p) => PrefixesIterKind::TriG(p.prefixes()),
825 TokioAsyncReaderQuadParserKind::Turtle(p) => PrefixesIterKind::Turtle(p.prefixes()),
826 TokioAsyncReaderQuadParserKind::RdfXml(p) => PrefixesIterKind::RdfXml(p.prefixes()),
827 TokioAsyncReaderQuadParserKind::NQuads(_)
828 | TokioAsyncReaderQuadParserKind::NTriples(_) => PrefixesIterKind::None,
829 },
830 }
831 }
832
833 /// The base IRI considered at the current step of the parsing.
834 ///
835 /// `None` is returned if no base IRI is set or the format does not support base IRIs.
836 ///
837 /// ```
838 /// # #[tokio::main(flavor = "current_thread")]
839 /// # async fn main() -> Result<(), oxrdfio::RdfParseError> {
840 /// use oxrdfio::{RdfFormat, RdfParser};
841 ///
842 /// let file = r#"@base <http://example.com/> .
843 /// @prefix schema: <http://schema.org/> .
844 /// <foo> a schema:Person ;
845 /// schema:name "Foo" ."#;
846 ///
847 /// let mut parser =
848 /// RdfParser::from_format(RdfFormat::Turtle).for_tokio_async_reader(file.as_bytes());
849 /// assert!(parser.base_iri().is_none()); // No base IRI at the beginning
850 ///
851 /// parser.next().await.unwrap()?; // We read the first triple
852 /// assert_eq!(parser.base_iri(), Some("http://example.com/")); // There is now a base IRI
853 /// //
854 /// # Ok(())
855 /// # }
856 /// ```
857 pub fn base_iri(&self) -> Option<&str> {
858 match &self.inner {
859 TokioAsyncReaderQuadParserKind::JsonLd(p) => p.base_iri(),
860 TokioAsyncReaderQuadParserKind::N3(p) => p.base_iri(),
861 TokioAsyncReaderQuadParserKind::TriG(p) => p.base_iri(),
862 TokioAsyncReaderQuadParserKind::Turtle(p) => p.base_iri(),
863 TokioAsyncReaderQuadParserKind::RdfXml(p) => p.base_iri(),
864 TokioAsyncReaderQuadParserKind::NQuads(_)
865 | TokioAsyncReaderQuadParserKind::NTriples(_) => None,
866 }
867 }
868}
869
870/// Parses a RDF file from a byte slice.
871///
872/// Can be built using [`RdfParser::for_slice`].
873///
874/// ```
875/// use oxrdfio::{RdfFormat, RdfParser};
876///
877/// let file = "<http://example.com/s> <http://example.com/p> <http://example.com/o> .";
878///
879/// let quads = RdfParser::from_format(RdfFormat::NTriples)
880/// .for_slice(file)
881/// .collect::<Result<Vec<_>, _>>()?;
882///
883/// assert_eq!(quads.len(), 1);
884/// assert_eq!(quads[0].subject.to_string(), "<http://example.com/s>");
885/// # std::io::Result::Ok(())
886/// ```
887#[must_use]
888pub struct SliceQuadParser<'a> {
889 inner: SliceQuadParserKind<'a>,
890 mapper: QuadMapper,
891}
892
893enum SliceQuadParserKind<'a> {
894 JsonLd(SliceJsonLdParser<'a>),
895 N3(SliceN3Parser<'a>),
896 NQuads(SliceNQuadsParser<'a>),
897 NTriples(SliceNTriplesParser<'a>),
898 RdfXml(SliceRdfXmlParser<'a>),
899 TriG(SliceTriGParser<'a>),
900 Turtle(SliceTurtleParser<'a>),
901}
902
903impl Iterator for SliceQuadParser<'_> {
904 type Item = Result<Quad, RdfSyntaxError>;
905
906 fn next(&mut self) -> Option<Self::Item> {
907 Some(match &mut self.inner {
908 SliceQuadParserKind::JsonLd(parser) => match parser.next()? {
909 Ok(quad) => self.mapper.map_quad(quad),
910 Err(e) => Err(e.into()),
911 },
912 SliceQuadParserKind::N3(parser) => match parser.next()? {
913 Ok(quad) => self.mapper.map_n3_quad(quad),
914 Err(e) => Err(e.into()),
915 },
916 SliceQuadParserKind::NQuads(parser) => match parser.next()? {
917 Ok(quad) => self.mapper.map_quad(quad),
918 Err(e) => Err(e.into()),
919 },
920 SliceQuadParserKind::NTriples(parser) => match parser.next()? {
921 Ok(triple) => Ok(self.mapper.map_triple_to_quad(triple)),
922 Err(e) => Err(e.into()),
923 },
924 SliceQuadParserKind::RdfXml(parser) => match parser.next()? {
925 Ok(triple) => Ok(self.mapper.map_triple_to_quad(triple)),
926 Err(e) => Err(e.into()),
927 },
928 SliceQuadParserKind::TriG(parser) => match parser.next()? {
929 Ok(quad) => self.mapper.map_quad(quad),
930 Err(e) => Err(e.into()),
931 },
932 SliceQuadParserKind::Turtle(parser) => match parser.next()? {
933 Ok(triple) => Ok(self.mapper.map_triple_to_quad(triple)),
934 Err(e) => Err(e.into()),
935 },
936 })
937 }
938}
939
940impl SliceQuadParser<'_> {
941 /// A callback to load remote documents during parsing like JSON-LD contexts.
942 ///
943 /// ```
944 /// use oxrdf::NamedNodeRef;
945 /// use oxrdf::vocab::rdf;
946 /// use oxrdfio::{JsonLdProfile, JsonLdProfileSet, LoadedDocument, RdfFormat, RdfParser};
947 ///
948 /// let file = r#"{
949 /// "@context": "file://context.jsonld",
950 /// "@type": "schema:Person",
951 /// "@id": "http://example.com/foo",
952 /// "schema:name": "Foo"
953 /// }"#;
954 ///
955 /// let schema_person = NamedNodeRef::new("http://schema.org/Person")?;
956 /// let mut count = 0;
957 /// for quad in RdfParser::from_format(RdfFormat::JsonLd {
958 /// profile: JsonLdProfileSet::empty(),
959 /// })
960 /// .for_slice(file)
961 /// .with_document_loader(|url| {
962 /// assert_eq!(url, "file://context.jsonld");
963 /// Ok(LoadedDocument {
964 /// url: "file://context.jsonld".into(),
965 /// content: br#"{"@context":{"schema": "http://schema.org/"}}"#.to_vec(),
966 /// format: RdfFormat::JsonLd {
967 /// profile: JsonLdProfile::Context.into(),
968 /// },
969 /// })
970 /// }) {
971 /// let quad = quad?;
972 /// if quad.predicate == rdf::TYPE && quad.object == schema_person.into() {
973 /// count += 1;
974 /// }
975 /// }
976 /// assert_eq!(1, count);
977 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
978 /// ```
979 pub fn with_document_loader(
980 mut self,
981 loader: impl Fn(&str) -> Result<LoadedDocument, Box<dyn Error + Send + Sync>>
982 + Send
983 + Sync
984 + UnwindSafe
985 + RefUnwindSafe
986 + 'static,
987 ) -> Self {
988 self.inner = match self.inner {
989 SliceQuadParserKind::JsonLd(p) => {
990 SliceQuadParserKind::JsonLd(p.with_load_document_callback(move |iri, _| {
991 let response = loader(iri)?;
992 if !matches!(response.format, RdfFormat::JsonLd { .. }) {
993 return Err(format!(
994 "The JSON-LD context format must be JSON-LD, {} found",
995 response.format
996 )
997 .into());
998 }
999 Ok(JsonLdRemoteDocument {
1000 document: response.content,
1001 document_url: response.url,
1002 })
1003 }))
1004 }
1005 i => i,
1006 };
1007 self
1008 }
1009
1010 /// The list of IRI prefixes considered at the current step of the parsing.
1011 ///
1012 /// This method returns (prefix name, prefix value) tuples.
1013 /// It is empty at the beginning of the parsing and gets updated when prefixes are encountered.
1014 /// It should be full at the end of the parsing (but if a prefix is overridden, only the latest version will be returned).
1015 ///
1016 /// An empty iterator is return if the format does not support prefixes.
1017 ///
1018 /// ```
1019 /// use oxrdfio::{RdfFormat, RdfParser};
1020 ///
1021 /// let file = r#"@base <http://example.com/> .
1022 /// @prefix schema: <http://schema.org/> .
1023 /// <foo> a schema:Person ;
1024 /// schema:name "Foo" ."#;
1025 ///
1026 /// let mut parser = RdfParser::from_format(RdfFormat::Turtle).for_slice(file);
1027 /// assert!(parser.prefixes().collect::<Vec<_>>().is_empty()); // No prefix at the beginning
1028 ///
1029 /// parser.next().unwrap()?; // We read the first triple
1030 /// assert_eq!(
1031 /// parser.prefixes().collect::<Vec<_>>(),
1032 /// [("schema", "http://schema.org/")]
1033 /// ); // There are now prefixes
1034 /// //
1035 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1036 /// ```
1037 pub fn prefixes(&self) -> PrefixesIter<'_> {
1038 PrefixesIter {
1039 inner: match &self.inner {
1040 SliceQuadParserKind::JsonLd(p) => PrefixesIterKind::JsonLd(p.prefixes()),
1041 SliceQuadParserKind::N3(p) => PrefixesIterKind::N3(p.prefixes()),
1042 SliceQuadParserKind::TriG(p) => PrefixesIterKind::TriG(p.prefixes()),
1043 SliceQuadParserKind::Turtle(p) => PrefixesIterKind::Turtle(p.prefixes()),
1044 SliceQuadParserKind::RdfXml(p) => PrefixesIterKind::RdfXml(p.prefixes()),
1045 SliceQuadParserKind::NQuads(_) | SliceQuadParserKind::NTriples(_) => {
1046 PrefixesIterKind::None
1047 }
1048 },
1049 }
1050 }
1051
1052 /// The base IRI considered at the current step of the parsing.
1053 ///
1054 /// `None` is returned if no base IRI is set or the format does not support base IRIs.
1055 ///
1056 /// ```
1057 /// use oxrdfio::{RdfFormat, RdfParser};
1058 ///
1059 /// let file = r#"@base <http://example.com/> .
1060 /// @prefix schema: <http://schema.org/> .
1061 /// <foo> a schema:Person ;
1062 /// schema:name "Foo" ."#;
1063 ///
1064 /// let mut parser = RdfParser::from_format(RdfFormat::Turtle).for_slice(file);
1065 /// assert!(parser.base_iri().is_none()); // No base at the beginning because none has been given to the parser.
1066 ///
1067 /// parser.next().unwrap()?; // We read the first triple
1068 /// assert_eq!(parser.base_iri(), Some("http://example.com/")); // There is now a base IRI.
1069 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1070 /// ```
1071 pub fn base_iri(&self) -> Option<&str> {
1072 match &self.inner {
1073 SliceQuadParserKind::JsonLd(p) => p.base_iri(),
1074 SliceQuadParserKind::N3(p) => p.base_iri(),
1075 SliceQuadParserKind::TriG(p) => p.base_iri(),
1076 SliceQuadParserKind::Turtle(p) => p.base_iri(),
1077 SliceQuadParserKind::RdfXml(p) => p.base_iri(),
1078 SliceQuadParserKind::NQuads(_) | SliceQuadParserKind::NTriples(_) => None,
1079 }
1080 }
1081}
1082
1083/// Iterator on the file prefixes.
1084///
1085/// See [`ReaderQuadParser::prefixes`].
1086pub struct PrefixesIter<'a> {
1087 inner: PrefixesIterKind<'a>,
1088}
1089
1090enum PrefixesIterKind<'a> {
1091 JsonLd(JsonLdPrefixesIter<'a>),
1092 Turtle(TurtlePrefixesIter<'a>),
1093 TriG(TriGPrefixesIter<'a>),
1094 N3(N3PrefixesIter<'a>),
1095 RdfXml(RdfXmlPrefixesIter<'a>),
1096 None,
1097}
1098
1099impl<'a> Iterator for PrefixesIter<'a> {
1100 type Item = (&'a str, &'a str);
1101
1102 #[inline]
1103 fn next(&mut self) -> Option<Self::Item> {
1104 match &mut self.inner {
1105 PrefixesIterKind::JsonLd(iter) => iter.next(),
1106 PrefixesIterKind::Turtle(iter) => iter.next(),
1107 PrefixesIterKind::TriG(iter) => iter.next(),
1108 PrefixesIterKind::N3(iter) => iter.next(),
1109 PrefixesIterKind::RdfXml(iter) => iter.next(),
1110 PrefixesIterKind::None => None,
1111 }
1112 }
1113
1114 #[inline]
1115 fn size_hint(&self) -> (usize, Option<usize>) {
1116 match &self.inner {
1117 PrefixesIterKind::JsonLd(iter) => iter.size_hint(),
1118 PrefixesIterKind::Turtle(iter) => iter.size_hint(),
1119 PrefixesIterKind::TriG(iter) => iter.size_hint(),
1120 PrefixesIterKind::N3(iter) => iter.size_hint(),
1121 PrefixesIterKind::RdfXml(iter) => iter.size_hint(),
1122 PrefixesIterKind::None => (0, Some(0)),
1123 }
1124 }
1125}
1126
1127struct QuadMapper {
1128 default_graph: GraphName,
1129 without_named_graphs: bool,
1130 blank_node_map: Option<HashMap<BlankNode, BlankNode>>,
1131}
1132
1133impl QuadMapper {
1134 fn map_blank_node(&mut self, node: BlankNode) -> BlankNode {
1135 if let Some(blank_node_map) = &mut self.blank_node_map {
1136 blank_node_map
1137 .entry(node)
1138 .or_insert_with(BlankNode::default)
1139 .clone()
1140 } else {
1141 node
1142 }
1143 }
1144
1145 fn map_subject(&mut self, node: NamedOrBlankNode) -> NamedOrBlankNode {
1146 match node {
1147 NamedOrBlankNode::NamedNode(node) => node.into(),
1148 NamedOrBlankNode::BlankNode(node) => self.map_blank_node(node).into(),
1149 }
1150 }
1151
1152 fn map_term(&mut self, node: Term) -> Term {
1153 match node {
1154 Term::NamedNode(node) => node.into(),
1155 Term::BlankNode(node) => self.map_blank_node(node).into(),
1156 Term::Literal(literal) => literal.into(),
1157 #[cfg(feature = "rdf-12")]
1158 Term::Triple(triple) => self.map_triple(*triple).into(),
1159 }
1160 }
1161
1162 fn map_triple(&mut self, triple: Triple) -> Triple {
1163 Triple {
1164 subject: self.map_subject(triple.subject),
1165 predicate: triple.predicate,
1166 object: self.map_term(triple.object),
1167 }
1168 }
1169
1170 fn map_graph_name(&mut self, graph_name: GraphName) -> Result<GraphName, RdfSyntaxError> {
1171 match graph_name {
1172 GraphName::NamedNode(node) => {
1173 if self.without_named_graphs {
1174 Err(RdfSyntaxError::msg("Named graphs are not allowed"))
1175 } else {
1176 Ok(node.into())
1177 }
1178 }
1179 GraphName::BlankNode(node) => {
1180 if self.without_named_graphs {
1181 Err(RdfSyntaxError::msg("Named graphs are not allowed"))
1182 } else {
1183 Ok(self.map_blank_node(node).into())
1184 }
1185 }
1186 GraphName::DefaultGraph => Ok(self.default_graph.clone()),
1187 }
1188 }
1189
1190 fn map_quad(&mut self, quad: Quad) -> Result<Quad, RdfSyntaxError> {
1191 Ok(Quad {
1192 subject: self.map_subject(quad.subject),
1193 predicate: quad.predicate,
1194 object: self.map_term(quad.object),
1195 graph_name: self.map_graph_name(quad.graph_name)?,
1196 })
1197 }
1198
1199 fn map_triple_to_quad(&mut self, triple: Triple) -> Quad {
1200 self.map_triple(triple).in_graph(self.default_graph.clone())
1201 }
1202
1203 fn map_n3_quad(&mut self, quad: N3Quad) -> Result<Quad, RdfSyntaxError> {
1204 Ok(Quad {
1205 subject: match quad.subject {
1206 N3Term::NamedNode(s) => Ok(s.into()),
1207 N3Term::BlankNode(s) => Ok(self.map_blank_node(s).into()),
1208 N3Term::Literal(_) => Err(RdfSyntaxError::msg(
1209 "literals are not allowed in regular RDF subjects",
1210 )),
1211 #[cfg(feature = "rdf-12")]
1212 N3Term::Triple(_) => Err(RdfSyntaxError::msg(
1213 "triple terms are not allowed in regular RDF subjects",
1214 )),
1215 N3Term::Variable(_) => Err(RdfSyntaxError::msg(
1216 "variables are not allowed in regular RDF subjects",
1217 )),
1218 }?,
1219 predicate: match quad.predicate {
1220 N3Term::NamedNode(p) => Ok(p),
1221 N3Term::BlankNode(_) => Err(RdfSyntaxError::msg(
1222 "blank nodes are not allowed in regular RDF predicates",
1223 )),
1224 N3Term::Literal(_) => Err(RdfSyntaxError::msg(
1225 "literals are not allowed in regular RDF predicates",
1226 )),
1227 #[cfg(feature = "rdf-12")]
1228 N3Term::Triple(_) => Err(RdfSyntaxError::msg(
1229 "quoted triples are not allowed in regular RDF predicates",
1230 )),
1231 N3Term::Variable(_) => Err(RdfSyntaxError::msg(
1232 "variables are not allowed in regular RDF predicates",
1233 )),
1234 }?,
1235 object: match quad.object {
1236 N3Term::NamedNode(o) => Ok(o.into()),
1237 N3Term::BlankNode(o) => Ok(self.map_blank_node(o).into()),
1238 N3Term::Literal(o) => Ok(o.into()),
1239 #[cfg(feature = "rdf-12")]
1240 N3Term::Triple(o) => Ok(self.map_triple(*o).into()),
1241 N3Term::Variable(_) => Err(RdfSyntaxError::msg(
1242 "variables are not allowed in regular RDF objects",
1243 )),
1244 }?,
1245 graph_name: self.map_graph_name(quad.graph_name)?,
1246 })
1247 }
1248}