From 85a4470b7aaba48a26a680d74a69a73a257a2662 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 9 Oct 2023 10:37:12 +0700 Subject: [PATCH] Add Constant type/value properties (#280) * add Constant property resolution function allow reading constant value/expr/is_literal and type (this one serialized to a json string since there is no impl to convert it to FieldValue) * add Constant property rustdocs example * add Constant property rustdoc schema * dep: add serde_json for Constant property Type * fix clippy * uglify with rustfmt * Update src/rustdoc_schema.graphql Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> * squeeze doc comment * add a doc comment note referencing a more comprehensive example to avoid repeating said example * add type_ field to AssociatedConstant property resolution function (temporary in the json string format) * add type_ field to AssociatedConstant property rustdoc schema * split schema docs so that each Constant property is documented * update schema doc comment * update schema docs trait name * fix a typo in schema docs * remove type Assoc/Const property until trustfall adds proper support for custom scalar types * remove type Assoc/Const property from schema * fix a typo in schema docs Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> * dep: remove serde_json since Constant property Type was removed Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> * remove unstable rustdocs example for Constant properties Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> * Update formatting of the schema docs Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> * Update AssociatedConstant example to refer to a const generic * Update Constant test with extra properties expr/value/is_literal --------- Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> --- src/adapter/mod.rs | 1 + src/adapter/properties.rs | 36 +++++++++++++++++ src/adapter/tests.rs | 36 +++++++++++++++-- src/rustdoc_schema.graphql | 79 +++++++++++++++++++++++++++++++++----- 4 files changed, 139 insertions(+), 13 deletions(-) diff --git a/src/adapter/mod.rs b/src/adapter/mod.rs index ed43285..3b21f2a 100644 --- a/src/adapter/mod.rs +++ b/src/adapter/mod.rs @@ -141,6 +141,7 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> { "AssociatedConstant" => { properties::resolve_associated_constant_property(contexts, property_name) } + "Constant" => properties::resolve_constant_property(contexts, property_name), _ => unreachable!("resolve_property {type_name} {property_name}"), } } diff --git a/src/adapter/properties.rs b/src/adapter/properties.rs index 5bba3cc..3c4de81 100644 --- a/src/adapter/properties.rs +++ b/src/adapter/properties.rs @@ -465,3 +465,39 @@ pub(crate) fn resolve_associated_constant_property<'a>( _ => unreachable!("AssociatedConstant property {property_name}"), } } + +pub(crate) fn resolve_constant_property<'a>( + contexts: ContextIterator<'a, Vertex<'a>>, + property_name: &str, +) -> ContextOutcomeIterator<'a, Vertex<'a>, FieldValue> { + match property_name { + "expr" => resolve_property_with( + contexts, + field_property!(as_item, inner, { + let ItemEnum::Constant(c) = &inner else { + unreachable!("expected to have a Constant") + }; + c.expr.clone().into() + }), + ), + "value" => resolve_property_with( + contexts, + field_property!(as_item, inner, { + let ItemEnum::Constant(c) = &inner else { + unreachable!("expected to have a Constant") + }; + c.value.clone().into() + }), + ), + "is_literal" => resolve_property_with( + contexts, + field_property!(as_item, inner, { + let ItemEnum::Constant(c) = &inner else { + unreachable!("expected to have a Constant") + }; + c.is_literal.into() + }), + ), + _ => unreachable!("Constant property {property_name}"), + } +} diff --git a/src/adapter/tests.rs b/src/adapter/tests.rs index 962eb48..63cd664 100644 --- a/src/adapter/tests.rs +++ b/src/adapter/tests.rs @@ -171,6 +171,9 @@ fn rustdoc_finds_consts() { item { ... on Constant { name @output + expr @output + value @output + is_literal @output importable_path { path @output @@ -190,24 +193,48 @@ fn rustdoc_finds_consts() { struct Output { name: String, path: Vec, + expr: String, + value: Option, + is_literal: bool, + } + #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize)] + struct OutputSimple { + name: String, + path: Vec, } let mut results: Vec<_> = trustfall::execute_query(&schema, adapter.clone(), query, variables.clone()) .expect("failed to run query") - .map(|row| row.try_into_struct().expect("shape mismatch")) + .map(|row| row.try_into_struct::().expect("shape mismatch")) .collect(); results.sort_unstable(); + // to compare to GlobalValue that doesn't Constant-specific properties + let mut results_simple: Vec<_> = + trustfall::execute_query(&schema, adapter.clone(), query, variables.clone()) + .expect("failed to run query") + .map(|row| { + row.try_into_struct::() + .expect("shape mismatch") + }) + .collect(); + results_simple.sort_unstable(); similar_asserts::assert_eq!( vec![ Output { name: "FIRST".into(), path: vec!["consts".into(), "FIRST".into()], + expr: "1".to_string(), + value: Some("1u32".to_string()), + is_literal: true, }, Output { name: "SECOND".into(), path: vec!["consts".into(), "inner".into(), "SECOND".into()], + expr: "2".to_string(), + value: Some("2i64".to_string()), + is_literal: true, }, ], results @@ -232,10 +259,13 @@ fn rustdoc_finds_consts() { let mut global_values_results: Vec<_> = trustfall::execute_query(&schema, adapter, global_values_query, variables) .expect("failed to run query") - .map(|row| row.try_into_struct().expect("shape mismatch")) + .map(|row| { + row.try_into_struct::() + .expect("shape mismatch") + }) .collect(); global_values_results.sort_unstable(); - assert_eq!(results, global_values_results); + assert_eq!(results_simple, global_values_results); } #[test] diff --git a/src/rustdoc_schema.graphql b/src/rustdoc_schema.graphql index 367b750..bc01cf8 100644 --- a/src/rustdoc_schema.graphql +++ b/src/rustdoc_schema.graphql @@ -695,6 +695,61 @@ type Constant implements Item & Importable & GlobalValue { attrs: [String!]! visibility_limit: String! + # properties for Constant + """ + The expression of the constant, if any, as a Rust literal or `"_"`. For example: + ```rust + // // expr + const MIN : usize = 16 ; // 16 + const MIN_SIZE: usize = MIN ; // "MIN", referring to the other constant's name + const LOG_AS : &str = "batch" ; // "\"batch\"", including escaped quotes + const YEAR : Years = Years(42); // "_" + const EXPR_2_2: i32 = 2 + 2 ; // "_" + const FN_FIVE : i32 = five() ; // "_" + const fn five() -> i32 { 5 }; + struct Years(i32); + ``` + If the constant is set: + + - to be equal to another constant, `expr` holds the name of that other constant. + - by evaluating a `const` expression, such as `2 + 2` or a `const fn` call, `expr` is `"_"` instead of including the full expression. + """ + expr: String + """ + The value of the constant, if any, as a Rust literal. For example: + ```rust + // // value + const MIN : usize = 16 ; // "16usize" + const MIN_SIZE: usize = MIN ; // "16usize" + const LOG_AS : &str = "batch" ; // None + const YEAR : Years = Years(42); // None + const EXPR_2_2: i32 = 2 + 2 ; // "4i32" + const FN_FIVE : i32 = five() ; // "5i32" + const fn five() -> i32 { 5 }; + struct Years(i32); + ``` + If the constant is set: + + - to be equal to another constant, `value` holds the value of that other constant. + - by evaluating a `const` expression, such as `2 + 2` or a `const fn` call, `value` is evaluated + """ + value: String + """ + The literal flag of the constant. For example: + ```rust + // // is_literal + const MIN : usize = 16 ; // true + const MIN_SIZE: usize = MIN ; // false + const LOG_AS : &str = "batch" ; // true + const YEAR : Years = Years(42); // false + const EXPR_2_2: i32 = 2 + 2 ; // false + const FN_FIVE : i32 = five() ; // false + const fn five() -> i32 { 5 }; + struct Years(i32); + ``` + """ + is_literal: Boolean + # edges from Item span: Span attribute: [Attribute!] @@ -882,21 +937,25 @@ type AssociatedConstant implements Item { For example: ```rust - trait BatchIterator { - const SIZE: usize = 16; // `"16"` is the default - const LOG_AS: &'static str = "batch"; // `"\"batch\""` is the default, including escaped quotes - const MIN_SIZE: usize = MIN; // "MIN" is the default, referring to the other constant's name + const fn five() -> i32 { 5 }; + struct Years(i32); + trait MyTrait { // rustdocs default field + const NUM : i32 = 16 ; // 16 + const MIN_SIZE: usize = MIN ; // "MIN", referring to the other constant's name + const LOG_AS : &'static str = "batch" ; // "\"batch\"", including escaped quotes + const EXPR2_2 : i32 = 2+2 ; // "_" + const FN_FIVE : i32 = five() ; // "_" + const YEAR : Years = Years(42); // "_" } ``` - If the associated constant is on a type's inherent impl, the default is always required to be set. + If the associated constant is on a type's inherent impl, `default` is always required to be set. - If the associated constant is set to be equal to another constant, the default holds the name - of that other constant. + If the associated constant is set: - If the associated constant is set by evaluating a `const` expression, such as `2 + 2` or - a `const fn` call, rustdoc's current behavior is to show a default value of `"_"` - instead of evaluating the constant value or including the full expression. + - to be equal to another constant, `default` holds the name of that other constant. + - by evaluating a `const` expression, such as `2 + 2` or a `const fn` call, + `default` is `"_"` instead of evaluating the constant value or including the full expression. """ default: String