Skip to main content

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}