diff --git a/src/adapter/mod.rs b/src/adapter/mod.rs index ddbfb9d..96af2e9 100644 --- a/src/adapter/mod.rs +++ b/src/adapter/mod.rs @@ -140,6 +140,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 18aec67..bdd24d1 100644 --- a/src/adapter/properties.rs +++ b/src/adapter/properties.rs @@ -423,3 +423,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 77e1170..65808fd 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 5603360..e6ff499 100644 --- a/src/rustdoc_schema.graphql +++ b/src/rustdoc_schema.graphql @@ -671,6 +671,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!] @@ -858,21 +913,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