Skip to main content

jsonld_core/rdf/
quad.rs

1use super::{RdfDirection, triples::Value};
2use crate::{ExpandedDocument, FlattenedDocument, LdQuads, ValidId};
3use rdfx::{
4    GeneralizedTriple as Triple,
5    LocalGenerator,
6    vocabulary::{BlankIdVocabulary, IriVocabulary, LiteralVocabulary, Vocabulary, VocabularyMut},
7};
8use std::{borrow::Cow, convert::TryInto, hash::Hash};
9
10/// RDF quad produced from an expanded document.
11pub type Quad<T, B, L> = rdfx::GeneralizedQuad<ValidId<T, B>, ValidId<T, B>, Value<T, B, L>, ValidId<T, B>>;
12
13/// RDF quad whose terms are borrowed from the document.
14pub type QuadRef<'a, T, B, L> = rdfx::GeneralizedQuad<Cow<'a, ValidId<T, B>>, Cow<'a, ValidId<T, B>>, Value<T, B, L>, &'a ValidId<T, B>>;
15
16struct Compound<'a, T, B, L> {
17    graph: Option<&'a ValidId<T, B>>,
18    triples: super::CompoundValueTriples<'a, T, B, L>,
19}
20
21type VocabularyCompoundLiteral<'a, N> = Compound<'a, <N as IriVocabulary>::Iri, <N as BlankIdVocabulary>::BlankId, <N as LiteralVocabulary>::Literal>;
22
23/// Warning raised while serializing a document as RDF quads.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum Warning {
26    /// A value could not be represented as an RDF term; the quad that would
27    /// have carried it was dropped from the stream.
28    ValueSerializationFailed,
29}
30
31impl std::fmt::Display for Warning {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            Self::ValueSerializationFailed => f.write_str("value not representable as an RDF term, quad dropped"),
35        }
36    }
37}
38
39/// Iterator over the RDF Quads of a JSON-LD document.
40pub struct Quads<'a, N: Vocabulary, G: LocalGenerator, W = ()> {
41    vocabulary: &'a mut N,
42    generator: &'a mut G,
43    rdf_direction: Option<RdfDirection>,
44    compound_value: Option<VocabularyCompoundLiteral<'a, N>>,
45    quads: crate::quad::Quads<'a, N::Iri, N::BlankId>,
46    produce_generalized_rdf: bool,
47    warnings: W,
48}
49
50impl<'a, N: Vocabulary, G: LocalGenerator, W> Quads<'a, N, G, W> {
51    /// Turns this iterator into one yielding owned quads.
52    pub fn cloned(self) -> ClonedQuads<'a, N, G, W> {
53        ClonedQuads { inner: self }
54    }
55
56    /// Attaches a warning handler to this iterator, replacing the current
57    /// one (`()` initially, which ignores warnings).
58    ///
59    /// The handler is called whenever a quad is dropped from the stream
60    /// because its value has no RDF representation.
61    pub fn with_warnings<V>(self, warnings: V) -> Quads<'a, N, G, V> {
62        Quads {
63            vocabulary: self.vocabulary,
64            generator: self.generator,
65            rdf_direction: self.rdf_direction,
66            compound_value: self.compound_value,
67            quads: self.quads,
68            produce_generalized_rdf: self.produce_generalized_rdf,
69            warnings,
70        }
71    }
72}
73
74impl<'a, N: Vocabulary + VocabularyMut, G: LocalGenerator, W> Iterator for Quads<'a, N, G, W>
75where
76    N::Iri: Clone,
77    N::BlankId: Clone,
78    N::Literal: Clone,
79    W: crate::warning::Handler<N, Warning>,
80{
81    type Item = QuadRef<'a, N::Iri, N::BlankId, N::Literal>;
82
83    fn next(&mut self) -> Option<Self::Item> {
84        loop {
85            if let Some(compound_value) = &mut self.compound_value {
86                match compound_value.triples.next(self.vocabulary, self.generator, self.rdf_direction) {
87                    Some(Triple(subject, property, object)) => {
88                        if self.produce_generalized_rdf || !property.is_blank() {
89                            break Some(rdfx::GeneralizedQuad(Cow::Owned(subject), Cow::Owned(property), object, compound_value.graph));
90                        }
91                    }
92                    None => self.compound_value = None,
93                }
94            }
95
96            match self.quads.next() {
97                Some(crate::quad::QuadRef(graph, subject, property, object)) => {
98                    let rdf_graph: Option<&'a ValidId<N::Iri, N::BlankId>> = match graph.map(std::convert::TryInto::try_into) {
99                        Some(Ok(r)) => Some(r),
100                        None => None,
101                        _ => continue,
102                    };
103
104                    let rdf_subject: &'a ValidId<N::Iri, N::BlankId> = match subject.try_into() {
105                        Ok(r) => r,
106                        Err(_) => continue,
107                    };
108
109                    let rdf_property: Cow<ValidId<N::Iri, N::BlankId>> = match property {
110                        crate::quad::PropertyRef::Type => Cow::Owned(ValidId::Iri(self.vocabulary.insert(super::RDF_TYPE))),
111                        crate::quad::PropertyRef::Ref(r) => match r.try_into() {
112                            Ok(r) => Cow::Borrowed(r),
113                            Err(_) => continue,
114                        },
115                    };
116
117                    if !self.produce_generalized_rdf && (*rdf_property).is_blank() {
118                        // Skip gRDF quad.
119                        continue;
120                    }
121
122                    match object.rdf_value_with(self.vocabulary, self.generator, self.rdf_direction) {
123                        Some(compound_value) => {
124                            if let Some(rdf_value_triples) = compound_value.triples {
125                                self.compound_value = Some(Compound {
126                                    graph: rdf_graph,
127                                    triples: rdf_value_triples,
128                                });
129                            }
130
131                            break Some(rdfx::GeneralizedQuad(Cow::Borrowed(rdf_subject), rdf_property, compound_value.value, rdf_graph));
132                        }
133                        None => {
134                            self.warnings.handle(self.vocabulary, Warning::ValueSerializationFailed);
135                        }
136                    }
137                }
138                None => break None,
139            }
140        }
141    }
142}
143
144/// Iterator over the RDF Quads of a JSON-LD document where borrowed values are
145/// cloned.
146pub struct ClonedQuads<'a, N: Vocabulary, G: LocalGenerator, W = ()> {
147    inner: Quads<'a, N, G, W>,
148}
149
150impl<N: Vocabulary + VocabularyMut, G: LocalGenerator, W> Iterator for ClonedQuads<'_, N, G, W>
151where
152    N::Iri: Clone,
153    N::BlankId: Clone,
154    N::Literal: Clone,
155    W: crate::warning::Handler<N, Warning>,
156{
157    type Item = Quad<N::Iri, N::BlankId, N::Literal>;
158
159    fn next(&mut self) -> Option<Self::Item> {
160        self.inner
161            .next()
162            .map(|rdfx::GeneralizedQuad(s, p, o, g)| rdfx::GeneralizedQuad(s.into_owned(), p.into_owned(), o, g.cloned()))
163    }
164}
165
166/// Documents that can be serialized as RDF quads.
167pub trait RdfQuads<T, B> {
168    /// Returns the RDF quads of this document, taking every parameter
169    /// explicitly.
170    fn rdf_quads_full<'a, V: Vocabulary<Iri = T, BlankId = B>, G: LocalGenerator>(
171        &'a self,
172        vocabulary: &'a mut V,
173        generator: &'a mut G,
174        rdf_direction: Option<RdfDirection>,
175        produce_generalized_rdf: bool,
176    ) -> Quads<'a, V, G>;
177
178    /// Returns the RDF quads of this document, using the given vocabulary.
179    fn rdf_quads_with<'a, V: Vocabulary<Iri = T, BlankId = B>, G: LocalGenerator>(
180        &'a self,
181        vocabulary: &'a mut V,
182        generator: &'a mut G,
183        rdf_direction: Option<RdfDirection>,
184    ) -> Quads<'a, V, G> {
185        self.rdf_quads_full(vocabulary, generator, rdf_direction, false)
186    }
187
188    /// Returns the RDF quads of this document.
189    fn rdf_quads<'a, G: LocalGenerator>(&'a self, generator: &'a mut G, rdf_direction: Option<RdfDirection>) -> Quads<'a, (), G>
190    where
191        (): Vocabulary<Iri = T, BlankId = B>,
192    {
193        self.rdf_quads_with(rdfx::vocabulary::no_vocabulary_mut(), generator, rdf_direction)
194    }
195}
196
197impl<T, B> RdfQuads<T, B> for ExpandedDocument<T, B> {
198    fn rdf_quads_full<'a, V: Vocabulary<Iri = T, BlankId = B>, G: LocalGenerator>(
199        &'a self,
200        vocabulary: &'a mut V,
201        generator: &'a mut G,
202        rdf_direction: Option<RdfDirection>,
203        produce_generalized_rdf: bool,
204    ) -> Quads<'a, V, G> {
205        Quads {
206            vocabulary,
207            generator,
208            rdf_direction,
209            compound_value: None,
210            quads: self.quads(),
211            produce_generalized_rdf,
212            warnings: (),
213        }
214    }
215}
216
217impl<T, B> RdfQuads<T, B> for FlattenedDocument<T, B> {
218    fn rdf_quads_full<'a, V: Vocabulary<Iri = T, BlankId = B>, G: LocalGenerator>(
219        &'a self,
220        vocabulary: &'a mut V,
221        generator: &'a mut G,
222        rdf_direction: Option<RdfDirection>,
223        produce_generalized_rdf: bool,
224    ) -> Quads<'a, V, G> {
225        Quads {
226            vocabulary,
227            generator,
228            rdf_direction,
229            compound_value: None,
230            quads: self.quads(),
231            produce_generalized_rdf,
232            warnings: (),
233        }
234    }
235}
236
237impl<T: Eq + Hash, B: Eq + Hash> RdfQuads<T, B> for crate::flattening::NodeMap<T, B> {
238    fn rdf_quads_full<'a, V: Vocabulary<Iri = T, BlankId = B>, G: LocalGenerator>(
239        &'a self,
240        vocabulary: &'a mut V,
241        generator: &'a mut G,
242        rdf_direction: Option<RdfDirection>,
243        produce_generalized_rdf: bool,
244    ) -> Quads<'a, V, G> {
245        Quads {
246            vocabulary,
247            generator,
248            rdf_direction,
249            compound_value: None,
250            quads: self.quads(),
251            produce_generalized_rdf,
252            warnings: (),
253        }
254    }
255}