rdf_dynsyn/parser/triples/
sync.rs

1use std::io::BufRead;
2
3use sophia_api::prelude::{Iri, TripleParser};
4use sophia_turtle::parser::{nt::NTriplesParser, turtle::TurtleParser};
5#[cfg(feature = "rdf_xml")]
6use sophia_xml::parser::RdfXmlParser;
7
8use super::{factory::DynSynTripleParserFactory, source::DynSynTripleSource};
9use crate::syntax::{self, invariant::triples_parsable::TriplesParsableSyntax};
10
11/// This is a sum-type that wraps around different triple-parsers from sophia.
12#[derive(Debug, Clone)]
13pub enum InnerTripleParser {
14    NTriples(NTriplesParser),
15    Turtle(TurtleParser),
16    #[cfg(feature = "rdf_xml")]
17    RdfXml(RdfXmlParser),
18}
19
20impl From<NTriplesParser> for InnerTripleParser {
21    #[inline]
22    fn from(p: NTriplesParser) -> Self {
23        Self::NTriples(p)
24    }
25}
26
27impl From<TurtleParser> for InnerTripleParser {
28    #[inline]
29    fn from(p: TurtleParser) -> Self {
30        Self::Turtle(p)
31    }
32}
33
34#[cfg(feature = "rdf_xml")]
35impl From<RdfXmlParser> for InnerTripleParser {
36    #[inline]
37    fn from(p: RdfXmlParser) -> Self {
38        Self::RdfXml(p)
39    }
40}
41
42impl InnerTripleParser {
43    /// Create a sum-parser for given syntax.
44    pub fn new(syntax_: TriplesParsableSyntax, base_iri: Option<Iri<String>>) -> Self {
45        match syntax_.into_subject() {
46            syntax::N_TRIPLES => NTriplesParser {}.into(),
47            syntax::TURTLE => TurtleParser { base: base_iri }.into(),
48            #[cfg(feature = "rdf_xml")]
49            syntax::RDF_XML => RdfXmlParser { base: base_iri }.into(),
50            // All triple parsable syntaxes are addressed.
51            _ => unreachable!(),
52        }
53    }
54}
55
56/// This parser implements [`sophia_api::parser::TripleParser`]
57/// trait, and can be instantiated at runtime against any of
58/// supported syntaxes using [`DynSynTripleParserFactory`]
59/// factory.
60///
61/// It can currently parse triples from documents in any of
62/// concrete_syntaxes: [`n-triples`](crate::syntax::N_TRIPLES),
63/// [`turtle`](crate::syntax::TURTLE), [`rdf-xml`](crate::syntax::RDF_XML). For docs in any of these
64/// syntaxes, this parser will stream triples through
65/// [`DynSynTripleSource`] instance.
66///
67#[derive(Debug, Clone)]
68pub struct DynSynTripleParser(InnerTripleParser);
69
70impl DynSynTripleParser {
71    /// Create a new parser with given params.
72    #[inline]
73    pub fn new(syntax_: TriplesParsableSyntax, base_iri: Option<Iri<String>>) -> Self {
74        Self(InnerTripleParser::new(syntax_, base_iri))
75    }
76}
77
78impl<R> TripleParser<R> for DynSynTripleParser
79where
80    R: BufRead,
81{
82    type Source = DynSynTripleSource<R>;
83
84    fn parse(&self, data: R) -> Self::Source {
85        match &self.0 {
86            InnerTripleParser::NTriples(p) => DynSynTripleSource(p.parse(data).into()),
87            InnerTripleParser::Turtle(p) => DynSynTripleSource(p.parse(data).into()),
88            #[cfg(feature = "rdf_xml")]
89            InnerTripleParser::RdfXml(p) => DynSynTripleSource(p.parse(data).into()),
90        }
91    }
92}
93
94impl DynSynTripleParserFactory {
95    /// Create new [`DynSynTripleParser`] instance, for given
96    /// `syntax_`, `base_iri`.
97    #[inline]
98    pub fn new_parser(
99        &self,
100        syntax_: TriplesParsableSyntax,
101        base_iri: Option<Iri<String>>,
102    ) -> DynSynTripleParser {
103        DynSynTripleParser::new(syntax_, base_iri)
104    }
105}
106
107// ----------------------------------------
108//                                      tests
109// ----------------------------------------
110
111#[cfg(test)]
112mod tests {
113    use std::collections::HashSet;
114
115    use once_cell::sync::Lazy;
116    use sophia_api::{
117        parser::{IntoParsable, TripleParser},
118        source::TripleSource,
119        term::SimpleTerm,
120    };
121    use sophia_isomorphism::isomorphic_graphs;
122    use sophia_turtle::parser::{nt::NTriplesParser, turtle::TurtleParser};
123
124    use super::*;
125    use crate::{
126        parser::test_data::*,
127        syntax::invariant::triples_parsable::{TP_N_TRIPLES, TP_RDF_XML, TP_TURTLE},
128        tests::TRACING,
129    };
130
131    static DYNSYN_TRIPLE_PARSER_FACTORY: Lazy<DynSynTripleParserFactory> =
132        Lazy::new(DynSynTripleParserFactory::default);
133
134    fn check_graph_parse_isomorphism<'b, B, P1, P2>(p1: &P1, p2: &P2, qs: &'b str)
135    where
136        P1: TripleParser<B>,
137        P2: TripleParser<B>,
138        &'b str: IntoParsable<Target = B>,
139    {
140        let mut g1 = HashSet::<[SimpleTerm; 3]>::new();
141        p1.parse_str(qs).add_to_graph(&mut g1).unwrap();
142
143        let mut g2 = HashSet::<[SimpleTerm; 3]>::new();
144        p2.parse_str(qs).add_to_graph(&mut g2).unwrap();
145
146        assert!(isomorphic_graphs(&g1, &g2).unwrap());
147    }
148
149    #[test]
150    pub fn correctly_parses_turtle() {
151        Lazy::force(&TRACING);
152        check_graph_parse_isomorphism(
153            &TurtleParser {
154                base: Some(BASE_IRI1.clone()),
155            },
156            &DYNSYN_TRIPLE_PARSER_FACTORY.new_parser(TP_TURTLE, Some(BASE_IRI1.clone())),
157            GRAPH_STR_TURTLE,
158        );
159    }
160
161    #[test]
162    pub fn correctly_parses_ntriples() {
163        Lazy::force(&TRACING);
164        check_graph_parse_isomorphism(
165            &NTriplesParser {},
166            &DYNSYN_TRIPLE_PARSER_FACTORY.new_parser(TP_N_TRIPLES, Some(BASE_IRI1.clone())),
167            GRAPH_STR_N_TRIPLES,
168        );
169    }
170
171    #[cfg(feature = "rdf_xml")]
172    #[test]
173    pub fn correctly_parses_rdf_xml() {
174        Lazy::force(&TRACING);
175        check_graph_parse_isomorphism(
176            &RdfXmlParser {
177                base: Some(BASE_IRI1.clone()),
178            },
179            &DYNSYN_TRIPLE_PARSER_FACTORY.new_parser(TP_RDF_XML, Some(BASE_IRI1.clone())),
180            GRAPH_STR_RDF_XML,
181        );
182    }
183}