1use std::{any::TypeId as StdTypeId, fmt::Debug};
32
33use atomic_refcell::{AtomicRef, AtomicRefMut};
34use petgraph::{
35 graph::NodeIndex,
36 visit::{Bfs, EdgeFiltered, EdgeRef},
37};
38use ref_cast::{RefCastCustom, ref_cast_custom};
39
40use super::{
41 graph::{CookedGraph, Extension, ExtensionMap},
42 types::GraphType,
43};
44
45pub mod any;
46pub mod container;
47pub mod enum_;
48pub mod inline;
49pub mod ir;
50pub mod operation;
51pub mod path;
52pub mod primitive;
53pub mod schema;
54pub mod struct_;
55pub mod tagged;
56pub mod untagged;
57
58use self::{inline::InlineTypeView, ir::TypeView, operation::OperationView};
59
60pub trait View<'graph, 'a: 'graph> {
62 fn inlines(&self) -> impl Iterator<Item = InlineTypeView<'graph, 'a>> + use<'graph, 'a, Self>;
65
66 fn used_by(&self) -> impl Iterator<Item = OperationView<'graph, 'a>> + use<'graph, 'a, Self>;
70
71 fn dependencies(&self) -> impl Iterator<Item = TypeView<'graph, 'a>> + use<'graph, 'a, Self>;
76
77 fn dependents(&self) -> impl Iterator<Item = TypeView<'graph, 'a>> + use<'graph, 'a, Self>;
82
83 fn hashable(&self) -> bool;
85
86 fn defaultable(&self) -> bool;
88}
89
90pub trait HasTypeId {
92 fn id(&self) -> TypeId;
94}
95
96#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
98pub struct TypeId(pub(in crate::ir) NodeIndex<usize>);
99
100pub trait HasResource<'a> {
102 fn resource(&self) -> Option<&'a str>;
104}
105
106pub trait ExtendableView<'graph, 'a: 'graph>: View<'graph, 'a> {
112 fn extensions(&self) -> &ViewExtensions<Self>
114 where
115 Self: Sized;
116
117 fn extensions_mut(&mut self) -> &mut ViewExtensions<Self>
119 where
120 Self: Sized;
121}
122
123impl<'graph, 'a: 'graph, T> HasTypeId for T
124where
125 T: ViewNode<'graph, 'a>,
126{
127 fn id(&self) -> TypeId {
128 TypeId(self.index())
129 }
130}
131
132impl<'graph, 'a: 'graph, T> View<'graph, 'a> for T
133where
134 T: ViewNode<'graph, 'a>,
135{
136 #[inline]
137 fn inlines(&self) -> impl Iterator<Item = InlineTypeView<'graph, 'a>> + use<'graph, 'a, T> {
138 let cooked = self.cooked();
139 let filtered = EdgeFiltered::from_fn(&cooked.graph, |e| {
142 !e.weight().shadow() && matches!(cooked.graph[e.target()], GraphType::Inline(_))
143 });
144 let mut bfs = Bfs::new(&cooked.graph, self.index());
145 std::iter::from_fn(move || bfs.next(&filtered))
146 .skip(1) .filter_map(|index| match cooked.graph[index] {
148 GraphType::Inline(ty) => Some(InlineTypeView::new(cooked, index, ty)),
149 _ => None,
150 })
151 }
152
153 #[inline]
154 fn used_by(&self) -> impl Iterator<Item = OperationView<'graph, 'a>> + use<'graph, 'a, T> {
155 let cooked = self.cooked();
156 cooked.metadata.used_by[self.index().index()]
157 .iter()
158 .map(|op| OperationView::new(cooked, op))
159 }
160
161 #[inline]
162 fn dependencies(&self) -> impl Iterator<Item = TypeView<'graph, 'a>> + use<'graph, 'a, T> {
163 let cooked = self.cooked();
164 let start = self.index();
165 cooked
166 .metadata
167 .closure
168 .dependencies_of(start)
169 .filter(move |&index| index != start)
170 .map(|index| TypeView::new(cooked, index))
171 }
172
173 #[inline]
174 fn dependents(&self) -> impl Iterator<Item = TypeView<'graph, 'a>> + use<'graph, 'a, T> {
175 let cooked = self.cooked();
176 let start = self.index();
177 cooked
178 .metadata
179 .closure
180 .dependents_of(start)
181 .filter(move |&index| index != start)
182 .map(|index| TypeView::new(cooked, index))
183 }
184
185 #[inline]
186 fn hashable(&self) -> bool {
187 self.cooked().metadata.hashable[self.index().index()]
188 }
189
190 #[inline]
191 fn defaultable(&self) -> bool {
192 self.cooked().metadata.defaultable[self.index().index()]
193 }
194}
195
196impl<'graph, 'a: 'graph, T> ExtendableView<'graph, 'a> for T
197where
198 T: ViewNode<'graph, 'a>,
199{
200 #[inline]
201 fn extensions(&self) -> &ViewExtensions<Self> {
202 ViewExtensions::new(self)
203 }
204
205 #[inline]
206 fn extensions_mut(&mut self) -> &mut ViewExtensions<Self> {
207 ViewExtensions::new_mut(self)
208 }
209}
210
211pub(crate) trait ViewNode<'graph, 'a: 'graph> {
212 fn cooked(&self) -> &'graph CookedGraph<'a>;
213 fn index(&self) -> NodeIndex<usize>;
214}
215
216impl<'graph, 'a: 'graph, T> internal::Extendable<'graph> for T
217where
218 T: ViewNode<'graph, 'a>,
219{
220 #[inline]
221 fn ext<'view>(&'view self) -> AtomicRef<'view, ExtensionMap>
222 where
223 'graph: 'view,
224 {
225 self.cooked().metadata.extensions[self.index().index()].borrow()
226 }
227
228 #[inline]
229 fn ext_mut<'b>(&'b mut self) -> AtomicRefMut<'b, ExtensionMap>
230 where
231 'graph: 'b,
232 {
233 self.cooked().metadata.extensions[self.index().index()].borrow_mut()
234 }
235}
236
237#[derive(RefCastCustom)]
239#[repr(transparent)]
240pub struct ViewExtensions<X>(X);
241
242impl<X> ViewExtensions<X> {
243 #[ref_cast_custom]
244 fn new(view: &X) -> &Self;
245
246 #[ref_cast_custom]
247 fn new_mut(view: &mut X) -> &mut Self;
248}
249
250impl<'a, X: internal::Extendable<'a>> ViewExtensions<X> {
251 #[inline]
254 pub fn get<'b, T: Send + Sync + 'static>(&'b self) -> Option<AtomicRef<'b, T>>
255 where
256 'a: 'b,
257 {
258 AtomicRef::filter_map(self.0.ext(), |ext| {
259 Some(
260 ext.get(&StdTypeId::of::<T>())?
261 .as_ref()
262 .downcast_ref::<T>()
263 .unwrap(),
264 )
265 })
266 }
267
268 #[inline]
271 pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) -> Option<T> {
272 self.0
273 .ext_mut()
274 .insert(StdTypeId::of::<T>(), Box::new(value))
275 .and_then(|old| *Extension::into_inner(old).downcast().unwrap())
276 }
277}
278
279impl<X> Debug for ViewExtensions<X> {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 f.debug_tuple("ViewExtensions").finish_non_exhaustive()
282 }
283}
284
285mod internal {
286 use atomic_refcell::{AtomicRef, AtomicRefMut};
287
288 use super::ExtensionMap;
289
290 pub trait Extendable<'graph> {
291 fn ext<'view>(&'view self) -> AtomicRef<'view, ExtensionMap>
304 where
305 'graph: 'view;
306
307 fn ext_mut<'view>(&'view mut self) -> AtomicRefMut<'view, ExtensionMap>
308 where
309 'graph: 'view;
310 }
311}