1use crate::{
2 AuditState, DependencyList, DerivedNode, GraphError, GraphResult, InputNode, NodeHandle,
3 NodeId, NodeKind, NodeMeta, OutputKey, OutputMeta, ResourceKey, Revision, ScopeId, ScopeMeta,
4 Transaction, TransactionId, TransactionOptions,
5 collection::{CollectionSpec, StoredCollection, StoredDiff},
6 derive::DerivedSpec,
7 input::{StoredInput, value_type},
8 output::OutputSpec,
9 output_payload::StoredOutput,
10 resource::ResourcePlanner,
11 topology::TopologyCache,
12};
13use std::collections::{BTreeMap, BTreeSet};
14
15pub struct Graph<C = ()> {
17 pub(crate) next_node_id: u64,
18 pub(crate) next_scope_id: u64,
19 pub(crate) next_output_key: u64,
20 pub(crate) next_transaction_id: TransactionId,
21 pub(crate) revision: Revision,
22 pub(crate) nodes: BTreeMap<NodeId, NodeMeta>,
23 pub(crate) scopes: BTreeMap<ScopeId, ScopeMeta>,
24 pub(crate) scope_children: BTreeMap<ScopeId, BTreeSet<ScopeId>>,
25 pub(crate) input_values: BTreeMap<NodeId, Box<dyn StoredInput>>,
26 pub(crate) derived_specs: BTreeMap<NodeId, DerivedSpec<C>>,
27 pub(crate) derived_values: BTreeMap<NodeId, Box<dyn StoredInput>>,
28 pub(crate) collection_specs: BTreeMap<NodeId, CollectionSpec<C>>,
29 pub(crate) collection_values: BTreeMap<NodeId, Box<dyn StoredCollection>>,
30 pub(crate) previous_collection_values: BTreeMap<NodeId, Box<dyn StoredCollection>>,
31 pub(crate) collection_diffs: BTreeMap<NodeId, Box<dyn StoredDiff>>,
32 pub(crate) resource_planners: Vec<ResourcePlanner<C>>,
33 pub(crate) resource_owners: BTreeMap<ResourceKey, BTreeSet<ScopeId>>,
34 pub(crate) output_specs: BTreeMap<OutputKey, OutputSpec<C>>,
35 pub(crate) output_values: BTreeMap<OutputKey, Box<dyn StoredOutput>>,
36 pub(crate) outputs: BTreeMap<OutputKey, OutputMeta>,
37 pub(crate) topology_cache: TopologyCache,
38 pub(crate) audit: AuditState,
39 pub(crate) transaction_open: bool,
40}
41
42impl<C> Graph<C> {
43 pub fn new_with_command_type() -> Self {
45 Self {
46 next_node_id: 1,
47 next_scope_id: 1,
48 next_output_key: 1,
49 next_transaction_id: TransactionId::default(),
50 revision: Revision::default(),
51 nodes: BTreeMap::new(),
52 scopes: BTreeMap::new(),
53 scope_children: BTreeMap::new(),
54 input_values: BTreeMap::new(),
55 derived_specs: BTreeMap::new(),
56 derived_values: BTreeMap::new(),
57 collection_specs: BTreeMap::new(),
58 collection_values: BTreeMap::new(),
59 previous_collection_values: BTreeMap::new(),
60 collection_diffs: BTreeMap::new(),
61 resource_planners: Vec::new(),
62 resource_owners: BTreeMap::new(),
63 output_specs: BTreeMap::new(),
64 output_values: BTreeMap::new(),
65 outputs: BTreeMap::new(),
66 topology_cache: TopologyCache::default(),
67 audit: AuditState::default(),
68 transaction_open: false,
69 }
70 }
71
72 pub fn revision(&self) -> Revision {
74 self.revision
75 }
76
77 pub fn begin_transaction(&mut self) -> GraphResult<Transaction<'_, C>> {
79 self.begin_transaction_with_options(TransactionOptions::default())
80 }
81
82 pub fn begin_transaction_with_options(
84 &mut self,
85 options: TransactionOptions,
86 ) -> GraphResult<Transaction<'_, C>> {
87 if self.transaction_open {
88 return Err(GraphError::NestedTransaction);
89 }
90
91 self.transaction_open = true;
92 let id = self.allocate_transaction_id();
93 Ok(Transaction::new(self, id, options))
94 }
95
96 pub(crate) fn create_scope_with_parent_direct(
97 &mut self,
98 id: ScopeId,
99 debug_name: impl Into<String>,
100 parent: Option<ScopeId>,
101 ) -> GraphResult<ScopeId> {
102 if let Some(parent) = parent {
103 let parent_meta = self.require_scope(parent)?;
104 if parent_meta.is_closed() {
105 return Err(GraphError::ScopeAlreadyClosed(parent));
106 }
107 }
108
109 self.scopes
110 .insert(id, ScopeMeta::new(id, debug_name, parent));
111 if let Some(parent) = parent {
112 self.scope_children.entry(parent).or_default().insert(id);
113 }
114 Ok(id)
115 }
116
117 pub(crate) fn input_direct<T>(
118 &mut self,
119 id: NodeId,
120 debug_name: impl Into<String>,
121 ) -> GraphResult<InputNode<T>>
122 where
123 T: Clone + PartialEq + Send + Sync + 'static,
124 {
125 let meta = NodeMeta::new(
126 id,
127 NodeKind::Input,
128 debug_name,
129 DependencyList::empty(),
130 self.revision,
131 Some(value_type::<T>()),
132 );
133 self.invalidate_topology_cache();
134 self.nodes.insert(id, meta);
135 Ok(InputNode::new(id))
136 }
137
138 pub(crate) fn derived_direct<T>(
139 &mut self,
140 id: NodeId,
141 debug_name: impl Into<String>,
142 dependencies: DependencyList,
143 derive: impl for<'ctx> Fn(&crate::DeriveContext<'ctx, C>) -> Result<T, crate::DeriveError>
144 + Send
145 + Sync
146 + 'static,
147 ) -> GraphResult<DerivedNode<T>>
148 where
149 T: Clone + PartialEq + Send + Sync + 'static,
150 {
151 self.validate_dependencies(id, &dependencies)?;
152 self.reject_collection_dependencies(&dependencies)?;
153 let meta = NodeMeta::new(
154 id,
155 NodeKind::Derived,
156 debug_name,
157 dependencies,
158 self.revision,
159 Some(value_type::<T>()),
160 );
161 self.invalidate_topology_cache();
162 self.nodes.insert(id, meta);
163 self.derived_specs.insert(id, DerivedSpec::<C>::new(derive));
164 Ok(DerivedNode::new(id))
165 }
166
167 pub(crate) fn attach_node_to_scope_direct(
168 &mut self,
169 node_id: NodeId,
170 scope: ScopeId,
171 ) -> GraphResult<()> {
172 let scope_meta = self.require_scope(scope)?;
173 if scope_meta.is_closed() {
174 return Err(GraphError::ScopeAlreadyClosed(scope));
175 }
176
177 let node_meta = self
178 .nodes
179 .get_mut(&node_id)
180 .ok_or(GraphError::UnknownNode(node_id))?;
181
182 if node_meta.owning_scope().is_some() {
183 return Err(GraphError::NodeAlreadyAttached(node_id));
184 }
185
186 node_meta.attach_scope(scope);
187 Ok(())
188 }
189
190 pub fn node_meta<H: NodeHandle>(&self, node: H) -> Option<&NodeMeta> {
192 self.nodes.get(&node.id())
193 }
194
195 pub fn node_meta_by_id(&self, id: NodeId) -> Option<&NodeMeta> {
197 self.nodes.get(&id)
198 }
199
200 pub fn scope_meta(&self, id: ScopeId) -> Option<&ScopeMeta> {
202 self.scopes.get(&id)
203 }
204
205 pub fn output_meta(&self, key: OutputKey) -> Option<&OutputMeta> {
207 self.outputs.get(&key)
208 }
209
210 pub fn dependencies<H: NodeHandle>(&self, node: H) -> Option<&DependencyList> {
212 self.node_meta(node).map(NodeMeta::dependencies)
213 }
214
215 pub fn nodes(&self) -> impl Iterator<Item = &NodeMeta> {
217 self.nodes.values()
218 }
219
220 pub fn scopes(&self) -> impl Iterator<Item = &ScopeMeta> {
222 self.scopes.values()
223 }
224
225 pub(crate) fn allocate_node_id(&mut self) -> NodeId {
226 let id = NodeId::from_index(self.next_node_id);
227 self.next_node_id += 1;
228 id
229 }
230
231 pub(crate) fn allocate_scope_id(&mut self) -> ScopeId {
232 let id = ScopeId::from_index(self.next_scope_id);
233 self.next_scope_id += 1;
234 id
235 }
236
237 pub(crate) fn allocate_output_key(&mut self) -> OutputKey {
238 let key = OutputKey::from_index(self.next_output_key);
239 self.next_output_key += 1;
240 key
241 }
242
243 fn allocate_transaction_id(&mut self) -> TransactionId {
244 self.next_transaction_id = self.next_transaction_id.next();
245 self.next_transaction_id
246 }
247
248 pub(crate) fn require_scope(&self, id: ScopeId) -> GraphResult<&ScopeMeta> {
249 self.scopes.get(&id).ok_or(GraphError::UnknownScope(id))
250 }
251}