1use oxigraph::model::*;
3
4use crate::{Error, GraphRead, Result};
5
6type Matches<'a> = Box<dyn Iterator<Item = Result<Quad>> + 'a>;
7
8pub struct UnionGraph<'a, G: GraphRead + ?Sized> {
13 source: &'a G,
14}
15
16impl<'a, G: GraphRead + ?Sized> UnionGraph<'a, G> {
17 pub fn new(source: &'a G) -> Self {
19 Self { source }
20 }
21
22 fn default_matches<'b>(
23 &'b self,
24 subject: Option<NamedOrBlankNodeRef<'_>>,
25 predicate: Option<NamedNodeRef<'_>>,
26 object: Option<TermRef<'_>>,
27 ) -> impl Iterator<Item = Result<Quad>> + use<'a, 'b, G> {
28 self.source
29 .matching_quads(subject, predicate, object, None)
30 .filter_map(|quad| match quad {
31 Err(error) => Some(Err(error)),
32 Ok(mut quad) => match self.representative(&quad) {
33 Err(error) => Some(Err(error)),
34 Ok(false) => None,
35 Ok(true) => {
36 quad.graph_name = GraphName::DefaultGraph;
37 Some(Ok(quad))
38 }
39 },
40 })
41 }
42
43 fn representative(&self, quad: &Quad) -> Result<bool> {
44 let first = self
47 .source
48 .matching_quads(
49 Some(quad.subject.as_ref()),
50 Some(quad.predicate.as_ref()),
51 Some(quad.object.as_ref()),
52 None,
53 )
54 .next()
55 .transpose()?
56 .ok_or_else(|| Error::Runtime("Graph changed during union read".into()))?;
57 Ok(first.graph_name == quad.graph_name)
58 }
59}
60
61impl<'source, G: GraphRead + ?Sized> GraphRead for UnionGraph<'source, G> {
62 fn matching_quads<'a>(
63 &'a self,
64 subject: Option<NamedOrBlankNodeRef<'_>>,
65 predicate: Option<NamedNodeRef<'_>>,
66 object: Option<TermRef<'_>>,
67 graph: Option<GraphNameRef<'_>>,
68 ) -> impl Iterator<Item = Result<Quad>> + use<'a, 'source, G> {
69 let matches: Matches<'a> = match graph {
70 Some(GraphNameRef::DefaultGraph) => {
71 Box::new(self.default_matches(subject, predicate, object))
72 }
73 Some(_) => Box::new(
74 self.source
75 .matching_quads(subject, predicate, object, graph),
76 ),
77 None => Box::new(
78 self.default_matches(subject, predicate, object).chain(
79 self.source
80 .matching_quads(subject, predicate, object, None)
81 .filter(|q| match q {
82 Ok(q) => !q.graph_name.is_default_graph(),
83 Err(_) => true,
84 }),
85 ),
86 ),
87 };
88 matches
89 }
90
91 fn named_graphs(&self) -> impl Iterator<Item = Result<NamedOrBlankNode>> + '_ {
92 self.source.named_graphs()
93 }
94
95 fn contains_named_graph(&self, graph: &NamedOrBlankNode) -> Result<bool> {
96 self.source.contains_named_graph(graph)
97 }
98}
99
100pub struct OverlayGraph<'a, G: GraphRead + ?Sized, S: GraphRead + ?Sized> {
104 source: &'a G,
105 shapes: UnionGraph<'a, S>,
106 graph: GraphName,
107}
108
109impl<'a, G: GraphRead + ?Sized, S: GraphRead + ?Sized> OverlayGraph<'a, G, S> {
110 pub fn new(source: &'a G, shapes: &'a S, graph: NamedOrBlankNode) -> Result<Self> {
112 if source.contains_named_graph(&graph)? {
113 return Err(Error::Invalid(format!(
114 "Overlay graph already exists: {graph}"
115 )));
116 }
117 Ok(Self {
118 source,
119 shapes: UnionGraph::new(shapes),
120 graph: graph.into(),
121 })
122 }
123
124 fn overlay_matches<'b>(
125 &'b self,
126 subject: Option<NamedOrBlankNodeRef<'_>>,
127 predicate: Option<NamedNodeRef<'_>>,
128 object: Option<TermRef<'_>>,
129 ) -> impl Iterator<Item = Result<Quad>> + use<'a, 'b, G, S> {
130 self.shapes
131 .matching_quads(subject, predicate, object, Some(GraphNameRef::DefaultGraph))
132 .map(|quad| {
133 quad.map(|mut quad| {
134 quad.graph_name = self.graph.clone();
135 quad
136 })
137 })
138 }
139}
140
141impl<'source, G: GraphRead + ?Sized, S: GraphRead + ?Sized> GraphRead
142 for OverlayGraph<'source, G, S>
143{
144 fn matching_quads<'a>(
145 &'a self,
146 subject: Option<NamedOrBlankNodeRef<'_>>,
147 predicate: Option<NamedNodeRef<'_>>,
148 object: Option<TermRef<'_>>,
149 graph: Option<GraphNameRef<'_>>,
150 ) -> impl Iterator<Item = Result<Quad>> + use<'a, 'source, G, S> {
151 let matches: Matches<'a> = match graph {
152 Some(graph) if graph == self.graph.as_ref() => {
153 Box::new(self.overlay_matches(subject, predicate, object))
154 }
155 Some(_) => Box::new(
156 self.source
157 .matching_quads(subject, predicate, object, graph),
158 ),
159 None => Box::new(
160 self.source
161 .matching_quads(subject, predicate, object, None)
162 .chain(self.overlay_matches(subject, predicate, object)),
163 ),
164 };
165 matches
166 }
167
168 fn named_graphs(&self) -> impl Iterator<Item = Result<NamedOrBlankNode>> + '_ {
169 self.source
170 .named_graphs()
171 .chain(std::iter::once(Ok(match &self.graph {
172 GraphName::NamedNode(node) => node.clone().into(),
173 GraphName::BlankNode(node) => node.clone().into(),
174 GraphName::DefaultGraph => unreachable!("overlay reserves a named graph"),
175 })))
176 }
177
178 fn contains_named_graph(&self, graph: &NamedOrBlankNode) -> Result<bool> {
179 Ok(self.graph.as_ref() == GraphNameRef::from(graph.as_ref())
180 || self.source.contains_named_graph(graph)?)
181 }
182}