ploidy_core/ir/views/
mod.rs1use std::{any::TypeId, fmt::Debug};
29
30use atomic_refcell::{AtomicRef, AtomicRefMut};
31use petgraph::{
32 Direction,
33 graph::NodeIndex,
34 visit::{Bfs, EdgeFiltered, EdgeRef},
35};
36use ref_cast::{RefCastCustom, ref_cast_custom};
37
38use super::{
39 graph::{CookedGraph, EdgeKind, Extension, ExtensionMap, Traversal, Traverse},
40 types::GraphType,
41};
42
43pub mod any;
44pub mod container;
45pub mod enum_;
46pub mod inline;
47pub mod ir;
48pub mod operation;
49pub mod primitive;
50pub mod schema;
51pub mod struct_;
52pub mod tagged;
53pub mod untagged;
54
55use self::{inline::InlineTypeView, ir::TypeView, operation::OperationView};
56
57pub trait View<'a> {
59 fn inlines(&self) -> impl Iterator<Item = InlineTypeView<'a>> + use<'a, Self>;
62
63 fn used_by(&self) -> impl Iterator<Item = OperationView<'a>> + use<'a, Self>;
67
68 fn dependencies(&self) -> impl Iterator<Item = TypeView<'a>> + use<'a, Self>;
73
74 fn dependents(&self) -> impl Iterator<Item = TypeView<'a>> + use<'a, Self>;
79
80 fn traverse<F>(
91 &self,
92 reach: Reach,
93 filter: F,
94 ) -> impl Iterator<Item = TypeView<'a>> + use<'a, Self, F>
95 where
96 F: Fn(EdgeKind, &TypeView<'a>) -> Traversal;
97}
98
99pub trait ExtendableView<'a>: View<'a> {
105 fn extensions(&self) -> &ViewExtensions<Self>
107 where
108 Self: Sized;
109
110 fn extensions_mut(&mut self) -> &mut ViewExtensions<Self>
112 where
113 Self: Sized;
114}
115
116impl<'a, T> View<'a> for T
117where
118 T: ViewNode<'a>,
119{
120 #[inline]
121 fn inlines(&self) -> impl Iterator<Item = InlineTypeView<'a>> + use<'a, T> {
122 let cooked = self.cooked();
123 let filtered = EdgeFiltered::from_fn(&cooked.graph, |e| {
125 matches!(cooked.graph[e.target()], GraphType::Inline(_))
126 });
127 let mut bfs = Bfs::new(&cooked.graph, self.index());
128 std::iter::from_fn(move || bfs.next(&filtered)).filter_map(|index| {
129 match cooked.graph[index] {
130 GraphType::Inline(ty) => Some(InlineTypeView::new(cooked, index, ty)),
131 _ => None,
132 }
133 })
134 }
135
136 #[inline]
137 fn used_by(&self) -> impl Iterator<Item = OperationView<'a>> + use<'a, T> {
138 let cooked = self.cooked();
139 let meta = &cooked.metadata.schemas[self.index().index()];
140 meta.used_by.iter().map(|op| OperationView::new(cooked, op))
141 }
142
143 #[inline]
144 fn dependencies(&self) -> impl Iterator<Item = TypeView<'a>> + use<'a, T> {
145 let cooked = self.cooked();
146 let meta = &cooked.metadata.schemas[self.index().index()];
147 meta.dependencies
148 .ones()
149 .map(NodeIndex::new)
150 .map(|index| TypeView::new(cooked, index))
151 }
152
153 #[inline]
154 fn dependents(&self) -> impl Iterator<Item = TypeView<'a>> + use<'a, T> {
155 let cooked = self.cooked();
156 let meta = &cooked.metadata.schemas[self.index().index()];
157 meta.dependents
158 .ones()
159 .map(NodeIndex::new)
160 .map(move |index| TypeView::new(cooked, index))
161 }
162
163 #[inline]
164 fn traverse<F>(
165 &self,
166 reach: Reach,
167 filter: F,
168 ) -> impl Iterator<Item = TypeView<'a>> + use<'a, T, F>
169 where
170 F: Fn(EdgeKind, &TypeView<'a>) -> Traversal,
171 {
172 let cooked = self.cooked();
173 let t = Traverse::from_neighbors(
174 &cooked.graph,
175 self.index(),
176 match reach {
177 Reach::Dependencies => Direction::Outgoing,
178 Reach::Dependents => Direction::Incoming,
179 },
180 );
181 t.run(move |kind, index| {
182 let view = TypeView::new(cooked, index);
183 filter(kind, &view)
184 })
185 .map(|index| TypeView::new(cooked, index))
186 }
187}
188
189impl<'a, T> ExtendableView<'a> for T
190where
191 T: ViewNode<'a>,
192{
193 #[inline]
194 fn extensions(&self) -> &ViewExtensions<Self> {
195 ViewExtensions::new(self)
196 }
197
198 #[inline]
199 fn extensions_mut(&mut self) -> &mut ViewExtensions<Self> {
200 ViewExtensions::new_mut(self)
201 }
202}
203
204pub(crate) trait ViewNode<'a> {
205 fn cooked(&self) -> &'a CookedGraph<'a>;
206 fn index(&self) -> NodeIndex<usize>;
207}
208
209impl<'graph, T> internal::Extendable<'graph> for T
210where
211 T: ViewNode<'graph>,
212{
213 #[inline]
214 fn ext<'view>(&'view self) -> AtomicRef<'view, ExtensionMap>
215 where
216 'graph: 'view,
217 {
218 self.cooked().metadata.schemas[self.index().index()]
219 .extensions
220 .borrow()
221 }
222
223 #[inline]
224 fn ext_mut<'b>(&'b mut self) -> AtomicRefMut<'b, ExtensionMap>
225 where
226 'graph: 'b,
227 {
228 self.cooked().metadata.schemas[self.index().index()]
229 .extensions
230 .borrow_mut()
231 }
232}
233
234#[derive(RefCastCustom)]
236#[repr(transparent)]
237pub struct ViewExtensions<X>(X);
238
239impl<X> ViewExtensions<X> {
240 #[ref_cast_custom]
241 fn new(view: &X) -> &Self;
242
243 #[ref_cast_custom]
244 fn new_mut(view: &mut X) -> &mut Self;
245}
246
247impl<'a, X: internal::Extendable<'a>> ViewExtensions<X> {
248 #[inline]
251 pub fn get<'b, T: Send + Sync + 'static>(&'b self) -> Option<AtomicRef<'b, T>>
252 where
253 'a: 'b,
254 {
255 AtomicRef::filter_map(self.0.ext(), |ext| {
256 Some(
257 ext.get(&TypeId::of::<T>())?
258 .as_ref()
259 .downcast_ref::<T>()
260 .unwrap(),
261 )
262 })
263 }
264
265 #[inline]
268 pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) -> Option<T> {
269 self.0
270 .ext_mut()
271 .insert(TypeId::of::<T>(), Box::new(value))
272 .and_then(|old| *Extension::into_inner(old).downcast().unwrap())
273 }
274}
275
276impl<X> Debug for ViewExtensions<X> {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 f.debug_tuple("ViewExtensions").finish_non_exhaustive()
279 }
280}
281
282#[derive(Clone, Copy, Debug, Eq, PartialEq)]
284pub enum Reach {
285 Dependencies,
287 Dependents,
289}
290
291mod internal {
292 use atomic_refcell::{AtomicRef, AtomicRefMut};
293
294 use super::ExtensionMap;
295
296 pub trait Extendable<'graph> {
297 fn ext<'view>(&'view self) -> AtomicRef<'view, ExtensionMap>
310 where
311 'graph: 'view;
312
313 fn ext_mut<'view>(&'view mut self) -> AtomicRefMut<'view, ExtensionMap>
314 where
315 'graph: 'view;
316 }
317}