Skip to content

Commit

Permalink
add union types to schema (#309)
Browse files Browse the repository at this point in the history
* add union types to schema

* add union support to adapter

* avoid extra indirection when resolving union edge

* add union to supported item kinds

* resolve __typename of unions

This test case is also included in this commit as it exposed the bug.

* add unions to some glob test cases

* add tests
  • Loading branch information
fprasx authored Jan 13, 2024
1 parent 77d4483 commit 66a0ca9
Show file tree
Hide file tree
Showing 13 changed files with 563 additions and 12 deletions.
29 changes: 29 additions & 0 deletions src/adapter/edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,35 @@ pub(super) fn resolve_enum_edge<'a, V: AsVertex<Vertex<'a>> + 'a>(
}
}

pub(super) fn resolve_union_edge<'a, V: AsVertex<Vertex<'a>> + 'a>(
contexts: ContextIterator<'a, V>,
edge_name: &str,
current_crate: &'a IndexedCrate<'a>,
previous_crate: Option<&'a IndexedCrate<'a>>,
) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Vertex<'a>>> {
match edge_name {
"field" => resolve_neighbors_with(contexts, move |vertex| {
let origin = vertex.origin;
let union_item = vertex.as_union().expect("vertex was not an Union");

let item_index = match origin {
Origin::CurrentCrate => &current_crate.inner.index,
Origin::PreviousCrate => {
&previous_crate
.expect("no previous crate provided")
.inner
.index
}
};

Box::new(union_item.fields.iter().map(move |field_id| {
origin.make_item_vertex(item_index.get(field_id).expect("missing item"))
}))
}),
_ => unreachable!("resolve_union_edge {edge_name}"),
}
}

pub(super) fn resolve_struct_field_edge<'a, V: AsVertex<Vertex<'a>> + 'a>(
contexts: ContextIterator<'a, V>,
edge_name: &str,
Expand Down
24 changes: 16 additions & 8 deletions src/adapter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> {
"Crate" => properties::resolve_crate_property(contexts, property_name),
"Item" => properties::resolve_item_property(contexts, property_name),
"ImplOwner" | "Struct" | "StructField" | "Enum" | "Variant" | "PlainVariant"
| "TupleVariant" | "StructVariant" | "Trait" | "Function" | "Method" | "Impl"
| "GlobalValue" | "Constant" | "Static" | "AssociatedType"
| "TupleVariant" | "StructVariant" | "Union" | "Trait" | "Function" | "Method"
| "Impl" | "GlobalValue" | "Constant" | "Static" | "AssociatedType"
| "AssociatedConstant" | "Module"
if matches!(
property_name.as_ref(),
Expand All @@ -113,6 +113,7 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> {
"Module" => properties::resolve_module_property(contexts, property_name),
"Struct" => properties::resolve_struct_property(contexts, property_name),
"Enum" => properties::resolve_enum_property(contexts, property_name),
"Union" => properties::resolve_union_property(contexts, property_name),
"Span" => properties::resolve_span_property(contexts, property_name),
"Path" => properties::resolve_path_property(contexts, property_name),
"ImportablePath" => {
Expand Down Expand Up @@ -165,7 +166,7 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> {
match type_name.as_ref() {
"CrateDiff" => edges::resolve_crate_diff_edge(contexts, edge_name),
"Crate" => edges::resolve_crate_edge(self, contexts, edge_name, resolve_info),
"Importable" | "ImplOwner" | "Struct" | "Enum" | "Trait" | "Function"
"Importable" | "ImplOwner" | "Struct" | "Enum" | "Union" | "Trait" | "Function"
| "GlobalValue" | "Constant" | "Static" | "Module"
if matches!(edge_name.as_ref(), "importable_path" | "canonical_path") =>
{
Expand All @@ -177,14 +178,14 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> {
)
}
"Item" | "ImplOwner" | "Struct" | "StructField" | "Enum" | "Variant"
| "PlainVariant" | "TupleVariant" | "StructVariant" | "Trait" | "Function"
| "Method" | "Impl" | "GlobalValue" | "Constant" | "Static" | "AssociatedType"
| "AssociatedConstant" | "Module"
| "PlainVariant" | "TupleVariant" | "Union" | "StructVariant" | "Trait"
| "Function" | "Method" | "Impl" | "GlobalValue" | "Constant" | "Static"
| "AssociatedType" | "AssociatedConstant" | "Module"
if matches!(edge_name.as_ref(), "span" | "attribute") =>
{
edges::resolve_item_edge(contexts, edge_name)
}
"ImplOwner" | "Struct" | "Enum"
"ImplOwner" | "Struct" | "Enum" | "Union"
if matches!(edge_name.as_ref(), "impl" | "inherent_impl") =>
{
edges::resolve_impl_owner_edge(self, contexts, edge_name, resolve_info)
Expand Down Expand Up @@ -220,6 +221,12 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> {
self.current_crate,
self.previous_crate,
),
"Union" => edges::resolve_union_edge(
contexts,
edge_name,
self.current_crate,
self.previous_crate,
),
"StructField" => edges::resolve_struct_field_edge(contexts, edge_name),
"Impl" => edges::resolve_impl_edge(self, contexts, edge_name, resolve_info),
"Trait" => edges::resolve_trait_edge(
Expand Down Expand Up @@ -254,7 +261,7 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> {
actual_type_name,
"PlainVariant" | "TupleVariant" | "StructVariant"
),
"ImplOwner" => matches!(actual_type_name, "Struct" | "Enum"),
"ImplOwner" => matches!(actual_type_name, "Struct" | "Enum" | "Union"),
"GlobalValue" => matches!(actual_type_name, "Constant" | "Static",),
_ => {
// The remaining types are final (don't have any subtypes)
Expand All @@ -277,6 +284,7 @@ pub(crate) fn supported_item_kind(item: &Item) -> bool {
| rustdoc_types::ItemEnum::StructField(..)
| rustdoc_types::ItemEnum::Enum(..)
| rustdoc_types::ItemEnum::Variant(..)
| rustdoc_types::ItemEnum::Union(..)
| rustdoc_types::ItemEnum::Function(..)
| rustdoc_types::ItemEnum::Impl(..)
| rustdoc_types::ItemEnum::Trait(..)
Expand Down
7 changes: 4 additions & 3 deletions src/adapter/optimizations/impl_lookup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,14 @@ fn resolve_owner_impl_slow_path<'a>(
};

// Get the IDs of all the impl blocks.
// Relies on the fact that only structs and enums can have impls,
// so we know that the vertex must represent either a struct or an enum.
// Relies on the fact that only structs, enums, and unions can have impls,
// so we know that the vertex must represent either a struct, enum, or union.
let impl_ids = vertex
.as_struct()
.map(|s| &s.impls)
.or_else(|| vertex.as_enum().map(|e| &e.impls))
.expect("vertex was neither a struct nor an enum");
.or_else(|| vertex.as_union().map(|u| &u.impls))
.expect("vertex was neither a struct, enum, or union");

Box::new(impl_ids.iter().filter_map(move |item_id| {
let next_item = item_index.get(item_id);
Expand Down
12 changes: 12 additions & 0 deletions src/adapter/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,18 @@ pub(super) fn resolve_enum_property<'a, V: AsVertex<Vertex<'a>> + 'a>(
}
}

pub(super) fn resolve_union_property<'a, V: AsVertex<Vertex<'a>> + 'a>(
contexts: ContextIterator<'a, V>,
property_name: &str,
) -> ContextOutcomeIterator<'a, V, FieldValue> {
match property_name {
"fields_stripped" => {
resolve_property_with(contexts, field_property!(as_union, fields_stripped))
}
_ => unreachable!("Union property {property_name}"),
}
}

pub(super) fn resolve_path_property<'a, V: AsVertex<Vertex<'a>> + 'a>(
contexts: ContextIterator<'a, V>,
property_name: &str,
Expand Down
Loading

0 comments on commit 66a0ca9

Please sign in to comment.