From 6cde9feca4e0778b46c72afc6cf07be14e0b0e92 Mon Sep 17 00:00:00 2001 From: Kyle Anthony Williams Date: Sun, 21 Jul 2024 18:00:58 -0400 Subject: [PATCH] Add enum discriminants --- src/adapter/edges.rs | 10 ++++ src/adapter/mod.rs | 1 + src/adapter/origin.rs | 7 +++ src/adapter/properties.rs | 29 +++++++++++ src/adapter/tests.rs | 60 +++++++++++++++++++++++ src/adapter/vertex.rs | 17 ++++++- src/rustdoc_schema.graphql | 12 +++++ test_crates/enum_discriminants/Cargo.toml | 9 ++++ test_crates/enum_discriminants/src/lib.rs | 5 ++ 9 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 test_crates/enum_discriminants/Cargo.toml create mode 100644 test_crates/enum_discriminants/src/lib.rs diff --git a/src/adapter/edges.rs b/src/adapter/edges.rs index 2bd328cc..bef2b606 100644 --- a/src/adapter/edges.rs +++ b/src/adapter/edges.rs @@ -293,6 +293,16 @@ pub(super) fn resolve_variant_edge<'a, V: AsVertex> + 'a>( })), } }), + "discriminant" => resolve_neighbors_with(contexts, move |vertex| { + let origin = vertex.origin; + let item = vertex.as_variant().expect("vertex was not a Variant"); + + if let Some(discriminant) = &item.discriminant { + Box::new(std::iter::once(origin.make_discriminant_vertex(discriminant.clone()))) + } else { + Box::new(std::iter::empty()) + } + }), _ => unreachable!("resolve_variant_edge {edge_name}"), } } diff --git a/src/adapter/mod.rs b/src/adapter/mod.rs index 29569e0c..2a2d8d78 100644 --- a/src/adapter/mod.rs +++ b/src/adapter/mod.rs @@ -150,6 +150,7 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> { properties::resolve_associated_constant_property(contexts, property_name) } "Constant" => properties::resolve_constant_property(contexts, property_name), + "Discriminant" => properties::resolve_discriminant_property(contexts, property_name), _ => unreachable!("resolve_property {type_name} {property_name}"), } } diff --git a/src/adapter/origin.rs b/src/adapter/origin.rs index 5d92bdce..9ea4b1ef 100644 --- a/src/adapter/origin.rs +++ b/src/adapter/origin.rs @@ -96,4 +96,11 @@ impl Origin { kind: abi.into(), } } + + pub(super) fn make_discriminant_vertex<'a>(&self, discriminant: rustdoc_types::Discriminant) -> Vertex<'a> { + Vertex { + origin: *self, + kind: discriminant.into(), + } + } } diff --git a/src/adapter/properties.rs b/src/adapter/properties.rs index 7873c5cc..d30628f1 100644 --- a/src/adapter/properties.rs +++ b/src/adapter/properties.rs @@ -560,3 +560,32 @@ pub(crate) fn resolve_constant_property<'a, V: AsVertex> + 'a>( _ => unreachable!("Constant property {property_name}"), } } + +pub(crate) fn resolve_discriminant_property<'a, V: AsVertex> + 'a>( + contexts: ContextIterator<'a, V>, + property_name: &str, +) -> ContextOutcomeIterator<'a, V, FieldValue> { + match property_name { + "expr" => resolve_property_with( + contexts, + |vertex| { + vertex + .as_discriminant() + .expect("vertex was not a Discriminant") + .expr + .into() + }, + ), + "value" => resolve_property_with( + contexts, + |vertex| { + vertex + .as_discriminant() + .expect("vertex was not a Discriminant") + .value + .into() + }, + ), + _ => unreachable!("AssociatedConstant property {property_name}"), + } +} diff --git a/src/adapter/tests.rs b/src/adapter/tests.rs index 8dff6e93..c3c94c1f 100644 --- a/src/adapter/tests.rs +++ b/src/adapter/tests.rs @@ -1657,3 +1657,63 @@ fn unions() { similar_asserts::assert_eq!(expected_results, results); } + +#[test] +fn enum_discriminants() { + let path = "./localdata/test_data/enum_discriminants/rustdoc.json"; + let content = std::fs::read_to_string(path) + .with_context(|| format!("Could not load {path} file, did you forget to run ./scripts/regenerate_test_rustdocs.sh ?")) + .expect("failed to load rustdoc"); + + let crate_ = serde_json::from_str(&content).expect("failed to parse rustdoc"); + let indexed_crate = IndexedCrate::new(&crate_); + let adapter = RustdocAdapter::new(&indexed_crate, None); + + let query = r#" +{ + Crate { + item { + ... on Enum { + variant { + discriminant { + expr @output + value @output + } + } + } + } + } +} +"#; + let variables: BTreeMap<&str, &str> = btreemap! {}; + + let schema = + Schema::parse(include_str!("../rustdoc_schema.graphql")).expect("schema failed to parse"); + + #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize)] + struct Output { + expr: String, + value: String, + } + + let mut results: Vec = + trustfall::execute_query(&schema, adapter.into(), query, variables.clone()) + .expect("failed to run query") + .map(|row| row.try_into_struct().expect("shape mismatch")) + .collect(); + results.sort_unstable(); + + similar_asserts::assert_eq!( + vec![ + Output { + expr: "1".into(), + value: "1".into(), + }, + Output { + expr: "{ _ }".into(), + value: "2".into(), + }, + ], + results + ); +} diff --git a/src/adapter/vertex.rs b/src/adapter/vertex.rs index 94fc48be..f7aa366d 100644 --- a/src/adapter/vertex.rs +++ b/src/adapter/vertex.rs @@ -1,7 +1,7 @@ use std::rc::Rc; use rustdoc_types::{ - Abi, Constant, Crate, Enum, Function, Impl, Item, Module, Path, Span, Static, Struct, Trait, + Abi, Constant, Crate, Discriminant, Enum, Function, Impl, Item, Module, Path, Span, Static, Struct, Trait, Type, Union, Variant, VariantKind, }; use trustfall::provider::Typename; @@ -36,6 +36,7 @@ pub enum VertexKind<'a> { ImplementedTrait(&'a Path, &'a Item), FunctionParameter(&'a str), FunctionAbi(&'a Abi), + Discriminant(Discriminant), } impl<'a> Typename for Vertex<'a> { @@ -77,6 +78,7 @@ impl<'a> Typename for Vertex<'a> { }, VertexKind::FunctionParameter(..) => "FunctionParameter", VertexKind::FunctionAbi(..) => "FunctionAbi", + VertexKind::Discriminant(..) => "Discriminant", } } } @@ -254,6 +256,13 @@ impl<'a> Vertex<'a> { _ => None, } } + + pub(super) fn as_discriminant(&self) -> Option { + match &self.kind { + VertexKind::Discriminant(discriminant) => Some(discriminant.clone()), + _ => None, + } + } } impl<'a> From<&'a Item> for VertexKind<'a> { @@ -279,3 +288,9 @@ impl<'a> From<&'a Abi> for VertexKind<'a> { Self::FunctionAbi(a) } } + +impl<'a> From for VertexKind<'a> { + fn from(d: Discriminant) -> Self { + Self::Discriminant(d) + } +} diff --git a/src/rustdoc_schema.graphql b/src/rustdoc_schema.graphql index 9a6b07a6..2f766d56 100644 --- a/src/rustdoc_schema.graphql +++ b/src/rustdoc_schema.graphql @@ -429,6 +429,15 @@ interface Variant implements Item { # own edges field: [StructField!] + discriminant: Discriminant +} + +""" +https://docs.rs/rustdoc-types/latest/rustdoc_types/struct.Discriminant.html +""" +type Discriminant { + expr: String! + value: String! } """ @@ -483,6 +492,7 @@ type PlainVariant implements Item & Variant { # edges from Variant field: [StructField!] + discriminant: Discriminant } """ @@ -537,6 +547,7 @@ type TupleVariant implements Item & Variant { # edges from Variant field: [StructField!] + discriminant: Discriminant } """ @@ -591,6 +602,7 @@ type StructVariant implements Item & Variant { # edges from Variant field: [StructField!] + discriminant: Discriminant } """ diff --git a/test_crates/enum_discriminants/Cargo.toml b/test_crates/enum_discriminants/Cargo.toml new file mode 100644 index 00000000..e9b8c5fa --- /dev/null +++ b/test_crates/enum_discriminants/Cargo.toml @@ -0,0 +1,9 @@ +[package] +publish = false +name = "enum_discriminants" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/test_crates/enum_discriminants/src/lib.rs b/test_crates/enum_discriminants/src/lib.rs new file mode 100644 index 00000000..5b57eebb --- /dev/null +++ b/test_crates/enum_discriminants/src/lib.rs @@ -0,0 +1,5 @@ +#[repr(C)] +pub enum A { + One = 1, + Two = 1 + 1, +}