Skip to main content

pattern_core/graph/
graph_view.rs

1//! GraphView: a universal view over a classified graph.
2//!
3//! Ported from `Pattern.Graph.GraphView` in the Haskell reference implementation.
4//!
5//! A `GraphView` pairs a `GraphQuery` (read-only query interface) with a flat
6//! list of classified elements. All graph transformations operate over `GraphView`
7//! and produce a new `GraphView`; only `materialize` converts back to `PatternGraph`.
8
9use std::hash::Hash;
10
11use crate::graph::graph_classifier::{GraphClass, GraphClassifier, GraphValue};
12use crate::graph::graph_query::GraphQuery;
13use crate::pattern::Pattern;
14use crate::pattern_graph::PatternGraph;
15use crate::reconcile::{HasIdentity, Mergeable, ReconciliationPolicy, Refinable};
16use crate::subject::Symbol;
17
18// ============================================================================
19// GraphView<Extra, V>
20// ============================================================================
21
22/// A universal graph-like interface: a query over the graph plus a list of
23/// classified elements.
24///
25/// `view_query` provides read-only access to the underlying graph structure.
26/// `view_elements` is the flat list of all elements in the view, each tagged
27/// with its `GraphClass`.
28///
29/// Transformations consume the view and produce a new one. Clone is available
30/// because `GraphQuery` is cheap to clone (Rc/Arc pointer increments only).
31pub struct GraphView<Extra, V: GraphValue> {
32    pub view_query: GraphQuery<V>,
33    pub view_elements: Vec<(GraphClass<Extra>, Pattern<V>)>,
34}
35
36impl<Extra: Clone, V: GraphValue + Clone> Clone for GraphView<Extra, V> {
37    fn clone(&self) -> Self {
38        GraphView {
39            view_query: self.view_query.clone(),
40            view_elements: self.view_elements.clone(),
41        }
42    }
43}
44
45// ============================================================================
46// from_pattern_graph
47// ============================================================================
48
49/// Builds the flat list of classified elements from a graph (shared by Rc/Arc variants).
50fn view_elements_from_graph<Extra, V>(
51    classifier: &GraphClassifier<Extra, V>,
52    graph: &PatternGraph<Extra, V>,
53) -> Vec<(GraphClass<Extra>, Pattern<V>)>
54where
55    Extra: Clone,
56    V: GraphValue + Clone,
57{
58    let mut view_elements: Vec<(GraphClass<Extra>, Pattern<V>)> = Vec::new();
59    for p in graph.pg_nodes.values() {
60        view_elements.push(((classifier.classify)(p), p.clone()));
61    }
62    for p in graph.pg_relationships.values() {
63        view_elements.push(((classifier.classify)(p), p.clone()));
64    }
65    for p in graph.pg_walks.values() {
66        view_elements.push(((classifier.classify)(p), p.clone()));
67    }
68    for p in graph.pg_annotations.values() {
69        view_elements.push(((classifier.classify)(p), p.clone()));
70    }
71    for (_, p) in graph.pg_other.values() {
72        view_elements.push(((classifier.classify)(p), p.clone()));
73    }
74    view_elements
75}
76
77/// Builds a `GraphView` from an existing `PatternGraph` and classifier.
78///
79/// The view's query is built from the graph (shared ownership via Rc/Arc).
80/// `view_elements` contains every element in the graph classified by the
81/// given classifier, in an unspecified but stable order.
82#[cfg(not(feature = "thread-safe"))]
83pub fn from_pattern_graph<Extra, V>(
84    classifier: &GraphClassifier<Extra, V>,
85    graph: &PatternGraph<Extra, V>,
86) -> GraphView<Extra, V>
87where
88    Extra: Clone + 'static,
89    V: GraphValue + Clone + 'static,
90    V::Id: Clone + Eq + Hash + 'static,
91{
92    use std::rc::Rc;
93
94    let rc_graph = Rc::new(PatternGraph {
95        pg_nodes: graph.pg_nodes.clone(),
96        pg_relationships: graph.pg_relationships.clone(),
97        pg_walks: graph.pg_walks.clone(),
98        pg_annotations: graph.pg_annotations.clone(),
99        pg_other: graph.pg_other.clone(),
100        pg_conflicts: graph.pg_conflicts.clone(),
101    });
102    let view_query = crate::pattern_graph::from_pattern_graph(rc_graph);
103    let view_elements = view_elements_from_graph(classifier, graph);
104    GraphView {
105        view_query,
106        view_elements,
107    }
108}
109
110#[cfg(feature = "thread-safe")]
111pub fn from_pattern_graph<Extra, V>(
112    classifier: &GraphClassifier<Extra, V>,
113    graph: &PatternGraph<Extra, V>,
114) -> GraphView<Extra, V>
115where
116    Extra: Clone + Send + Sync + 'static,
117    V: GraphValue + Clone + Send + Sync + 'static,
118    V::Id: Clone + Eq + Hash + Send + Sync + 'static,
119{
120    use std::sync::Arc;
121
122    let arc_graph = Arc::new(PatternGraph {
123        pg_nodes: graph.pg_nodes.clone(),
124        pg_relationships: graph.pg_relationships.clone(),
125        pg_walks: graph.pg_walks.clone(),
126        pg_annotations: graph.pg_annotations.clone(),
127        pg_other: graph.pg_other.clone(),
128        pg_conflicts: graph.pg_conflicts.clone(),
129    });
130    let view_query = crate::pattern_graph::from_pattern_graph(arc_graph);
131    let view_elements = view_elements_from_graph(classifier, graph);
132    GraphView {
133        view_query,
134        view_elements,
135    }
136}
137
138// ============================================================================
139// from_graph_lens (DEFERRED)
140// ============================================================================
141
142/// Builds a `GraphView` from a `GraphLens`.
143///
144/// **Deferred**: `GraphLens` has not yet been ported to pattern-rs.
145/// This constructor will be implemented when `GraphLens` is available.
146/// The signature accepts a classifier and a lens placeholder so that callers
147/// can use the same API shape; it panics at runtime until the type is ported.
148#[allow(dead_code)]
149pub fn from_graph_lens<Extra, V, L>(
150    _classifier: &GraphClassifier<Extra, V>,
151    _lens: L,
152) -> GraphView<Extra, V>
153where
154    V: GraphValue,
155{
156    todo!("from_graph_lens: deferred until GraphLens is ported to pattern-rs")
157}
158
159// ============================================================================
160// materialize
161// ============================================================================
162
163/// Consumes a `GraphView` and produces a `PatternGraph`.
164///
165/// Each element in `view_elements` is inserted into a new graph using the
166/// given classifier and reconciliation policy. Duplicate identities are resolved
167/// by the policy.
168pub fn materialize<Extra, V>(
169    classifier: &GraphClassifier<Extra, V>,
170    policy: &ReconciliationPolicy<V::MergeStrategy>,
171    view: GraphView<Extra, V>,
172) -> PatternGraph<Extra, V>
173where
174    V: GraphValue<Id = Symbol>
175        + HasIdentity<V, Symbol>
176        + Mergeable
177        + Refinable
178        + PartialEq
179        + Clone
180        + 'static,
181    Extra: 'static,
182{
183    crate::pattern_graph::from_patterns_with_policy(
184        classifier,
185        policy,
186        view.view_elements.into_iter().map(|(_, p)| p),
187    )
188}