1use std::{
2 any::{Any, TypeId as StdTypeId},
3 collections::VecDeque,
4 fmt::Debug,
5 num::NonZeroUsize,
6};
7
8use atomic_refcell::AtomicRefCell;
9use fixedbitset::FixedBitSet;
10use itertools::Itertools;
11use petgraph::{
12 Direction,
13 adj::UnweightedList,
14 algo::{TarjanScc, tred},
15 data::Build,
16 graph::{DiGraph, EdgeIndex, NodeIndex},
17 stable_graph::StableDiGraph,
18 visit::{
19 DfsPostOrder, EdgeFiltered, EdgeRef, GraphRef, IntoEdgeReferences, IntoEdges,
20 IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers, NodeCount, NodeIndexable,
21 VisitMap, Visitable,
22 },
23};
24use rustc_hash::{FxBuildHasher, FxHashMap};
25
26use crate::{arena::Arena, ir::TypeView, parse::Info};
27
28use super::{
29 spec::{ResolvedSpecType, Spec},
30 types::{
31 FieldMeta, GraphContainer, GraphInlineType, GraphOperation, GraphSchemaType, GraphStruct,
32 GraphTagged, GraphType, GraphUntagged, InlineTypeId, InlineTypeIds, InlineTypePathRoot,
33 OperationUsage, PrimitiveType, SpecInlineType, SpecSchemaType, SpecType, StructFieldName,
34 TaggedVariantMeta, UntaggedVariantMeta, VariantMeta,
35 shape::{Operation, Parameter, ParameterInfo, Request, Response},
36 },
37 views::{TypeId, operation::OperationView, primitive::PrimitiveView, schema::SchemaTypeView},
38};
39
40type RawDiGraph<'a> = StableDiGraph<GraphType<'a>, GraphEdge<'a>, usize>;
42
43type CookedDiGraph<'a> = DiGraph<GraphType<'a>, GraphEdge<'a>, usize>;
45
46#[derive(Debug)]
56pub struct RawGraph<'a> {
57 arena: &'a Arena,
58 spec: &'a Spec<'a>,
59 graph: RawDiGraph<'a>,
60 schemas: FxHashMap<&'a str, NodeIndex<usize>>,
61 ops: &'a [&'a GraphOperation<'a>],
62 ids: InlineTypeIds<'a>,
63}
64
65impl<'a> RawGraph<'a> {
66 pub fn new(arena: &'a Arena, spec: &'a Spec<'a>) -> Self {
68 let tys = SpecTypeVisitor::new(
71 spec.schemas
72 .values()
73 .chain(spec.operations.iter().flat_map(|op| op.types().copied())),
74 );
75
76 let mut indices = FxHashMap::default();
78 let mut schemas = FxHashMap::default();
79 let mut graph = RawDiGraph::default();
80 for (parent, child) in tys {
81 use std::collections::hash_map::Entry;
82
83 let source = spec.resolve(child);
84 let &mut to = match indices.entry(source) {
85 Entry::Occupied(entry) => entry.into_mut(),
86 Entry::Vacant(entry) => {
87 let index = graph.add_node(match *entry.key() {
91 ResolvedSpecType::Schema(&ty) => GraphType::Schema(ty.into()),
92 ResolvedSpecType::Inline(&ty) => GraphType::Inline(ty.into()),
93 });
94 if let ResolvedSpecType::Schema(ty) = source {
95 schemas.entry(ty.name()).or_insert(index);
96 }
97 entry.insert(index)
98 }
99 };
100
101 if let Some((parent, edge)) = parent {
102 let destination = spec.resolve(parent);
103 let &mut from = match indices.entry(destination) {
104 Entry::Occupied(entry) => entry.into_mut(),
105 Entry::Vacant(entry) => {
106 let index = graph.add_node(match *entry.key() {
107 ResolvedSpecType::Schema(&ty) => GraphType::Schema(ty.into()),
108 ResolvedSpecType::Inline(&ty) => GraphType::Inline(ty.into()),
109 });
110 if let ResolvedSpecType::Schema(ty) = destination {
111 schemas.entry(ty.name()).or_insert(index);
112 }
113 entry.insert(index)
114 }
115 };
116 graph.add_edge(from, to, edge);
117 }
118 }
119
120 let ops = arena.alloc_slice_exact(spec.operations.iter().map(|op| {
122 let params = arena.alloc_slice_exact(op.params.iter().map(|param| match param {
123 Parameter::Path(info) => Parameter::Path(ParameterInfo {
124 name: info.name,
125 ty: match info.ty {
126 SpecType::Schema(s) => indices[&ResolvedSpecType::Schema(s)],
127 SpecType::Inline(i) => indices[&ResolvedSpecType::Inline(i)],
128 SpecType::Ref(r) => schemas[&*r.name()],
129 },
130 required: info.required,
131 description: info.description,
132 style: info.style,
133 }),
134 Parameter::Query(info) => Parameter::Query(ParameterInfo {
135 name: info.name,
136 ty: match info.ty {
137 SpecType::Schema(s) => indices[&ResolvedSpecType::Schema(s)],
138 SpecType::Inline(i) => indices[&ResolvedSpecType::Inline(i)],
139 SpecType::Ref(r) => schemas[&*r.name()],
140 },
141 required: info.required,
142 description: info.description,
143 style: info.style,
144 }),
145 }));
146
147 let request = op.request.as_ref().map(|r| match r {
148 Request::Json(ty) => Request::Json(match ty {
149 SpecType::Schema(s) => indices[&ResolvedSpecType::Schema(s)],
150 SpecType::Inline(i) => indices[&ResolvedSpecType::Inline(i)],
151 SpecType::Ref(r) => schemas[&*r.name()],
152 }),
153 Request::Multipart => Request::Multipart,
154 });
155
156 let response = op.response.as_ref().map(|r| match r {
157 Response::Json(ty) => Response::Json(match ty {
158 SpecType::Schema(s) => indices[&ResolvedSpecType::Schema(s)],
159 SpecType::Inline(i) => indices[&ResolvedSpecType::Inline(i)],
160 SpecType::Ref(r) => schemas[&*r.name()],
161 }),
162 });
163
164 &*arena.alloc(Operation {
165 id: op.id,
166 method: op.method,
167 path: op.path,
168 resource: op.resource,
169 description: op.description,
170 params,
171 request,
172 response,
173 })
174 }));
175
176 Self {
177 arena,
178 spec,
179 graph,
180 schemas,
181 ops,
182 ids: spec.ids,
183 }
184 }
185
186 pub fn inline_tagged_variants(&mut self) -> &mut Self {
206 let inlinables = self.inlinable_tagged_variants().collect_vec();
209
210 let mut retargets = FxHashMap::default();
211 retargets.reserve(inlinables.len());
212
213 for InlinableVariant { tagged, variant } in inlinables {
216 let id = self.ids.next();
219 let index = self
220 .graph
221 .add_node(GraphType::Inline(GraphInlineType::Struct(id, variant.ty)));
222
223 let original_field_edges = self.fields(variant.index).collect_vec();
234 for edge in original_field_edges.into_iter().rev() {
235 self.graph.add_edge(
236 index,
237 edge.target,
238 GraphEdge::Field {
239 meta: edge.meta,
240 shadow: true,
241 },
242 );
243 }
244
245 self.graph
250 .add_edge(index, tagged.index, GraphEdge::Inherits { shadow: true });
251 self.graph
252 .add_edge(index, variant.index, GraphEdge::Inherits { shadow: true });
253
254 retargets.insert((tagged.index, variant.index), index);
255 }
256
257 let taggeds: FixedBitSet = retargets
259 .keys()
260 .map(|&(tagged, _)| tagged.index())
261 .collect();
262 for index in taggeds.ones().map(NodeIndex::new) {
263 let old_edges = self
264 .graph
265 .edges_directed(index, Direction::Outgoing)
266 .filter(|e| matches!(e.weight(), GraphEdge::Variant(_)))
267 .map(|e| (e.id(), *e.weight(), e.target()))
268 .collect_vec();
269 for &(id, _, _) in &old_edges {
270 self.graph.remove_edge(id);
271 }
272 for (_, weight, target) in old_edges.into_iter().rev() {
275 let new_target = retargets.get(&(index, target)).copied().unwrap_or(target);
276 self.graph.add_edge(index, new_target, weight);
277 }
278 }
279
280 self
281 }
282
283 pub fn inline_untagged_variants(&mut self) -> &mut Self {
288 let inlinables = self.inlinable_untagged_variants().collect_vec();
289
290 let mut retargets = FxHashMap::default();
291 retargets.reserve(inlinables.len());
292
293 for InlinableUntaggedVariant { untagged, variant } in inlinables {
294 match variant {
295 VariantStruct::Schema(variant) => {
296 let id = self.ids.next();
297 let index = self
298 .graph
299 .add_node(GraphType::Inline(GraphInlineType::Struct(id, variant.ty)));
300
301 let original_field_edges = self.fields(variant.index).collect_vec();
302 for edge in original_field_edges.into_iter().rev() {
303 self.graph.add_edge(
304 index,
305 edge.target,
306 GraphEdge::Field {
307 meta: edge.meta,
308 shadow: true,
309 },
310 );
311 }
312
313 self.graph.add_edge(
314 index,
315 untagged.index,
316 GraphEdge::Inherits { shadow: true },
317 );
318 self.graph
319 .add_edge(index, variant.index, GraphEdge::Inherits { shadow: true });
320
321 retargets.insert((untagged.index, variant.index), index);
322 }
323 VariantStruct::Inline(variant) => {
324 self.graph.add_edge(
325 variant,
326 untagged.index,
327 GraphEdge::Inherits { shadow: true },
328 );
329 }
330 }
331 }
332
333 let untaggeds: FixedBitSet = retargets
334 .keys()
335 .map(|&(untagged, _)| untagged.index())
336 .collect();
337 for index in untaggeds.ones().map(NodeIndex::new) {
338 let old_edges = self
339 .graph
340 .edges_directed(index, Direction::Outgoing)
341 .filter(|e| matches!(e.weight(), GraphEdge::Variant(_)))
342 .map(|e| (e.id(), *e.weight(), e.target()))
343 .collect_vec();
344 for &(id, _, _) in &old_edges {
345 self.graph.remove_edge(id);
346 }
347 for (_, weight, target) in old_edges.into_iter().rev() {
350 let new_target = retargets.get(&(index, target)).copied().unwrap_or(target);
351 self.graph.add_edge(index, new_target, weight);
352 }
353 }
354
355 self
356 }
357
358 pub fn collapse_trivial_inlines(&mut self) -> &mut Self {
371 let inherits_to_any = self
375 .graph
376 .edge_references()
377 .filter(|edge| {
378 matches!(edge.weight(), GraphEdge::Inherits { .. })
379 && matches!(
380 self.graph[edge.target()],
381 GraphType::Schema(GraphSchemaType::Any(_))
382 | GraphType::Inline(GraphInlineType::Any(_))
383 )
384 })
385 .map(|edge| edge.id())
386 .collect_vec();
387 for id in inherits_to_any {
388 self.graph.remove_edge(id);
389 }
390
391 let collapsibles: FixedBitSet = self
394 .graph
395 .node_indices()
396 .filter(|&node| {
397 matches!(
398 self.graph[node],
399 GraphType::Inline(
400 GraphInlineType::Struct(..)
401 | GraphInlineType::Tagged(..)
402 | GraphInlineType::Untagged(..)
403 )
404 )
405 })
406 .filter(|&node| {
407 self.graph
408 .edges_directed(node, Direction::Outgoing)
409 .all(|edge| {
410 !matches!(
411 edge.weight(),
412 GraphEdge::Field { .. } | GraphEdge::Variant(_)
413 )
414 })
415 && self
416 .graph
417 .edges_directed(node, Direction::Outgoing)
418 .filter(|edge| {
419 matches!(edge.weight(), GraphEdge::Inherits { .. })
420 && edge.target() != node
421 })
422 .exactly_one()
423 .is_ok()
424 })
425 .map(|node| node.index())
426 .collect();
427
428 let closure = Closure::new(&EdgeFiltered::from_fn(&self.graph, |edge| {
435 matches!(edge.weight(), GraphEdge::Inherits { .. })
436 && collapsibles.contains(edge.source().index())
437 }));
438 let collapsed_to: FxHashMap<_, _> = collapsibles
439 .ones()
440 .map(NodeIndex::new)
441 .flat_map(|node| {
442 closure
443 .dependencies_of(node)
444 .find(|target| !collapsibles.contains(target.index()))
445 .map(|target| (node, target))
446 })
447 .collect();
448 if collapsed_to.is_empty() {
449 return self;
450 }
451
452 let sources_to_redirect: FixedBitSet = self
455 .graph
456 .edge_references()
457 .filter(|edge| {
458 collapsed_to.contains_key(&edge.target())
459 && !collapsed_to.contains_key(&edge.source())
460 })
461 .map(|edge| edge.source().index())
462 .collect();
463 for source in sources_to_redirect.ones().map(NodeIndex::new) {
464 let old_edges = self
465 .graph
466 .edges_directed(source, Direction::Outgoing)
467 .map(|edge| {
468 let old_target = edge.target();
469 let new_target = collapsed_to.get(&old_target).copied().unwrap_or(old_target);
470 (edge.id(), *edge.weight(), new_target)
471 })
472 .collect_vec();
473 for &(id, _, _) in &old_edges {
474 self.graph.remove_edge(id);
475 }
476 for (_, weight, target) in old_edges.into_iter().rev() {
480 self.graph.add_edge(source, target, weight);
481 }
482 }
483
484 let ops = self
486 .ops
487 .iter()
488 .map(|&op| {
489 let params = op
490 .params
491 .iter()
492 .map(|¶m| {
493 let rewrite = match param {
494 Parameter::Path(info) => collapsed_to
495 .get(&info.ty)
496 .map(|&ty| Parameter::Path(ParameterInfo { ty, ..info })),
497 Parameter::Query(info) => collapsed_to
498 .get(&info.ty)
499 .map(|&ty| Parameter::Query(ParameterInfo { ty, ..info })),
500 };
501 rewrite.unwrap_or(param)
502 })
503 .collect_vec();
504
505 let request = op
506 .request
507 .and_then(|request| match request {
508 Request::Json(ty) => {
509 let &ty = collapsed_to.get(&ty)?;
510 Some(Request::Json(ty))
511 }
512 Request::Multipart => None,
513 })
514 .or(op.request);
515
516 let response = op
517 .response
518 .and_then(|response| match response {
519 Response::Json(ty) => {
520 let &ty = collapsed_to.get(&ty)?;
521 Some(Response::Json(ty))
522 }
523 })
524 .or(op.response);
525
526 if params == op.params && request == op.request && response == op.response {
527 op
528 } else {
529 self.arena.alloc(Operation {
530 params: self.arena.alloc_slice_copy(¶ms),
531 request,
532 response,
533 ..*op
534 })
535 }
536 })
537 .collect_vec();
538 if ops != self.ops {
539 self.ops = self.arena.alloc_slice_copy(&ops);
540 }
541
542 for (node, _) in collapsed_to {
545 self.graph.remove_node(node);
546 }
547
548 self
549 }
550
551 #[inline]
553 pub fn cook(&self) -> CookedGraph<'a> {
554 CookedGraph::new(self)
555 }
556
557 fn fields(&self, node: NodeIndex<usize>) -> impl Iterator<Item = OutgoingEdge<FieldMeta<'a>>> {
560 self.graph
561 .edges_directed(node, Direction::Outgoing)
562 .filter_map(|e| match e.weight() {
563 &GraphEdge::Field { meta, .. } => {
564 let target = e.target();
565 Some(OutgoingEdge { meta, target })
566 }
567 _ => None,
568 })
569 }
570
571 fn inlinable_tagged_variants(&self) -> impl Iterator<Item = InlinableVariant<'a>> {
574 let used_by_ops: FixedBitSet = self
583 .ops
584 .iter()
585 .flat_map(|op| op.types())
586 .map(|index| index.index())
587 .collect();
588
589 self.inlinable_variants()
590 .filter_map(|variant| match variant {
591 InlinableUnionVariant::Tagged(tagged, VariantStruct::Schema(variant)) => {
592 Some((tagged, variant))
593 }
594 _ => None,
595 })
596 .filter_map(move |(tagged, variant)| {
597 if used_by_ops[variant.index.index()] {
603 return Some((tagged, variant));
604 }
605
606 let first_tagged = self
610 .graph
611 .neighbors_directed(variant.index, Direction::Incoming)
612 .find_map(|index| match self.graph[index] {
613 GraphType::Schema(GraphSchemaType::Tagged(_, ty)) => {
614 Some(Node { index, ty })
615 }
616 _ => None,
617 })?;
618 let all_agree = self
619 .graph
620 .neighbors_directed(variant.index, Direction::Incoming)
621 .all(|index| match self.graph[index] {
622 GraphType::Schema(GraphSchemaType::Tagged(_, ty)) => {
623 ty.tag == first_tagged.ty.tag
624 && self.fields(index).eq(self.fields(first_tagged.index))
625 }
626 _ => false,
627 });
628 if all_agree {
629 return None;
630 }
631 Some((tagged, variant))
632 })
633 .filter_map(|(tagged, variant)| {
634 let ancestors = EdgeFiltered::from_fn(&self.graph, |e| {
640 matches!(e.weight(), GraphEdge::Inherits { .. })
641 });
642 let mut dfs = DfsPostOrder::new(&ancestors, variant.index);
643 let has_tag_field = std::iter::from_fn(|| dfs.next(&ancestors))
644 .filter(|&n| {
645 matches!(
646 self.graph[n],
647 GraphType::Schema(GraphSchemaType::Struct(..))
648 | GraphType::Inline(GraphInlineType::Struct(..))
649 )
650 })
651 .any(|n| {
652 self.fields(n).any(|f| {
653 matches!(f.meta.name, StructFieldName::Name(name)
654 if name == tagged.ty.tag)
655 })
656 });
657
658 if has_tag_field {
662 return Some(InlinableVariant { tagged, variant });
663 }
664
665 if dfs.discovered[tagged.index.index()] {
668 return None;
669 }
670
671 self.fields(tagged.index).next()?;
674
675 Some(InlinableVariant { tagged, variant })
676 })
677 }
678
679 fn inlinable_untagged_variants(&self) -> impl Iterator<Item = InlinableUntaggedVariant<'a>> {
682 self.inlinable_variants()
683 .filter_map(|variant| {
684 let InlinableUnionVariant::Untagged(untagged, meta, variant) = variant else {
685 return None;
686 };
687 self.fields(untagged.index).next()?;
688 if meta.null {
689 return None;
690 }
691 Some((untagged, variant))
692 })
693 .filter_map(|(untagged, variant)| {
694 let ancestors = EdgeFiltered::from_fn(&self.graph, |e| {
695 matches!(e.weight(), GraphEdge::Inherits { .. })
696 });
697 let mut dfs = DfsPostOrder::new(&ancestors, variant.index());
698 while dfs.next(&ancestors).is_some() {}
699 if dfs.discovered[untagged.index.index()] {
700 return None;
701 }
702
703 Some(InlinableUntaggedVariant { untagged, variant })
704 })
705 }
706
707 fn inlinable_variants(&self) -> impl Iterator<Item = InlinableUnionVariant<'a>> {
710 self.graph
711 .node_indices()
712 .filter(|&index| {
713 matches!(
714 self.graph[index],
715 GraphType::Schema(GraphSchemaType::Tagged(..) | GraphSchemaType::Untagged(..))
716 )
717 })
718 .flat_map(move |index| {
719 self.graph
720 .edges_directed(index, Direction::Outgoing)
721 .filter_map(move |edge| {
722 let &GraphEdge::Variant(meta) = edge.weight() else {
723 return None;
724 };
725 let variant = {
726 let index = edge.target();
727 match self.graph[index] {
728 GraphType::Schema(GraphSchemaType::Struct(_, ty)) => {
729 VariantStruct::Schema(Node { index, ty })
730 }
731 GraphType::Inline(GraphInlineType::Struct(..)) => {
732 VariantStruct::Inline(index)
733 }
734 _ => return None,
735 }
736 };
737 Some(match (self.graph[index], meta) {
738 (
739 GraphType::Schema(GraphSchemaType::Tagged(_, ty)),
740 VariantMeta::Tagged(_),
741 ) => InlinableUnionVariant::Tagged(Node { index, ty }, variant),
742 (
743 GraphType::Schema(GraphSchemaType::Untagged(_, ty)),
744 VariantMeta::Untagged(meta),
745 ) => InlinableUnionVariant::Untagged(Node { index, ty }, meta, variant),
746 _ => unreachable!(),
747 })
748 })
749 })
750 }
751}
752
753#[derive(Debug)]
759pub struct CookedGraph<'a> {
760 arena: &'a Arena,
761 pub(super) graph: CookedDiGraph<'a>,
762 info: &'a Info,
763 schemas: FxHashMap<&'a str, NodeIndex<usize>>,
764 ops: &'a [&'a GraphOperation<'a>],
765 pub(super) metadata: CookedGraphMetadata<'a>,
767}
768
769impl<'a> CookedGraph<'a> {
770 fn new(raw: &RawGraph<'a>) -> Self {
771 let mut graph =
774 CookedDiGraph::with_capacity(raw.graph.node_count(), raw.graph.edge_count());
775 let mut indices =
776 FxHashMap::with_capacity_and_hasher(raw.graph.node_count(), FxBuildHasher);
777 for raw_index in raw.graph.node_indices() {
778 let cooked_index = graph.add_node(raw.graph[raw_index]);
779 indices.insert(raw_index, cooked_index);
780 }
781
782 for index in raw.graph.node_indices() {
791 let from = indices[&index];
792 let edges = raw
793 .graph
794 .edges(index)
795 .map(|e| (indices[&e.target()], *e.weight()));
796 for (to, kind) in edges {
797 graph.add_edge(from, to, kind);
798 }
799 }
800
801 let ops: &_ = raw.arena.alloc_slice_exact(raw.ops.iter().map(|&op| {
803 &*raw.arena.alloc(Operation {
804 id: op.id,
805 method: op.method,
806 path: op.path,
807 resource: op.resource,
808 description: op.description,
809 params: raw
810 .arena
811 .alloc_slice_exact(op.params.iter().map(|p| match p {
812 Parameter::Path(info) => Parameter::Path(ParameterInfo {
813 name: info.name,
814 ty: indices[&info.ty],
815 required: info.required,
816 description: info.description,
817 style: info.style,
818 }),
819 Parameter::Query(info) => Parameter::Query(ParameterInfo {
820 name: info.name,
821 ty: indices[&info.ty],
822 required: info.required,
823 description: info.description,
824 style: info.style,
825 }),
826 })),
827 request: op.request.as_ref().map(|r| match r {
828 Request::Json(ty) => Request::Json(indices[ty]),
829 Request::Multipart => Request::Multipart,
830 }),
831 response: op.response.as_ref().map(|r| match r {
832 Response::Json(ty) => Response::Json(indices[ty]),
833 }),
834 })
835 }));
836
837 let metadata = MetadataBuilder::new(raw.arena, &graph, ops).build();
838
839 Self {
840 arena: raw.arena,
841 graph,
842 info: raw.spec.info,
843 schemas: raw
844 .schemas
845 .iter()
846 .map(|(&name, index)| (name, indices[index]))
847 .collect(),
848 ops,
849 metadata,
850 }
851 }
852
853 #[inline]
855 pub fn arena(&self) -> &'a Arena {
856 self.arena
857 }
858
859 #[inline]
862 pub fn info(&self) -> &'a Info {
863 self.info
864 }
865
866 #[inline]
868 pub fn schemas(&self) -> impl Iterator<Item = SchemaTypeView<'_, 'a>> + use<'_, 'a> {
869 self.graph
870 .node_indices()
871 .filter_map(|index| match self.graph[index] {
872 GraphType::Schema(ty) => Some(SchemaTypeView::new(self, index, ty)),
873 _ => None,
874 })
875 }
876
877 #[inline]
879 pub fn schema(&self, name: &str) -> Option<SchemaTypeView<'_, 'a>> {
880 self.schemas
881 .get(name)
882 .and_then(|&index| match self.graph[index] {
883 GraphType::Schema(ty) => Some(SchemaTypeView::new(self, index, ty)),
884 _ => None,
885 })
886 }
887
888 #[inline]
890 pub fn view(&self, id: TypeId) -> TypeView<'_, 'a> {
891 TypeView::new(self, id.0)
892 }
893
894 #[inline]
896 pub fn primitives(&self) -> impl Iterator<Item = PrimitiveView<'_, 'a>> + use<'_, 'a> {
897 self.graph
898 .node_indices()
899 .filter_map(|index| match self.graph[index] {
900 GraphType::Schema(GraphSchemaType::Primitive(_, p))
901 | GraphType::Inline(GraphInlineType::Primitive(_, p)) => {
902 Some(PrimitiveView::new(self, index, p))
903 }
904 _ => None,
905 })
906 }
907
908 #[inline]
910 pub fn operations(&self) -> impl Iterator<Item = OperationView<'_, 'a>> + use<'_, 'a> {
911 self.ops.iter().map(|&op| OperationView::new(self, op))
912 }
913
914 #[inline]
915 pub(super) fn inherits(
916 &self,
917 node: NodeIndex<usize>,
918 ) -> impl Iterator<Item = OutgoingEdge<()>> {
919 self.graph
920 .edges_directed(node, Direction::Outgoing)
921 .filter(|e| matches!(e.weight(), GraphEdge::Inherits { .. }))
922 .map(|e| OutgoingEdge {
923 meta: (),
924 target: e.target(),
925 })
926 }
927
928 #[inline]
929 pub(super) fn fields(
930 &self,
931 node: NodeIndex<usize>,
932 ) -> impl Iterator<Item = OutgoingEdge<FieldMeta<'a>>> {
933 self.graph
934 .edges_directed(node, Direction::Outgoing)
935 .filter_map(|e| match e.weight() {
936 &GraphEdge::Field { meta, .. } => {
937 let target = e.target();
938 Some(OutgoingEdge { meta, target })
939 }
940 _ => None,
941 })
942 }
943
944 #[inline]
945 pub(super) fn variants(
946 &self,
947 node: NodeIndex<usize>,
948 ) -> impl Iterator<Item = OutgoingEdge<VariantMeta<'a>>> {
949 self.graph
950 .edges_directed(node, Direction::Outgoing)
951 .filter_map(|e| match e.weight() {
952 &GraphEdge::Variant(meta) => {
953 let target = e.target();
954 Some(OutgoingEdge { meta, target })
955 }
956 _ => None,
957 })
958 }
959}
960
961struct InlinableVariant<'a> {
963 tagged: Node<GraphTagged<'a>>,
965 variant: Node<GraphStruct<'a>>,
967}
968
969struct InlinableUntaggedVariant<'a> {
971 untagged: Node<GraphUntagged<'a>>,
973 variant: VariantStruct<'a>,
975}
976
977#[derive(Clone, Copy)]
979enum VariantStruct<'a> {
980 Schema(Node<GraphStruct<'a>>),
981 Inline(NodeIndex<usize>),
982}
983
984impl VariantStruct<'_> {
985 #[inline]
986 fn index(&self) -> NodeIndex<usize> {
987 match self {
988 Self::Schema(node) => node.index,
989 &Self::Inline(index) => index,
990 }
991 }
992}
993
994enum InlinableUnionVariant<'a> {
996 Tagged(Node<GraphTagged<'a>>, VariantStruct<'a>),
997 Untagged(
998 Node<GraphUntagged<'a>>,
999 UntaggedVariantMeta,
1000 VariantStruct<'a>,
1001 ),
1002}
1003
1004#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1008pub enum GraphEdge<'a> {
1009 Inherits { shadow: bool },
1011 Field { shadow: bool, meta: FieldMeta<'a> },
1014 Variant(VariantMeta<'a>),
1016 Contains,
1019}
1020
1021impl GraphEdge<'_> {
1022 #[inline]
1030 pub fn shadow(&self) -> bool {
1031 matches!(
1032 self,
1033 GraphEdge::Field { shadow: true, .. } | GraphEdge::Inherits { shadow: true }
1034 )
1035 }
1036}
1037
1038#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1040pub struct OutgoingEdge<T> {
1041 pub meta: T,
1042 pub target: NodeIndex<usize>,
1043}
1044
1045#[derive(Clone, Copy)]
1046struct Node<Ty> {
1047 index: NodeIndex<usize>,
1048 ty: Ty,
1049}
1050
1051pub(super) struct CookedGraphMetadata<'a> {
1053 pub closure: Closure,
1055 pub box_sccs: Vec<usize>,
1058 pub hashable: FixedBitSet,
1060 pub defaultable: FixedBitSet,
1062 pub used_by: Vec<Vec<GraphOperation<'a>>>,
1064 pub uses: FxHashMap<GraphOperation<'a>, FixedBitSet>,
1066 pub paths: FxHashMap<InlineTypeId, InlineTypePath<'a>>,
1068 pub extensions: Vec<AtomicRefCell<ExtensionMap>>,
1070}
1071
1072impl Debug for CookedGraphMetadata<'_> {
1073 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1074 f.debug_struct("CookedGraphMetadata")
1075 .field("closure", &self.closure)
1076 .field("box_sccs", &self.box_sccs)
1077 .field("hashable", &self.hashable)
1078 .field("defaultable", &self.defaultable)
1079 .field("used_by", &self.used_by)
1080 .field("uses", &self.uses)
1081 .finish_non_exhaustive()
1082 }
1083}
1084
1085struct HashDefault {
1088 hashable: FixedBitSet,
1089 defaultable: FixedBitSet,
1090}
1091
1092struct Operations<'a> {
1095 pub uses: FxHashMap<GraphOperation<'a>, FixedBitSet>,
1097 pub used_by: Vec<Vec<GraphOperation<'a>>>,
1099}
1100
1101#[derive(Clone, Copy, Debug)]
1103pub(super) struct InlineTypePath<'a> {
1104 pub root: InlineTypePathRoot<'a, NodeIndex<usize>, &'a str>,
1105 pub edges: &'a [EdgeIndex<usize>],
1106}
1107
1108struct MetadataBuilder<'graph, 'a> {
1109 arena: &'a Arena,
1110 graph: &'graph CookedDiGraph<'a>,
1111 ops: &'graph [&'graph GraphOperation<'a>],
1112 closure: Closure,
1114}
1115
1116impl<'graph, 'a> MetadataBuilder<'graph, 'a> {
1117 fn new(
1118 arena: &'a Arena,
1119 graph: &'graph CookedDiGraph<'a>,
1120 ops: &'graph [&'graph GraphOperation<'a>],
1121 ) -> Self {
1122 Self {
1123 arena,
1124 graph,
1125 ops,
1126 closure: Closure::new(graph),
1127 }
1128 }
1129
1130 fn build(self) -> CookedGraphMetadata<'a> {
1131 let operations = self.operations();
1132 let HashDefault {
1133 hashable,
1134 defaultable,
1135 } = self.hash_default();
1136 let box_sccs = self.box_sccs();
1137 let paths = self.paths();
1138 CookedGraphMetadata {
1139 closure: self.closure,
1140 box_sccs,
1141 hashable,
1142 defaultable,
1143 used_by: operations.used_by,
1144 uses: operations.uses,
1145 paths,
1146 extensions: std::iter::repeat_with(AtomicRefCell::default)
1149 .take(self.graph.node_count())
1150 .collect(),
1151 }
1152 }
1153
1154 fn operations(&self) -> Operations<'a> {
1155 let mut operations = Operations {
1156 uses: FxHashMap::default(),
1157 used_by: vec![vec![]; self.graph.node_count()],
1158 };
1159
1160 for &&op in self.ops {
1161 let mut dependencies = FixedBitSet::with_capacity(self.graph.node_count());
1164 for &node in op.types() {
1165 dependencies.extend(self.closure.dependencies_of(node).map(|n| n.index()));
1166 }
1167 operations.uses.entry(op).insert_entry(dependencies);
1168 }
1169
1170 for (op, deps) in &operations.uses {
1172 for node in deps.ones() {
1173 operations.used_by[node].push(*op);
1174 }
1175 }
1176
1177 operations
1178 }
1179
1180 fn paths(&self) -> FxHashMap<InlineTypeId, InlineTypePath<'a>> {
1182 #[derive(Clone)]
1183 struct PartialPath<'a> {
1184 root: InlineTypePathRoot<'a, NodeIndex<usize>, &'a str>,
1185 edges: Vec<EdgeIndex<usize>>,
1186 }
1187
1188 let filtered = EdgeFiltered::from_fn(self.graph, |e| {
1189 !e.weight().shadow() && matches!(self.graph[e.target()], GraphType::Inline(_))
1190 });
1191
1192 let mut by_node = FxHashMap::default();
1193 let mut bfs = EdgeBfs::new(&filtered);
1194
1195 for index in self.graph.node_indices() {
1197 if matches!(self.graph[index], GraphType::Schema(_)) && bfs.discover(index) {
1198 by_node.insert(
1199 index,
1200 PartialPath {
1201 root: InlineTypePathRoot::Schema(index),
1202 edges: vec![],
1203 },
1204 );
1205 }
1206 while let Some(edge) = bfs.next() {
1207 let parent = &by_node[&edge.source()];
1208 let mut child = parent.clone();
1209 child.edges.push(edge.id());
1210 by_node.insert(edge.target(), child);
1211 }
1212 }
1213
1214 for op in self.ops {
1217 for param in op.params {
1218 let (usage, info) = match param {
1219 Parameter::Path(info) => (OperationUsage::Path(info.name), info),
1220 Parameter::Query(info) => (OperationUsage::Query(info.name), info),
1221 };
1222 if matches!(self.graph[info.ty], GraphType::Inline(_)) && bfs.discover(info.ty) {
1223 by_node.insert(
1224 info.ty,
1225 PartialPath {
1226 root: InlineTypePathRoot::Operation {
1227 id: op.id,
1228 resource: op.resource,
1229 usage,
1230 },
1231 edges: vec![],
1232 },
1233 );
1234 }
1235 }
1236 if let Some(Request::Json(index)) = op.request
1237 && matches!(self.graph[index], GraphType::Inline(_))
1238 && bfs.discover(index)
1239 {
1240 by_node.insert(
1241 index,
1242 PartialPath {
1243 root: InlineTypePathRoot::Operation {
1244 id: op.id,
1245 resource: op.resource,
1246 usage: OperationUsage::Request,
1247 },
1248 edges: vec![],
1249 },
1250 );
1251 }
1252 if let Some(Response::Json(index)) = op.response
1253 && matches!(self.graph[index], GraphType::Inline(_))
1254 && bfs.discover(index)
1255 {
1256 by_node.insert(
1257 index,
1258 PartialPath {
1259 root: InlineTypePathRoot::Operation {
1260 id: op.id,
1261 resource: op.resource,
1262 usage: OperationUsage::Response,
1263 },
1264 edges: vec![],
1265 },
1266 );
1267 }
1268 while let Some(edge) = bfs.next() {
1269 let parent = &by_node[&edge.source()];
1270 let mut child = parent.clone();
1271 child.edges.push(edge.id());
1272 by_node.insert(edge.target(), child);
1273 }
1274 }
1275
1276 by_node
1277 .into_iter()
1278 .filter_map(
1279 |(index, PartialPath { root, edges })| match self.graph[index] {
1280 GraphType::Inline(ty) => Some((
1281 ty.id(),
1282 InlineTypePath {
1283 root,
1284 edges: self.arena.alloc_slice(edges),
1285 },
1286 )),
1287 _ => None,
1288 },
1289 )
1290 .collect()
1291 }
1292
1293 fn box_sccs(&self) -> Vec<usize> {
1294 let box_edges = EdgeFiltered::from_fn(self.graph, |e| match e.weight() {
1295 GraphEdge::Inherits { .. } => false,
1298 GraphEdge::Contains => match self.graph[e.source()] {
1299 GraphType::Schema(GraphSchemaType::Container(_, c))
1300 | GraphType::Inline(GraphInlineType::Container(_, c)) => {
1301 !matches!(c, GraphContainer::Array { .. } | GraphContainer::Map { .. })
1304 }
1305 _ => true,
1306 },
1307 _ => true,
1308 });
1309 let mut scc = TarjanScc::new();
1310 scc.run(&box_edges, |_| ());
1311 self.graph
1312 .node_indices()
1313 .map(|node| scc.node_component_index(&box_edges, node))
1314 .collect()
1315 }
1316
1317 fn hash_default(&self) -> HashDefault {
1318 let n = self.graph.node_count();
1320 let mut unhashable = FixedBitSet::with_capacity(n);
1321 let mut undefaultable = FixedBitSet::with_capacity(n);
1322 for node in self.graph.node_indices() {
1323 use {GraphType::*, PrimitiveType::*};
1324 match &self.graph[node] {
1325 Schema(GraphSchemaType::Primitive(_, F32 | F64))
1326 | Inline(GraphInlineType::Primitive(_, F32 | F64)) => {
1327 unhashable.insert(node.index());
1328 }
1329 Schema(
1330 GraphSchemaType::Primitive(_, Url)
1331 | GraphSchemaType::Tagged(_, _)
1332 | GraphSchemaType::Untagged(_, _),
1333 )
1334 | Inline(
1335 GraphInlineType::Primitive(_, Url)
1336 | GraphInlineType::Tagged(_, _)
1337 | GraphInlineType::Untagged(_, _),
1338 ) => {
1339 undefaultable.insert(node.index());
1340 }
1341 _ => (),
1342 }
1343 }
1344
1345 let inherits = Closure::new(&EdgeFiltered::from_fn(self.graph, |e| {
1347 matches!(e.weight(), GraphEdge::Inherits { .. })
1348 }));
1349
1350 let mut queue: VecDeque<_> = unhashable.ones().map(NodeIndex::new).collect();
1356 while let Some(node) = queue.pop_front() {
1357 for edge in self.graph.edges_directed(node, Direction::Incoming) {
1358 let source = edge.source();
1359 match edge.weight() {
1360 GraphEdge::Contains | GraphEdge::Variant(_) => {
1361 if !unhashable.put(source.index()) {
1362 queue.push_back(source);
1363 }
1364 }
1365 GraphEdge::Field { .. } => {
1366 if !unhashable.put(source.index()) {
1367 queue.push_back(source);
1368 }
1369 for desc in inherits.dependents_of(source).filter(|&d| d != source) {
1373 if !unhashable.put(desc.index()) {
1374 queue.push_back(desc);
1375 }
1376 }
1377 }
1378 GraphEdge::Inherits { .. } => {}
1383 }
1384 }
1385 }
1386
1387 let mut queue: VecDeque<_> = undefaultable.ones().map(NodeIndex::new).collect();
1389 while let Some(node) = queue.pop_front() {
1390 for edge in self.graph.edges_directed(node, Direction::Incoming) {
1391 if !matches!(
1392 edge.weight(),
1393 GraphEdge::Field { meta, .. } if meta.required
1394 ) {
1395 continue;
1398 }
1399 let source = edge.source();
1400 if !undefaultable.put(source.index()) {
1401 queue.push_back(source);
1402 }
1403 for desc in inherits.dependents_of(source).filter(|&d| d != source) {
1407 if !undefaultable.put(desc.index()) {
1408 queue.push_back(desc);
1409 }
1410 }
1411 }
1412 }
1413
1414 HashDefault {
1415 hashable: invert(unhashable),
1416 defaultable: invert(undefaultable),
1417 }
1418 }
1419}
1420
1421fn invert(mut bits: FixedBitSet) -> FixedBitSet {
1423 bits.toggle_range(..);
1424 bits
1425}
1426
1427#[derive(Debug)]
1429struct SpecTypeVisitor<'a> {
1430 stack: Vec<(Option<(&'a SpecType<'a>, GraphEdge<'a>)>, &'a SpecType<'a>)>,
1431}
1432
1433impl<'a> SpecTypeVisitor<'a> {
1434 #[inline]
1436 fn new(roots: impl Iterator<Item = &'a SpecType<'a>>) -> Self {
1437 let mut stack = roots.map(|root| (None, root)).collect_vec();
1438 stack.reverse();
1439 Self { stack }
1440 }
1441}
1442
1443impl<'a> Iterator for SpecTypeVisitor<'a> {
1444 type Item = (Option<(&'a SpecType<'a>, GraphEdge<'a>)>, &'a SpecType<'a>);
1445
1446 fn next(&mut self) -> Option<Self::Item> {
1447 let (parent, top) = self.stack.pop()?;
1448 if matches!(parent, Some((parent, _)) if parent == top) {
1449 return Some((parent, top));
1452 }
1453 match top {
1454 SpecType::Schema(SpecSchemaType::Struct(_, ty))
1455 | SpecType::Inline(SpecInlineType::Struct(_, ty)) => {
1456 self.stack.extend(
1457 itertools::chain!(
1458 ty.fields.iter().map(|field| (
1459 GraphEdge::Field {
1460 shadow: false,
1461 meta: FieldMeta {
1462 name: field.name,
1463 required: field.required,
1464 description: field.description,
1465 flattened: field.flattened,
1466 },
1467 },
1468 field.ty
1469 )),
1470 ty.parents
1471 .iter()
1472 .map(|parent| (GraphEdge::Inherits { shadow: false }, *parent)),
1473 )
1474 .map(|(edge, ty)| (Some((top, edge)), ty))
1475 .rev(),
1476 );
1477 }
1478 SpecType::Schema(SpecSchemaType::Untagged(_, ty))
1479 | SpecType::Inline(SpecInlineType::Untagged(_, ty)) => {
1480 self.stack.extend(
1481 itertools::chain!(
1482 ty.fields.iter().map(|field| (
1483 GraphEdge::Field {
1484 shadow: false,
1485 meta: FieldMeta {
1486 name: field.name,
1487 required: field.required,
1488 description: field.description,
1489 flattened: field.flattened,
1490 },
1491 },
1492 field.ty
1493 )),
1494 ty.variants.iter().enumerate().map(|(index, variant)| {
1495 let ordinal = NonZeroUsize::new(index + 1).unwrap();
1496 match variant {
1497 &Some(ty) => (
1498 GraphEdge::Variant(
1499 UntaggedVariantMeta {
1500 ordinal,
1501 null: false,
1502 }
1503 .into(),
1504 ),
1505 ty,
1506 ),
1507 None => (
1510 GraphEdge::Variant(
1511 UntaggedVariantMeta {
1512 ordinal,
1513 null: true,
1514 }
1515 .into(),
1516 ),
1517 top,
1518 ),
1519 }
1520 }),
1521 ty.parents
1522 .iter()
1523 .map(|parent| (GraphEdge::Inherits { shadow: false }, *parent)),
1524 )
1525 .map(|(edge, ty)| (Some((top, edge)), ty))
1526 .rev(),
1527 );
1528 }
1529 SpecType::Schema(SpecSchemaType::Tagged(_, ty))
1530 | SpecType::Inline(SpecInlineType::Tagged(_, ty)) => {
1531 self.stack.extend(
1532 itertools::chain!(
1533 ty.fields.iter().map(|field| (
1534 GraphEdge::Field {
1535 shadow: false,
1536 meta: FieldMeta {
1537 name: field.name,
1538 required: field.required,
1539 description: field.description,
1540 flattened: field.flattened,
1541 },
1542 },
1543 field.ty
1544 )),
1545 ty.variants.iter().map(|variant| (
1546 GraphEdge::Variant(
1547 TaggedVariantMeta {
1548 name: variant.name,
1549 aliases: variant.aliases,
1550 }
1551 .into()
1552 ),
1553 variant.ty
1554 )),
1555 ty.parents
1556 .iter()
1557 .map(|parent| (GraphEdge::Inherits { shadow: false }, *parent)),
1558 )
1559 .map(|(edge, ty)| (Some((top, edge)), ty))
1560 .rev(),
1561 );
1562 }
1563 SpecType::Schema(SpecSchemaType::Container(_, container))
1564 | SpecType::Inline(SpecInlineType::Container(_, container)) => {
1565 self.stack
1566 .push((Some((top, GraphEdge::Contains)), container.inner().ty));
1567 }
1568 SpecType::Schema(
1569 SpecSchemaType::Enum(..) | SpecSchemaType::Primitive(..) | SpecSchemaType::Any(_),
1570 )
1571 | SpecType::Inline(
1572 SpecInlineType::Enum(..) | SpecInlineType::Primitive(..) | SpecInlineType::Any(_),
1573 ) => (),
1574 SpecType::Ref(_) => (),
1575 }
1576 Some((parent, top))
1577 }
1578}
1579
1580pub(super) type ExtensionMap = FxHashMap<StdTypeId, Box<dyn Extension>>;
1582
1583pub trait Extension: Any + Send + Sync {
1584 fn into_inner(self: Box<Self>) -> Box<dyn Any>;
1585}
1586
1587impl dyn Extension {
1588 #[inline]
1589 pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
1590 (self as &dyn Any).downcast_ref::<T>()
1591 }
1592}
1593
1594impl<T: Send + Sync + 'static> Extension for T {
1595 #[inline]
1596 fn into_inner(self: Box<Self>) -> Box<dyn Any> {
1597 self
1598 }
1599}
1600
1601struct TopoSccs<G> {
1608 graph: G,
1609 tarjan: TarjanScc<NodeIndex<usize>>,
1610 sccs: Vec<Vec<usize>>,
1611}
1612
1613impl<G> TopoSccs<G>
1614where
1615 G: Closable<NodeIndex<usize>> + Copy,
1616{
1617 fn new(graph: G) -> Self {
1618 let mut sccs = Vec::new();
1619 let mut tarjan = TarjanScc::new();
1620 tarjan.run(graph, |scc_nodes| {
1621 sccs.push(scc_nodes.iter().map(|node| node.index()).collect());
1622 });
1623 sccs.reverse();
1626 Self {
1627 graph,
1628 tarjan,
1629 sccs,
1630 }
1631 }
1632
1633 #[inline]
1634 fn scc_count(&self) -> usize {
1635 self.sccs.len()
1636 }
1637
1638 #[inline]
1640 fn topo_index(&self, node: NodeIndex<usize>) -> usize {
1641 self.sccs.len() - 1 - self.tarjan.node_component_index(self.graph, node)
1644 }
1645
1646 fn condensation(&self) -> UnweightedList<usize> {
1653 let mut dag = UnweightedList::with_capacity(self.scc_count());
1654 for to in 0..self.scc_count() {
1655 dag.add_node();
1656 for neighbor in self.sccs[to].iter().flat_map(|&index| {
1657 self.graph
1658 .neighbors_directed(NodeIndex::new(index), Direction::Incoming)
1659 }) {
1660 let from = self.topo_index(neighbor);
1661 if from != to {
1662 dag.update_edge(from, to, ());
1663 }
1664 }
1665 }
1666 dag
1667 }
1668}
1669
1670#[derive(Debug)]
1672pub(super) struct Closure {
1673 scc_indices: Vec<usize>,
1675 scc_members: Vec<Vec<usize>>,
1677 scc_deps: Vec<Vec<usize>>,
1680 scc_rdeps: Vec<Vec<usize>>,
1683}
1684
1685impl Closure {
1686 fn new<G>(graph: G) -> Self
1688 where
1689 G: Closable<NodeIndex<usize>> + Copy,
1690 {
1691 let sccs = TopoSccs::new(graph);
1692 let condensation = sccs.condensation();
1693 let (_, closure) = tred::dag_transitive_reduction_closure(&condensation);
1694
1695 let scc_deps = (0..sccs.scc_count())
1698 .map(|scc| closure.neighbors(scc).collect_vec())
1699 .collect_vec();
1700 let mut scc_rdeps = vec![vec![]; sccs.scc_count()];
1701 for (scc, deps) in scc_deps.iter().enumerate() {
1702 for &dep in deps {
1703 scc_rdeps[dep].push(scc);
1704 }
1705 }
1706
1707 let mut scc_indices = vec![0; graph.node_count()];
1708 for node in graph.node_identifiers() {
1709 scc_indices[node.index()] = sccs.topo_index(node);
1710 }
1711
1712 Closure {
1713 scc_indices,
1714 scc_members: sccs.sccs.iter().cloned().collect_vec(),
1715 scc_deps,
1716 scc_rdeps,
1717 }
1718 }
1719
1720 #[inline]
1722 pub fn scc_index_of(&self, node: NodeIndex<usize>) -> usize {
1723 self.scc_indices[node.index()]
1724 }
1725
1726 pub fn dependencies_of(
1729 &self,
1730 node: NodeIndex<usize>,
1731 ) -> impl Iterator<Item = NodeIndex<usize>> {
1732 let scc = self.scc_index_of(node);
1733 std::iter::once(scc)
1734 .chain(self.scc_deps[scc].iter().copied())
1735 .flat_map(|s| self.scc_members[s].iter().copied()) .map(NodeIndex::new)
1737 }
1738
1739 pub fn dependents_of(&self, node: NodeIndex<usize>) -> impl Iterator<Item = NodeIndex<usize>> {
1742 let scc = self.scc_index_of(node);
1743 std::iter::once(scc)
1744 .chain(self.scc_rdeps[scc].iter().copied())
1745 .flat_map(|s| self.scc_members[s].iter().copied())
1746 .map(NodeIndex::new)
1747 }
1748
1749 #[inline]
1752 pub fn depends_on(&self, node: NodeIndex<usize>, other: NodeIndex<usize>) -> bool {
1753 if node == other {
1754 return false;
1755 }
1756 let scc = self.scc_index_of(node);
1757 let other_scc = self.scc_index_of(other);
1758 scc == other_scc || self.scc_deps[scc].contains(&other_scc)
1759 }
1760}
1761
1762trait Closable<N>:
1764 NodeCount
1765 + IntoNodeIdentifiers<NodeId = N>
1766 + IntoNeighbors<NodeId = N>
1767 + IntoNeighborsDirected<NodeId = N>
1768 + NodeIndexable<NodeId = N>
1769{
1770}
1771
1772impl<N, G> Closable<N> for G where
1773 G: NodeCount
1774 + IntoNodeIdentifiers<NodeId = N>
1775 + IntoNeighbors<NodeId = N>
1776 + IntoNeighborsDirected<NodeId = N>
1777 + NodeIndexable<NodeId = N>
1778{
1779}
1780
1781struct EdgeBfs<G, N, VM> {
1789 graph: G,
1790 queue: VecDeque<N>,
1791 discovered: VM,
1792}
1793
1794impl<G, N, VM> EdgeBfs<G, N, VM>
1795where
1796 N: Copy,
1797 VM: VisitMap<N>,
1798{
1799 fn new(graph: G) -> Self
1801 where
1802 G: GraphRef + Visitable<NodeId = N, Map = VM>,
1803 {
1804 let discovered = graph.visit_map();
1805 Self {
1806 graph,
1807 queue: VecDeque::new(),
1808 discovered,
1809 }
1810 }
1811
1812 fn discover(&mut self, node: N) -> bool {
1815 if self.discovered.visit(node) {
1816 self.queue.push_back(node);
1817 true
1818 } else {
1819 false
1820 }
1821 }
1822
1823 fn next(&mut self) -> Option<G::EdgeRef>
1830 where
1831 G: IntoEdges<NodeId = N>,
1832 {
1833 loop {
1834 let &source = self.queue.front()?;
1835 for edge in self.graph.edges(source) {
1836 let target = edge.target();
1837 if self.discovered.visit(target) {
1838 self.queue.push_back(target);
1839 return Some(edge);
1840 }
1841 }
1842 self.queue.pop_front();
1843 }
1844 }
1845}
1846
1847#[cfg(test)]
1848mod tests {
1849 use super::*;
1850
1851 use crate::tests::assert_matches;
1852
1853 fn linear_graph() -> DiGraph<(), (), usize> {
1855 let mut g = DiGraph::default();
1856 let a = g.add_node(());
1857 let b = g.add_node(());
1858 let c = g.add_node(());
1859 g.extend_with_edges([(a, b), (b, c)]);
1860 g
1861 }
1862
1863 fn cyclic_graph() -> DiGraph<(), (), usize> {
1865 let mut g = DiGraph::default();
1866 let a = g.add_node(());
1867 let b = g.add_node(());
1868 let c = g.add_node(());
1869 let d = g.add_node(());
1870 g.extend_with_edges([(a, b), (b, c), (c, a), (d, a)]);
1871 g
1872 }
1873
1874 #[test]
1877 fn test_linear_graph_has_singleton_sccs() {
1878 let g = linear_graph();
1879 let sccs = TopoSccs::new(&g);
1880 let sizes = sccs.sccs.iter().map(|scc| scc.len()).collect_vec();
1881 assert_matches!(&*sizes, [1, 1, 1]);
1882 }
1883
1884 #[test]
1885 fn test_cyclic_graph_has_one_multi_node_scc() {
1886 let g = cyclic_graph();
1887 let sccs = TopoSccs::new(&g);
1888
1889 let sizes = sccs.sccs.iter().map(|scc| scc.len()).collect_vec();
1892 assert_matches!(&*sizes, [1, 3]);
1893 }
1894
1895 #[test]
1898 fn test_sccs_are_in_topological_order() {
1899 let g = cyclic_graph();
1900 let sccs = TopoSccs::new(&g);
1901
1902 let d_topo = sccs.topo_index(3.into());
1903 let a_topo = sccs.topo_index(0.into());
1904 assert!(
1905 d_topo < a_topo,
1906 "D should precede A-B-C in topological order"
1907 );
1908 }
1909
1910 #[test]
1911 fn test_topo_index_consistent_within_scc() {
1912 let g = cyclic_graph();
1913 let sccs = TopoSccs::new(&g);
1914
1915 let a_topo = sccs.topo_index(0.into());
1918 let b_topo = sccs.topo_index(1.into());
1919 let c_topo = sccs.topo_index(2.into());
1920
1921 assert_eq!(a_topo, b_topo);
1922 assert_eq!(b_topo, c_topo);
1923 }
1924
1925 #[test]
1928 fn test_condensation_has_correct_node_count() {
1929 let g = cyclic_graph();
1930 let sccs = TopoSccs::new(&g);
1931 let dag = sccs.condensation();
1932
1933 assert_eq!(dag.node_count(), 2);
1934 }
1935
1936 #[test]
1937 fn test_condensation_has_correct_edges() {
1938 let g = cyclic_graph();
1939 let sccs = TopoSccs::new(&g);
1940 let dag = sccs.condensation();
1941
1942 let d_topo = sccs.topo_index(3.into());
1945 let abc_topo = sccs.topo_index(0.into());
1946
1947 let d_neighbors = dag.neighbors(d_topo).collect_vec();
1948 assert_eq!(&*d_neighbors, [abc_topo]);
1949
1950 let abc_neighbors = dag.neighbors(abc_topo).collect_vec();
1951 assert!(abc_neighbors.is_empty());
1952 }
1953
1954 #[test]
1955 fn test_condensation_neighbors_in_topological_order() {
1956 let mut g = DiGraph::<(), (), usize>::default();
1960 let second = g.add_node(());
1961 let top = g.add_node(());
1962 let first = g.add_node(());
1963 g.extend_with_edges([(top, second), (top, first), (first, second)]);
1964
1965 let sccs = TopoSccs::new(&g);
1966 let dag = sccs.condensation();
1967
1968 let top_topo = sccs.topo_index(top);
1969 assert_eq!(top_topo, 0);
1970
1971 let first_topo = sccs.topo_index(first);
1972 assert_eq!(first_topo, 1);
1973
1974 let second_topo = sccs.topo_index(second);
1975 assert_eq!(second_topo, 2);
1976
1977 let neighbors = dag.neighbors(top_topo).collect_vec();
1978 assert_eq!(&*neighbors, [first_topo, second_topo]);
1979 }
1980}