1use std::collections::HashMap;
7use std::path::Path;
8use std::sync::{Arc, Mutex};
9
10use anyhow::Result;
11
12use crate::graph::*;
13use crate::labels::EdgeLabel;
14use crate::properties;
15use crate::{NodeType, SWHID};
16
17mod contents;
18mod label_names;
19mod maps;
20mod persons;
21mod strings;
22mod timestamps;
23
24#[derive(Clone, Debug, PartialEq, Eq)]
26pub enum PropertyAccess {
27 Swhid(NodeId),
29 NodeType(NodeId),
30 NodeId(SWHID),
31 NodeIdForInvalidBytes([u8; SWHID::BYTES_SIZE]),
32 NodeIdForInvalidStrArray([u8; 50]),
33 NodeIdForInvalidString(String),
34
35 IsSkippedContent(NodeId),
37 ContentLength(NodeId),
38
39 AuthorId(NodeId),
41 CommitterId(NodeId),
42
43 Message(NodeId),
45 TagName(NodeId),
46
47 AuthorTimestamp(NodeId),
49 AuthorTimestampOffset(NodeId),
50 CommitterTimestamp(NodeId),
51 CommitterTimestampOffset(NodeId),
52
53 LabelNames,
56}
57
58#[derive(Clone, Debug, PartialEq, Eq)]
60pub enum GraphAccessRecord {
61 Path,
63 IsTransposed,
64 NumNodes,
65 HasNode(NodeId),
66 NumArcs,
67 NumNodesByType,
68 NumArcsByType,
69 HasArc(NodeId, NodeId),
70
71 Property(PropertyAccess),
73
74 Successors(NodeId),
76 Outdegree(NodeId),
77
78 LabeledSuccessors(NodeId),
80 UntypedLabeledSuccessors(NodeId),
81
82 Predecessors(NodeId),
84 Indegree(NodeId),
85
86 LabeledPredecessors(NodeId),
88 UntypedLabeledPredecessors(NodeId),
89}
90
91#[derive(Debug)]
92struct GraphSpyInner<G: SwhGraph> {
93 graph: G,
94}
95
96#[derive(Debug)]
98pub struct GraphSpy<
99 G: SwhGraph,
100 MAPS: properties::MaybeMaps = properties::NoMaps,
101 TIMESTAMPS: properties::MaybeTimestamps = properties::NoTimestamps,
102 PERSONS: properties::MaybePersons = properties::NoPersons,
103 CONTENTS: properties::MaybeContents = properties::NoContents,
104 STRINGS: properties::MaybeStrings = properties::NoStrings,
105 LABELNAMES: properties::MaybeLabelNames = properties::NoLabelNames,
106> {
107 inner: Arc<GraphSpyInner<G>>,
108 pub history: Arc<Mutex<Vec<GraphAccessRecord>>>,
110 properties:
111 properties::SwhGraphProperties<MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>,
112}
113
114impl<G: SwhGraph>
115 GraphSpy<
116 G,
117 properties::NoMaps,
118 properties::NoTimestamps,
119 properties::NoPersons,
120 properties::NoContents,
121 properties::NoStrings,
122 properties::NoLabelNames,
123 >
124{
125 pub fn new(graph: G) -> Self {
126 let path = graph.path().to_owned();
127 let num_nodes = graph.num_nodes();
128 GraphSpy {
129 inner: Arc::new(GraphSpyInner { graph }),
130 history: Arc::new(Mutex::new(Vec::new())),
131 properties: properties::SwhGraphProperties {
132 path,
133 num_nodes,
134 maps: properties::NoMaps,
135 timestamps: properties::NoTimestamps,
136 persons: properties::NoPersons,
137 contents: properties::NoContents,
138 strings: properties::NoStrings,
139 label_names: properties::NoLabelNames,
140 label_names_are_in_base64_order: Default::default(),
141 },
142 }
143 }
144}
145
146impl<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
147 GraphSpy<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
148where
149 G: SwhGraph,
150 MAPS: properties::MaybeMaps,
151 TIMESTAMPS: properties::MaybeTimestamps,
152 PERSONS: properties::MaybePersons,
153 CONTENTS: properties::MaybeContents,
154 STRINGS: properties::MaybeStrings,
155 LABELNAMES: properties::MaybeLabelNames,
156{
157 fn record(&self, record: GraphAccessRecord) {
158 self.history.lock().unwrap().push(record);
159 }
160
161 pub fn graph(&self) -> &G {
162 &self.inner.graph
163 }
164}
165
166impl<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES> SwhGraph
167 for GraphSpy<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
168where
169 G: SwhGraph,
170 MAPS: properties::MaybeMaps,
171 TIMESTAMPS: properties::MaybeTimestamps,
172 PERSONS: properties::MaybePersons,
173 CONTENTS: properties::MaybeContents,
174 STRINGS: properties::MaybeStrings,
175 LABELNAMES: properties::MaybeLabelNames,
176{
177 fn path(&self) -> &Path {
178 self.record(GraphAccessRecord::Path);
179 self.inner.graph.path()
180 }
181 fn is_transposed(&self) -> bool {
182 self.record(GraphAccessRecord::IsTransposed);
183 self.inner.graph.is_transposed()
184 }
185 fn num_nodes(&self) -> usize {
186 self.record(GraphAccessRecord::NumNodes);
187 self.inner.graph.num_nodes()
188 }
189 fn has_node(&self, node_id: NodeId) -> bool {
190 self.record(GraphAccessRecord::HasNode(node_id));
191 self.inner.graph.has_node(node_id)
192 }
193 fn num_arcs(&self) -> u64 {
194 self.record(GraphAccessRecord::NumArcs);
195 self.inner.graph.num_arcs()
196 }
197 fn num_nodes_by_type(&self) -> Result<HashMap<NodeType, usize>> {
198 self.record(GraphAccessRecord::NumNodesByType);
199 self.inner.graph.num_nodes_by_type()
200 }
201 fn num_arcs_by_type(&self) -> Result<HashMap<(NodeType, NodeType), usize>> {
202 self.record(GraphAccessRecord::NumArcsByType);
203 self.inner.graph.num_arcs_by_type()
204 }
205 fn has_arc(&self, src_node_id: NodeId, dst_node_id: NodeId) -> bool {
206 self.record(GraphAccessRecord::HasArc(src_node_id, dst_node_id));
207 self.inner.graph.has_arc(src_node_id, dst_node_id)
208 }
209}
210
211impl<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES> SwhForwardGraph
212 for GraphSpy<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
213where
214 G: SwhForwardGraph,
215 MAPS: properties::MaybeMaps,
216 TIMESTAMPS: properties::MaybeTimestamps,
217 PERSONS: properties::MaybePersons,
218 CONTENTS: properties::MaybeContents,
219 STRINGS: properties::MaybeStrings,
220 LABELNAMES: properties::MaybeLabelNames,
221{
222 type Successors<'succ>
223 = <G as SwhForwardGraph>::Successors<'succ>
224 where
225 Self: 'succ;
226
227 fn successors(&self, node_id: NodeId) -> Self::Successors<'_> {
228 self.record(GraphAccessRecord::Successors(node_id));
229 self.inner.graph.successors(node_id)
230 }
231 fn outdegree(&self, node_id: NodeId) -> usize {
232 self.record(GraphAccessRecord::Outdegree(node_id));
233 self.inner.graph.outdegree(node_id)
234 }
235}
236
237impl<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES> SwhLabeledForwardGraph
238 for GraphSpy<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
239where
240 G: SwhLabeledForwardGraph,
241 MAPS: properties::Maps,
242 TIMESTAMPS: properties::MaybeTimestamps,
243 PERSONS: properties::MaybePersons,
244 CONTENTS: properties::MaybeContents,
245 STRINGS: properties::MaybeStrings,
246 LABELNAMES: properties::MaybeLabelNames,
247{
248 type LabeledArcs<'arc>
249 = <G as SwhLabeledForwardGraph>::LabeledArcs<'arc>
250 where
251 Self: 'arc;
252 type LabeledSuccessors<'succ>
253 = <G as SwhLabeledForwardGraph>::LabeledSuccessors<'succ>
254 where
255 Self: 'succ;
256
257 fn untyped_labeled_successors(&self, node_id: NodeId) -> Self::LabeledSuccessors<'_> {
258 self.record(GraphAccessRecord::UntypedLabeledSuccessors(node_id));
259 self.inner.graph.untyped_labeled_successors(node_id)
260 }
261
262 fn labeled_successors(
263 &self,
264 node_id: NodeId,
265 ) -> impl Iterator<Item = (usize, impl Iterator<Item = EdgeLabel>)>
266 + IntoFlattenedLabeledArcsIterator<EdgeLabel>
267 + '_ {
268 self.record(GraphAccessRecord::LabeledSuccessors(node_id));
269 self.inner.graph.labeled_successors(node_id)
270 }
271}
272
273impl<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES> SwhBackwardGraph
274 for GraphSpy<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
275where
276 G: SwhBackwardGraph,
277 MAPS: properties::MaybeMaps,
278 TIMESTAMPS: properties::MaybeTimestamps,
279 PERSONS: properties::MaybePersons,
280 CONTENTS: properties::MaybeContents,
281 STRINGS: properties::MaybeStrings,
282 LABELNAMES: properties::MaybeLabelNames,
283{
284 type Predecessors<'succ>
285 = <G as SwhBackwardGraph>::Predecessors<'succ>
286 where
287 Self: 'succ;
288
289 fn predecessors(&self, node_id: NodeId) -> Self::Predecessors<'_> {
290 self.record(GraphAccessRecord::Predecessors(node_id));
291 self.inner.graph.predecessors(node_id)
292 }
293 fn indegree(&self, node_id: NodeId) -> usize {
294 self.record(GraphAccessRecord::Indegree(node_id));
295 self.inner.graph.indegree(node_id)
296 }
297}
298
299impl<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES> SwhLabeledBackwardGraph
300 for GraphSpy<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
301where
302 G: SwhLabeledBackwardGraph,
303 MAPS: properties::Maps,
304 TIMESTAMPS: properties::MaybeTimestamps,
305 PERSONS: properties::MaybePersons,
306 CONTENTS: properties::MaybeContents,
307 STRINGS: properties::MaybeStrings,
308 LABELNAMES: properties::MaybeLabelNames,
309{
310 type LabeledArcs<'arc>
311 = <G as SwhLabeledBackwardGraph>::LabeledArcs<'arc>
312 where
313 Self: 'arc;
314 type LabeledPredecessors<'succ>
315 = <G as SwhLabeledBackwardGraph>::LabeledPredecessors<'succ>
316 where
317 Self: 'succ;
318
319 fn untyped_labeled_predecessors(&self, node_id: NodeId) -> Self::LabeledPredecessors<'_> {
320 self.record(GraphAccessRecord::UntypedLabeledPredecessors(node_id));
321 self.inner.graph.untyped_labeled_predecessors(node_id)
322 }
323 fn labeled_predecessors(
324 &self,
325 node_id: NodeId,
326 ) -> impl IntoIterator<Item = (usize, impl Iterator<Item = crate::labels::EdgeLabel>)>
327 + IntoFlattenedLabeledArcsIterator<EdgeLabel>
328 + '_ {
329 self.record(GraphAccessRecord::LabeledPredecessors(node_id));
330 self.inner.graph.labeled_predecessors(node_id)
331 }
332}
333
334impl<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES> SwhGraphWithProperties
335 for GraphSpy<G, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
336where
337 G: SwhGraphWithProperties,
338 MAPS: properties::MaybeMaps,
339 TIMESTAMPS: properties::MaybeTimestamps,
340 PERSONS: properties::MaybePersons,
341 CONTENTS: properties::MaybeContents,
342 STRINGS: properties::MaybeStrings,
343 LABELNAMES: properties::MaybeLabelNames,
344{
345 type Maps = MAPS;
346 type Timestamps = TIMESTAMPS;
347 type Persons = PERSONS;
348 type Contents = CONTENTS;
349 type Strings = STRINGS;
350 type LabelNames = LABELNAMES;
351
352 fn properties(
353 &self,
354 ) -> &properties::SwhGraphProperties<
355 Self::Maps,
356 Self::Timestamps,
357 Self::Persons,
358 Self::Contents,
359 Self::Strings,
360 Self::LabelNames,
361 > {
362 &self.properties
363 }
364}