1use std::{
2 any::{Any, TypeId},
3 collections::VecDeque,
4 fmt::Debug,
5};
6
7use atomic_refcell::AtomicRefCell;
8use fixedbitset::FixedBitSet;
9use itertools::Itertools;
10use petgraph::{
11 Direction,
12 adj::UnweightedList,
13 algo::{TarjanScc, tred},
14 data::Build,
15 graph::{DiGraph, NodeIndex},
16 stable_graph::StableDiGraph,
17 visit::{
18 DfsPostOrder, EdgeFiltered, EdgeRef, IntoNeighbors, IntoNeighborsDirected,
19 IntoNodeIdentifiers, NodeCount, NodeIndexable,
20 },
21};
22use rustc_hash::{FxBuildHasher, FxHashMap};
23
24use crate::{
25 arena::Arena,
26 ir::{SchemaTypeInfo, UntaggedVariantMeta},
27 parse::Info,
28};
29
30use super::{
31 spec::{ResolvedSpecType, Spec},
32 types::{
33 FieldMeta, GraphContainer, GraphInlineType, GraphOperation, GraphSchemaType, GraphStruct,
34 GraphTagged, GraphType, InlineTypePath, InlineTypePathRoot, InlineTypePathSegment,
35 PrimitiveType, SpecInlineType, SpecSchemaType, SpecType, SpecUntaggedVariant,
36 StructFieldName, TaggedVariantMeta, VariantMeta,
37 shape::{Operation, Parameter, ParameterInfo, Request, Response},
38 },
39 views::{operation::OperationView, primitive::PrimitiveView, schema::SchemaTypeView},
40};
41
42type RawDiGraph<'a> = StableDiGraph<GraphType<'a>, GraphEdge<'a>, usize>;
44
45type CookedDiGraph<'a> = DiGraph<GraphType<'a>, GraphEdge<'a>, usize>;
47
48#[derive(Debug)]
59pub struct RawGraph<'a> {
60 arena: &'a Arena,
61 spec: &'a Spec<'a>,
62 graph: RawDiGraph<'a>,
63 ops: &'a [&'a GraphOperation<'a>],
64}
65
66impl<'a> RawGraph<'a> {
67 pub fn new(arena: &'a Arena, spec: &'a Spec<'a>) -> Self {
69 let tys = SpecTypeVisitor::new(
72 spec.schemas
73 .values()
74 .chain(spec.operations.iter().flat_map(|op| op.types().copied())),
75 );
76
77 let mut indices = FxHashMap::default();
79 let mut schemas = FxHashMap::default();
80 let mut graph = RawDiGraph::default();
81 for (parent, child) in tys {
82 use std::collections::hash_map::Entry;
83
84 let source = spec.resolve(child);
85 let &mut to = match indices.entry(source) {
86 Entry::Occupied(entry) => entry.into_mut(),
87 Entry::Vacant(entry) => {
88 let index = graph.add_node(match *entry.key() {
92 ResolvedSpecType::Schema(&ty) => GraphType::Schema(ty.into()),
93 ResolvedSpecType::Inline(&ty) => GraphType::Inline(ty.into()),
94 });
95 if let ResolvedSpecType::Schema(ty) = source {
96 schemas.entry(ty.name()).or_insert(index);
97 }
98 entry.insert(index)
99 }
100 };
101
102 if let Some((parent, edge)) = parent {
103 let destination = spec.resolve(parent);
104 let &mut from = match indices.entry(destination) {
105 Entry::Occupied(entry) => entry.into_mut(),
106 Entry::Vacant(entry) => {
107 let index = graph.add_node(match *entry.key() {
108 ResolvedSpecType::Schema(&ty) => GraphType::Schema(ty.into()),
109 ResolvedSpecType::Inline(&ty) => GraphType::Inline(ty.into()),
110 });
111 if let ResolvedSpecType::Schema(ty) = destination {
112 schemas.entry(ty.name()).or_insert(index);
113 }
114 entry.insert(index)
115 }
116 };
117 graph.add_edge(from, to, edge);
118 }
119 }
120
121 let ops = arena.alloc_slice_exact(spec.operations.iter().map(|op| {
123 let params = arena.alloc_slice_exact(op.params.iter().map(|param| match param {
124 Parameter::Path(info) => Parameter::Path(ParameterInfo {
125 name: info.name,
126 ty: match info.ty {
127 SpecType::Schema(s) => indices[&ResolvedSpecType::Schema(s)],
128 SpecType::Inline(i) => indices[&ResolvedSpecType::Inline(i)],
129 SpecType::Ref(r) => schemas[&*r.name()],
130 },
131 required: info.required,
132 description: info.description,
133 style: info.style,
134 }),
135 Parameter::Query(info) => Parameter::Query(ParameterInfo {
136 name: info.name,
137 ty: match info.ty {
138 SpecType::Schema(s) => indices[&ResolvedSpecType::Schema(s)],
139 SpecType::Inline(i) => indices[&ResolvedSpecType::Inline(i)],
140 SpecType::Ref(r) => schemas[&*r.name()],
141 },
142 required: info.required,
143 description: info.description,
144 style: info.style,
145 }),
146 }));
147
148 let request = op.request.as_ref().map(|r| match r {
149 Request::Json(ty) => Request::Json(match ty {
150 SpecType::Schema(s) => indices[&ResolvedSpecType::Schema(s)],
151 SpecType::Inline(i) => indices[&ResolvedSpecType::Inline(i)],
152 SpecType::Ref(r) => schemas[&*r.name()],
153 }),
154 Request::Multipart => Request::Multipart,
155 });
156
157 let response = op.response.as_ref().map(|r| match r {
158 Response::Json(ty) => Response::Json(match ty {
159 SpecType::Schema(s) => indices[&ResolvedSpecType::Schema(s)],
160 SpecType::Inline(i) => indices[&ResolvedSpecType::Inline(i)],
161 SpecType::Ref(r) => schemas[&*r.name()],
162 }),
163 });
164
165 &*arena.alloc(Operation {
166 id: op.id,
167 method: op.method,
168 path: op.path,
169 resource: op.resource,
170 description: op.description,
171 params,
172 request,
173 response,
174 })
175 }));
176
177 Self {
178 arena,
179 spec,
180 graph,
181 ops,
182 }
183 }
184
185 pub fn inline_tagged_variants(&mut self) -> &mut Self {
205 let inlinables = self.inlinable_tagged_variants().collect_vec();
208
209 let mut retargets = FxHashMap::default();
210 retargets.reserve(inlinables.len());
211
212 for InlinableVariant { tagged, variant } in inlinables {
215 let index = self
218 .graph
219 .add_node(GraphType::Inline(GraphInlineType::Struct(
220 InlineTypePath {
221 root: InlineTypePathRoot::Type(tagged.info.name),
222 segments: self.arena.alloc_slice_copy(&[
223 InlineTypePathSegment::TaggedVariant(variant.info.name),
224 ]),
225 },
226 variant.ty,
227 )));
228
229 let original_field_edges = self.fields(variant.index).collect_vec();
240 for edge in original_field_edges.into_iter().rev() {
241 self.graph.add_edge(
242 index,
243 edge.target,
244 GraphEdge::Field {
245 meta: edge.meta,
246 shadow: true,
247 },
248 );
249 }
250
251 self.graph
256 .add_edge(index, tagged.index, GraphEdge::Inherits { shadow: true });
257 self.graph
258 .add_edge(index, variant.index, GraphEdge::Inherits { shadow: true });
259
260 retargets.insert((tagged.index, variant.index), index);
261 }
262
263 let taggeds: FixedBitSet = retargets
265 .keys()
266 .map(|&(tagged, _)| tagged.index())
267 .collect();
268 for index in taggeds.ones().map(NodeIndex::new) {
269 let old_edges = self
270 .graph
271 .edges_directed(index, Direction::Outgoing)
272 .filter(|e| matches!(e.weight(), GraphEdge::Variant(_)))
273 .map(|e| (e.id(), *e.weight(), e.target()))
274 .collect_vec();
275 for &(id, _, _) in &old_edges {
276 self.graph.remove_edge(id);
277 }
278 for (_, weight, target) in old_edges.into_iter().rev() {
281 let new_target = retargets.get(&(index, target)).copied().unwrap_or(target);
282 self.graph.add_edge(index, new_target, weight);
283 }
284 }
285
286 self
287 }
288
289 #[inline]
291 pub fn cook(&self) -> CookedGraph<'a> {
292 CookedGraph::new(self)
293 }
294
295 fn fields(&self, node: NodeIndex<usize>) -> impl Iterator<Item = OutgoingEdge<FieldMeta<'a>>> {
298 self.graph
299 .edges_directed(node, Direction::Outgoing)
300 .filter_map(|e| match e.weight() {
301 &GraphEdge::Field { meta, .. } => {
302 let target = e.target();
303 Some(OutgoingEdge { meta, target })
304 }
305 _ => None,
306 })
307 }
308
309 fn inlinable_tagged_variants(&self) -> impl Iterator<Item = InlinableVariant<'a>> {
312 let used_by_ops: FixedBitSet = self
321 .ops
322 .iter()
323 .flat_map(|op| op.types())
324 .map(|index| index.index())
325 .collect();
326
327 self.graph
328 .node_indices()
329 .filter_map(|index| match self.graph[index] {
330 GraphType::Schema(GraphSchemaType::Tagged(info, ty)) => {
331 Some(Node { index, info, ty })
332 }
333 _ => None,
334 })
335 .flat_map(move |tagged| {
336 self.graph
337 .edges_directed(tagged.index, Direction::Outgoing)
338 .filter(|e| matches!(e.weight(), GraphEdge::Variant(_)))
339 .filter_map(move |e| match self.graph[e.target()] {
340 GraphType::Schema(GraphSchemaType::Struct(info, ty)) => {
341 let index = e.target();
342 Some((tagged, Node { index, info, ty }))
343 }
344 _ => None,
345 })
346 })
347 .filter_map(move |(tagged, variant)| {
348 if used_by_ops[variant.index.index()] {
354 return Some((tagged, variant));
355 }
356
357 let first_tagged = self
361 .graph
362 .neighbors_directed(variant.index, Direction::Incoming)
363 .find_map(|index| match self.graph[index] {
364 GraphType::Schema(GraphSchemaType::Tagged(info, ty)) => {
365 Some(Node { index, info, ty })
366 }
367 _ => None,
368 })?;
369 let all_agree = self
370 .graph
371 .neighbors_directed(variant.index, Direction::Incoming)
372 .all(|index| match self.graph[index] {
373 GraphType::Schema(GraphSchemaType::Tagged(_, ty)) => {
374 ty.tag == first_tagged.ty.tag
375 && self.fields(index).eq(self.fields(first_tagged.index))
376 }
377 _ => false,
378 });
379 if all_agree {
380 return None;
381 }
382 Some((tagged, variant))
383 })
384 .filter_map(|(tagged, variant)| {
385 let ancestors = EdgeFiltered::from_fn(&self.graph, |e| {
391 matches!(e.weight(), GraphEdge::Inherits { .. })
392 });
393 let mut dfs = DfsPostOrder::new(&ancestors, variant.index);
394 let has_tag_field = std::iter::from_fn(|| dfs.next(&ancestors))
395 .filter(|&n| {
396 matches!(
397 self.graph[n],
398 GraphType::Schema(GraphSchemaType::Struct(..))
399 | GraphType::Inline(GraphInlineType::Struct(..))
400 )
401 })
402 .any(|n| {
403 self.fields(n).any(|f| {
404 matches!(f.meta.name, StructFieldName::Name(name)
405 if name == tagged.ty.tag)
406 })
407 });
408
409 if has_tag_field {
413 return Some(InlinableVariant { tagged, variant });
414 }
415
416 if dfs.discovered[tagged.index.index()] {
419 return None;
420 }
421
422 self.fields(tagged.index).next()?;
425
426 Some(InlinableVariant { tagged, variant })
427 })
428 }
429}
430
431#[derive(Debug)]
437pub struct CookedGraph<'a> {
438 pub(super) graph: CookedDiGraph<'a>,
439 info: &'a Info,
440 ops: &'a [&'a GraphOperation<'a>],
441 pub(super) metadata: CookedGraphMetadata<'a>,
443}
444
445impl<'a> CookedGraph<'a> {
446 fn new(raw: &RawGraph<'a>) -> Self {
447 let mut graph =
450 CookedDiGraph::with_capacity(raw.graph.node_count(), raw.graph.edge_count());
451 let mut indices =
452 FxHashMap::with_capacity_and_hasher(raw.graph.node_count(), FxBuildHasher);
453 for raw_index in raw.graph.node_indices() {
454 let cooked_index = graph.add_node(raw.graph[raw_index]);
455 indices.insert(raw_index, cooked_index);
456 }
457
458 for index in raw.graph.node_indices() {
467 let from = indices[&index];
468 let edges = raw
469 .graph
470 .edges(index)
471 .map(|e| (indices[&e.target()], *e.weight()));
472 for (to, kind) in edges {
473 graph.add_edge(from, to, kind);
474 }
475 }
476
477 let ops: &_ = raw.arena.alloc_slice_exact(raw.ops.iter().map(|&op| {
479 &*raw.arena.alloc(Operation {
480 id: op.id,
481 method: op.method,
482 path: op.path,
483 resource: op.resource,
484 description: op.description,
485 params: raw
486 .arena
487 .alloc_slice_exact(op.params.iter().map(|p| match p {
488 Parameter::Path(info) => Parameter::Path(ParameterInfo {
489 name: info.name,
490 ty: indices[&info.ty],
491 required: info.required,
492 description: info.description,
493 style: info.style,
494 }),
495 Parameter::Query(info) => Parameter::Query(ParameterInfo {
496 name: info.name,
497 ty: indices[&info.ty],
498 required: info.required,
499 description: info.description,
500 style: info.style,
501 }),
502 })),
503 request: op.request.as_ref().map(|r| match r {
504 Request::Json(ty) => Request::Json(indices[ty]),
505 Request::Multipart => Request::Multipart,
506 }),
507 response: op.response.as_ref().map(|r| match r {
508 Response::Json(ty) => Response::Json(indices[ty]),
509 }),
510 })
511 }));
512
513 let metadata = MetadataBuilder::new(&graph, ops).build();
514
515 Self {
516 graph,
517 info: raw.spec.info,
518 ops,
519 metadata,
520 }
521 }
522
523 #[inline]
526 pub fn info(&self) -> &'a Info {
527 self.info
528 }
529
530 #[inline]
532 pub fn schemas(&self) -> impl Iterator<Item = SchemaTypeView<'_>> {
533 self.graph
534 .node_indices()
535 .filter_map(|index| match self.graph[index] {
536 GraphType::Schema(ty) => Some(SchemaTypeView::new(self, index, ty)),
537 _ => None,
538 })
539 }
540
541 #[inline]
543 pub fn primitives(&self) -> impl Iterator<Item = PrimitiveView<'_>> {
544 self.graph
545 .node_indices()
546 .filter_map(|index| match self.graph[index] {
547 GraphType::Schema(GraphSchemaType::Primitive(_, p))
548 | GraphType::Inline(GraphInlineType::Primitive(_, p)) => {
549 Some(PrimitiveView::new(self, index, p))
550 }
551 _ => None,
552 })
553 }
554
555 #[inline]
557 pub fn operations(&self) -> impl Iterator<Item = OperationView<'_>> {
558 self.ops.iter().map(move |&op| OperationView::new(self, op))
559 }
560
561 #[inline]
562 pub(super) fn inherits(
563 &self,
564 node: NodeIndex<usize>,
565 ) -> impl Iterator<Item = OutgoingEdge<()>> {
566 self.graph
567 .edges_directed(node, Direction::Outgoing)
568 .filter(|e| matches!(e.weight(), GraphEdge::Inherits { .. }))
569 .map(|e| OutgoingEdge {
570 meta: (),
571 target: e.target(),
572 })
573 }
574
575 #[inline]
576 pub(super) fn fields(
577 &self,
578 node: NodeIndex<usize>,
579 ) -> impl Iterator<Item = OutgoingEdge<FieldMeta<'a>>> {
580 self.graph
581 .edges_directed(node, Direction::Outgoing)
582 .filter_map(|e| match e.weight() {
583 &GraphEdge::Field { meta, .. } => {
584 let target = e.target();
585 Some(OutgoingEdge { meta, target })
586 }
587 _ => None,
588 })
589 }
590
591 #[inline]
592 pub(super) fn variants(
593 &self,
594 node: NodeIndex<usize>,
595 ) -> impl Iterator<Item = OutgoingEdge<VariantMeta<'a>>> {
596 self.graph
597 .edges_directed(node, Direction::Outgoing)
598 .filter_map(|e| match e.weight() {
599 &GraphEdge::Variant(meta) => {
600 let target = e.target();
601 Some(OutgoingEdge { meta, target })
602 }
603 _ => None,
604 })
605 }
606}
607
608struct InlinableVariant<'a> {
610 tagged: Node<'a, GraphTagged<'a>>,
612 variant: Node<'a, GraphStruct<'a>>,
614}
615
616#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
620pub enum GraphEdge<'a> {
621 Inherits { shadow: bool },
623 Field { shadow: bool, meta: FieldMeta<'a> },
626 Variant(VariantMeta<'a>),
628 Contains,
631}
632
633impl GraphEdge<'_> {
634 #[inline]
642 pub fn shadow(&self) -> bool {
643 matches!(
644 self,
645 GraphEdge::Field { shadow: true, .. } | GraphEdge::Inherits { shadow: true }
646 )
647 }
648}
649
650#[derive(Clone, Copy, Debug, Eq, PartialEq)]
652pub struct OutgoingEdge<T> {
653 pub meta: T,
654 pub target: NodeIndex<usize>,
655}
656
657#[derive(Clone, Copy)]
658struct Node<'a, Ty> {
659 index: NodeIndex<usize>,
660 info: SchemaTypeInfo<'a>,
661 ty: Ty,
662}
663
664pub(super) struct CookedGraphMetadata<'a> {
666 pub closure: Closure,
668 pub box_sccs: Vec<usize>,
671 pub hashable: FixedBitSet,
673 pub defaultable: FixedBitSet,
675 pub used_by: Vec<Vec<GraphOperation<'a>>>,
677 pub uses: FxHashMap<GraphOperation<'a>, FixedBitSet>,
679 pub extensions: Vec<AtomicRefCell<ExtensionMap>>,
681}
682
683impl Debug for CookedGraphMetadata<'_> {
684 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
685 f.debug_struct("CookedGraphMetadata")
686 .field("closure", &self.closure)
687 .field("box_sccs", &self.box_sccs)
688 .field("hashable", &self.hashable)
689 .field("defaultable", &self.defaultable)
690 .field("used_by", &self.used_by)
691 .field("uses", &self.uses)
692 .finish_non_exhaustive()
693 }
694}
695
696struct HashDefault {
699 hashable: FixedBitSet,
700 defaultable: FixedBitSet,
701}
702
703struct Operations<'a> {
706 pub uses: FxHashMap<GraphOperation<'a>, FixedBitSet>,
708 pub used_by: Vec<Vec<GraphOperation<'a>>>,
710}
711
712struct MetadataBuilder<'graph, 'a> {
713 graph: &'graph CookedDiGraph<'a>,
714 ops: &'graph [&'graph GraphOperation<'a>],
715 closure: Closure,
717}
718
719impl<'graph, 'a> MetadataBuilder<'graph, 'a> {
720 fn new(graph: &'graph CookedDiGraph<'a>, ops: &'graph [&'graph GraphOperation<'a>]) -> Self {
721 Self {
722 graph,
723 ops,
724 closure: Closure::new(graph),
725 }
726 }
727
728 fn build(self) -> CookedGraphMetadata<'a> {
729 let operations = self.operations();
730 let HashDefault {
731 hashable,
732 defaultable,
733 } = self.hash_default();
734 let box_sccs = self.box_sccs();
735 CookedGraphMetadata {
736 closure: self.closure,
737 box_sccs,
738 hashable,
739 defaultable,
740 used_by: operations.used_by,
741 uses: operations.uses,
742 extensions: std::iter::repeat_with(AtomicRefCell::default)
745 .take(self.graph.node_count())
746 .collect(),
747 }
748 }
749
750 fn operations(&self) -> Operations<'a> {
751 let mut operations = Operations {
752 uses: FxHashMap::default(),
753 used_by: vec![vec![]; self.graph.node_count()],
754 };
755
756 for &&op in self.ops {
757 let mut dependencies = FixedBitSet::with_capacity(self.graph.node_count());
760 for &node in op.types() {
761 dependencies.extend(self.closure.dependencies_of(node).map(|n| n.index()));
762 }
763 operations.uses.entry(op).insert_entry(dependencies);
764 }
765
766 for (op, deps) in &operations.uses {
768 for node in deps.ones() {
769 operations.used_by[node].push(*op);
770 }
771 }
772
773 operations
774 }
775
776 fn box_sccs(&self) -> Vec<usize> {
777 let box_edges = EdgeFiltered::from_fn(self.graph, |e| match e.weight() {
778 GraphEdge::Inherits { .. } => false,
781 GraphEdge::Contains => match self.graph[e.source()] {
782 GraphType::Schema(GraphSchemaType::Container(_, c))
783 | GraphType::Inline(GraphInlineType::Container(_, c)) => {
784 !matches!(c, GraphContainer::Array { .. } | GraphContainer::Map { .. })
787 }
788 _ => true,
789 },
790 _ => true,
791 });
792 let mut scc = TarjanScc::new();
793 scc.run(&box_edges, |_| ());
794 self.graph
795 .node_indices()
796 .map(move |node| scc.node_component_index(&box_edges, node))
797 .collect()
798 }
799
800 fn hash_default(&self) -> HashDefault {
801 let n = self.graph.node_count();
803 let mut unhashable = FixedBitSet::with_capacity(n);
804 let mut undefaultable = FixedBitSet::with_capacity(n);
805 for node in self.graph.node_indices() {
806 use {GraphType::*, PrimitiveType::*};
807 match &self.graph[node] {
808 Schema(GraphSchemaType::Primitive(_, F32 | F64))
809 | Inline(GraphInlineType::Primitive(_, F32 | F64)) => {
810 unhashable.insert(node.index());
811 }
812 Schema(
813 GraphSchemaType::Primitive(_, Url)
814 | GraphSchemaType::Tagged(_, _)
815 | GraphSchemaType::Untagged(_, _),
816 )
817 | Inline(
818 GraphInlineType::Primitive(_, Url)
819 | GraphInlineType::Tagged(_, _)
820 | GraphInlineType::Untagged(_, _),
821 ) => {
822 undefaultable.insert(node.index());
823 }
824 _ => (),
825 }
826 }
827
828 let inherits = Closure::new(&EdgeFiltered::from_fn(self.graph, |e| {
830 matches!(e.weight(), GraphEdge::Inherits { .. })
831 }));
832
833 let mut queue: VecDeque<_> = unhashable.ones().map(NodeIndex::new).collect();
839 while let Some(node) = queue.pop_front() {
840 for edge in self.graph.edges_directed(node, Direction::Incoming) {
841 let source = edge.source();
842 match edge.weight() {
843 GraphEdge::Contains | GraphEdge::Variant(_) => {
844 if !unhashable.put(source.index()) {
845 queue.push_back(source);
846 }
847 }
848 GraphEdge::Field { .. } => {
849 if !unhashable.put(source.index()) {
850 queue.push_back(source);
851 }
852 for desc in inherits.dependents_of(source).filter(|&d| d != source) {
856 if !unhashable.put(desc.index()) {
857 queue.push_back(desc);
858 }
859 }
860 }
861 GraphEdge::Inherits { .. } => {}
866 }
867 }
868 }
869
870 let mut queue: VecDeque<_> = undefaultable.ones().map(NodeIndex::new).collect();
872 while let Some(node) = queue.pop_front() {
873 for edge in self.graph.edges_directed(node, Direction::Incoming) {
874 if !matches!(
875 edge.weight(),
876 GraphEdge::Field { meta, .. } if meta.required
877 ) {
878 continue;
881 }
882 let source = edge.source();
883 if !undefaultable.put(source.index()) {
884 queue.push_back(source);
885 }
886 for desc in inherits.dependents_of(source).filter(|&d| d != source) {
890 if !undefaultable.put(desc.index()) {
891 queue.push_back(desc);
892 }
893 }
894 }
895 }
896
897 HashDefault {
898 hashable: invert(unhashable),
899 defaultable: invert(undefaultable),
900 }
901 }
902}
903
904fn invert(mut bits: FixedBitSet) -> FixedBitSet {
906 bits.toggle_range(..);
907 bits
908}
909
910#[derive(Debug)]
912struct SpecTypeVisitor<'a> {
913 stack: Vec<(Option<(&'a SpecType<'a>, GraphEdge<'a>)>, &'a SpecType<'a>)>,
914}
915
916impl<'a> SpecTypeVisitor<'a> {
917 #[inline]
919 fn new(roots: impl Iterator<Item = &'a SpecType<'a>>) -> Self {
920 let mut stack = roots.map(|root| (None, root)).collect_vec();
921 stack.reverse();
922 Self { stack }
923 }
924}
925
926impl<'a> Iterator for SpecTypeVisitor<'a> {
927 type Item = (Option<(&'a SpecType<'a>, GraphEdge<'a>)>, &'a SpecType<'a>);
928
929 fn next(&mut self) -> Option<Self::Item> {
930 let (parent, top) = self.stack.pop()?;
931 if matches!(
932 parent,
933 Some((
934 _,
935 GraphEdge::Variant(VariantMeta::Untagged(UntaggedVariantMeta::Null))
936 ))
937 ) {
938 return Some((parent, top));
941 }
942 match top {
943 SpecType::Schema(SpecSchemaType::Struct(_, ty))
944 | SpecType::Inline(SpecInlineType::Struct(_, ty)) => {
945 self.stack.extend(
946 itertools::chain!(
947 ty.fields.iter().map(|field| (
948 GraphEdge::Field {
949 shadow: false,
950 meta: FieldMeta {
951 name: field.name,
952 required: field.required,
953 description: field.description,
954 flattened: field.flattened,
955 },
956 },
957 field.ty
958 )),
959 ty.parents
960 .iter()
961 .map(|parent| (GraphEdge::Inherits { shadow: false }, *parent)),
962 )
963 .map(|(edge, ty)| (Some((top, edge)), ty))
964 .rev(),
965 );
966 }
967 SpecType::Schema(SpecSchemaType::Untagged(_, ty))
968 | SpecType::Inline(SpecInlineType::Untagged(_, ty)) => {
969 self.stack.extend(
970 itertools::chain!(
971 ty.fields.iter().map(|field| (
972 GraphEdge::Field {
973 shadow: false,
974 meta: FieldMeta {
975 name: field.name,
976 required: field.required,
977 description: field.description,
978 flattened: field.flattened,
979 },
980 },
981 field.ty
982 )),
983 ty.variants.iter().map(|variant| match variant {
984 &SpecUntaggedVariant::Some(hint, ty) => {
985 let meta = UntaggedVariantMeta::Type { hint };
986 (GraphEdge::Variant(meta.into()), ty)
987 }
988 SpecUntaggedVariant::Null => {
991 (GraphEdge::Variant(UntaggedVariantMeta::Null.into()), top)
992 }
993 }),
994 )
995 .map(|(edge, ty)| (Some((top, edge)), ty))
996 .rev(),
997 );
998 }
999 SpecType::Schema(SpecSchemaType::Tagged(_, ty))
1000 | SpecType::Inline(SpecInlineType::Tagged(_, ty)) => {
1001 self.stack.extend(
1002 itertools::chain!(
1003 ty.fields.iter().map(|field| (
1004 GraphEdge::Field {
1005 shadow: false,
1006 meta: FieldMeta {
1007 name: field.name,
1008 required: field.required,
1009 description: field.description,
1010 flattened: field.flattened,
1011 },
1012 },
1013 field.ty
1014 )),
1015 ty.variants.iter().map(|variant| (
1016 GraphEdge::Variant(
1017 TaggedVariantMeta {
1018 name: variant.name,
1019 aliases: variant.aliases,
1020 }
1021 .into()
1022 ),
1023 variant.ty
1024 )),
1025 )
1026 .map(|(edge, ty)| (Some((top, edge)), ty))
1027 .rev(),
1028 );
1029 }
1030 SpecType::Schema(SpecSchemaType::Container(_, container))
1031 | SpecType::Inline(SpecInlineType::Container(_, container)) => {
1032 self.stack
1033 .push((Some((top, GraphEdge::Contains)), container.inner().ty));
1034 }
1035 SpecType::Schema(
1036 SpecSchemaType::Enum(..) | SpecSchemaType::Primitive(..) | SpecSchemaType::Any(_),
1037 )
1038 | SpecType::Inline(
1039 SpecInlineType::Enum(..) | SpecInlineType::Primitive(..) | SpecInlineType::Any(_),
1040 ) => (),
1041 SpecType::Ref(_) => (),
1042 }
1043 Some((parent, top))
1044 }
1045}
1046
1047pub(super) type ExtensionMap = FxHashMap<TypeId, Box<dyn Extension>>;
1049
1050pub trait Extension: Any + Send + Sync {
1051 fn into_inner(self: Box<Self>) -> Box<dyn Any>;
1052}
1053
1054impl dyn Extension {
1055 #[inline]
1056 pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
1057 (self as &dyn Any).downcast_ref::<T>()
1058 }
1059}
1060
1061impl<T: Send + Sync + 'static> Extension for T {
1062 #[inline]
1063 fn into_inner(self: Box<Self>) -> Box<dyn Any> {
1064 self
1065 }
1066}
1067
1068struct TopoSccs<G> {
1075 graph: G,
1076 tarjan: TarjanScc<NodeIndex<usize>>,
1077 sccs: Vec<Vec<usize>>,
1078}
1079
1080impl<G> TopoSccs<G>
1081where
1082 G: Closable<NodeIndex<usize>> + Copy,
1083{
1084 fn new(graph: G) -> Self {
1085 let mut sccs = Vec::new();
1086 let mut tarjan = TarjanScc::new();
1087 tarjan.run(graph, |scc_nodes| {
1088 sccs.push(scc_nodes.iter().map(|node| node.index()).collect());
1089 });
1090 sccs.reverse();
1093 Self {
1094 graph,
1095 tarjan,
1096 sccs,
1097 }
1098 }
1099
1100 #[inline]
1101 fn scc_count(&self) -> usize {
1102 self.sccs.len()
1103 }
1104
1105 #[inline]
1107 fn topo_index(&self, node: NodeIndex<usize>) -> usize {
1108 self.sccs.len() - 1 - self.tarjan.node_component_index(self.graph, node)
1111 }
1112
1113 fn condensation(&self) -> UnweightedList<usize> {
1120 let mut dag = UnweightedList::with_capacity(self.scc_count());
1121 for to in 0..self.scc_count() {
1122 dag.add_node();
1123 for neighbor in self.sccs[to].iter().flat_map(|&index| {
1124 self.graph
1125 .neighbors_directed(NodeIndex::new(index), Direction::Incoming)
1126 }) {
1127 let from = self.topo_index(neighbor);
1128 if from != to {
1129 dag.update_edge(from, to, ());
1130 }
1131 }
1132 }
1133 dag
1134 }
1135}
1136
1137#[derive(Debug)]
1139pub(super) struct Closure {
1140 scc_indices: Vec<usize>,
1142 scc_members: Vec<Vec<usize>>,
1144 scc_deps: Vec<Vec<usize>>,
1147 scc_rdeps: Vec<Vec<usize>>,
1150}
1151
1152impl Closure {
1153 fn new<G>(graph: G) -> Self
1155 where
1156 G: Closable<NodeIndex<usize>> + Copy,
1157 {
1158 let sccs = TopoSccs::new(graph);
1159 let condensation = sccs.condensation();
1160 let (_, closure) = tred::dag_transitive_reduction_closure(&condensation);
1161
1162 let scc_deps = (0..sccs.scc_count())
1165 .map(|scc| closure.neighbors(scc).collect_vec())
1166 .collect_vec();
1167 let mut scc_rdeps = vec![vec![]; sccs.scc_count()];
1168 for (scc, deps) in scc_deps.iter().enumerate() {
1169 for &dep in deps {
1170 scc_rdeps[dep].push(scc);
1171 }
1172 }
1173
1174 let mut scc_indices = vec![0; graph.node_count()];
1175 for node in graph.node_identifiers() {
1176 scc_indices[node.index()] = sccs.topo_index(node);
1177 }
1178
1179 Closure {
1180 scc_indices,
1181 scc_members: sccs.sccs.iter().cloned().collect_vec(),
1182 scc_deps,
1183 scc_rdeps,
1184 }
1185 }
1186
1187 #[inline]
1189 pub fn scc_index_of(&self, node: NodeIndex<usize>) -> usize {
1190 self.scc_indices[node.index()]
1191 }
1192
1193 pub fn dependencies_of(
1196 &self,
1197 node: NodeIndex<usize>,
1198 ) -> impl Iterator<Item = NodeIndex<usize>> {
1199 let scc = self.scc_index_of(node);
1200 std::iter::once(scc)
1201 .chain(self.scc_deps[scc].iter().copied())
1202 .flat_map(|s| self.scc_members[s].iter().copied()) .map(NodeIndex::new)
1204 }
1205
1206 pub fn dependents_of(&self, node: NodeIndex<usize>) -> impl Iterator<Item = NodeIndex<usize>> {
1209 let scc = self.scc_index_of(node);
1210 std::iter::once(scc)
1211 .chain(self.scc_rdeps[scc].iter().copied())
1212 .flat_map(|s| self.scc_members[s].iter().copied())
1213 .map(NodeIndex::new)
1214 }
1215
1216 #[inline]
1219 pub fn depends_on(&self, node: NodeIndex<usize>, other: NodeIndex<usize>) -> bool {
1220 if node == other {
1221 return false;
1222 }
1223 let scc = self.scc_index_of(node);
1224 let other_scc = self.scc_index_of(other);
1225 scc == other_scc || self.scc_deps[scc].contains(&other_scc)
1226 }
1227}
1228
1229trait Closable<N>:
1231 NodeCount
1232 + IntoNodeIdentifiers<NodeId = N>
1233 + IntoNeighbors<NodeId = N>
1234 + IntoNeighborsDirected<NodeId = N>
1235 + NodeIndexable<NodeId = N>
1236{
1237}
1238
1239impl<N, G> Closable<N> for G where
1240 G: NodeCount
1241 + IntoNodeIdentifiers<NodeId = N>
1242 + IntoNeighbors<NodeId = N>
1243 + IntoNeighborsDirected<NodeId = N>
1244 + NodeIndexable<NodeId = N>
1245{
1246}
1247
1248#[cfg(test)]
1249mod tests {
1250 use super::*;
1251
1252 use crate::tests::assert_matches;
1253
1254 fn linear_graph() -> DiGraph<(), (), usize> {
1256 let mut g = DiGraph::default();
1257 let a = g.add_node(());
1258 let b = g.add_node(());
1259 let c = g.add_node(());
1260 g.extend_with_edges([(a, b), (b, c)]);
1261 g
1262 }
1263
1264 fn cyclic_graph() -> DiGraph<(), (), usize> {
1266 let mut g = DiGraph::default();
1267 let a = g.add_node(());
1268 let b = g.add_node(());
1269 let c = g.add_node(());
1270 let d = g.add_node(());
1271 g.extend_with_edges([(a, b), (b, c), (c, a), (d, a)]);
1272 g
1273 }
1274
1275 #[test]
1278 fn test_linear_graph_has_singleton_sccs() {
1279 let g = linear_graph();
1280 let sccs = TopoSccs::new(&g);
1281 let sizes = sccs.sccs.iter().map(|scc| scc.len()).collect_vec();
1282 assert_matches!(&*sizes, [1, 1, 1]);
1283 }
1284
1285 #[test]
1286 fn test_cyclic_graph_has_one_multi_node_scc() {
1287 let g = cyclic_graph();
1288 let sccs = TopoSccs::new(&g);
1289
1290 let sizes = sccs.sccs.iter().map(|scc| scc.len()).collect_vec();
1293 assert_matches!(&*sizes, [1, 3]);
1294 }
1295
1296 #[test]
1299 fn test_sccs_are_in_topological_order() {
1300 let g = cyclic_graph();
1301 let sccs = TopoSccs::new(&g);
1302
1303 let d_topo = sccs.topo_index(3.into());
1304 let a_topo = sccs.topo_index(0.into());
1305 assert!(
1306 d_topo < a_topo,
1307 "D should precede A-B-C in topological order"
1308 );
1309 }
1310
1311 #[test]
1312 fn test_topo_index_consistent_within_scc() {
1313 let g = cyclic_graph();
1314 let sccs = TopoSccs::new(&g);
1315
1316 let a_topo = sccs.topo_index(0.into());
1319 let b_topo = sccs.topo_index(1.into());
1320 let c_topo = sccs.topo_index(2.into());
1321
1322 assert_eq!(a_topo, b_topo);
1323 assert_eq!(b_topo, c_topo);
1324 }
1325
1326 #[test]
1329 fn test_condensation_has_correct_node_count() {
1330 let g = cyclic_graph();
1331 let sccs = TopoSccs::new(&g);
1332 let dag = sccs.condensation();
1333
1334 assert_eq!(dag.node_count(), 2);
1335 }
1336
1337 #[test]
1338 fn test_condensation_has_correct_edges() {
1339 let g = cyclic_graph();
1340 let sccs = TopoSccs::new(&g);
1341 let dag = sccs.condensation();
1342
1343 let d_topo = sccs.topo_index(3.into());
1346 let abc_topo = sccs.topo_index(0.into());
1347
1348 let d_neighbors = dag.neighbors(d_topo).collect_vec();
1349 assert_eq!(&*d_neighbors, [abc_topo]);
1350
1351 let abc_neighbors = dag.neighbors(abc_topo).collect_vec();
1352 assert!(abc_neighbors.is_empty());
1353 }
1354
1355 #[test]
1356 fn test_condensation_neighbors_in_topological_order() {
1357 let mut g = DiGraph::<(), (), usize>::default();
1361 let second = g.add_node(());
1362 let top = g.add_node(());
1363 let first = g.add_node(());
1364 g.extend_with_edges([(top, second), (top, first), (first, second)]);
1365
1366 let sccs = TopoSccs::new(&g);
1367 let dag = sccs.condensation();
1368
1369 let top_topo = sccs.topo_index(top);
1370 assert_eq!(top_topo, 0);
1371
1372 let first_topo = sccs.topo_index(first);
1373 assert_eq!(first_topo, 1);
1374
1375 let second_topo = sccs.topo_index(second);
1376 assert_eq!(second_topo, 2);
1377
1378 let neighbors = dag.neighbors(top_topo).collect_vec();
1379 assert_eq!(&*neighbors, [first_topo, second_topo]);
1380 }
1381}