feat(query): add ast counterpart to ObjectLiteral (#666)

This commit is contained in:
u9g 2023-07-30 18:15:57 -04:00 committed by GitHub
parent 29ddca52b7
commit 228cab9269
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 113 additions and 12 deletions

View file

@ -136,11 +136,13 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> {
property_name.as_ref(),
resolve_info,
),
"ObjectLiteral" => super::properties::resolve_object_literal_property(
contexts,
property_name.as_ref(),
resolve_info,
),
"ObjectLiteralAST" | "ObjectLiteral" => {
super::properties::resolve_object_literal_property(
contexts,
property_name.as_ref(),
resolve_info,
)
}
"PathPart" => super::properties::resolve_path_part_property(
contexts,
property_name.as_ref(),
@ -323,11 +325,12 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> {
parameters,
resolve_info,
),
"ObjectLiteral" => super::edges::resolve_object_literal_edge(
"ObjectLiteral" | "ObjectLiteralAST" => super::edges::resolve_object_literal_edge(
contexts,
edge_name.as_ref(),
parameters,
resolve_info,
self,
),
"PathPart" => super::edges::resolve_path_part_edge(
contexts,

View file

@ -1272,6 +1272,7 @@ pub(super) fn resolve_object_literal_edge<'a, 'b: 'a>(
edge_name: &str,
parameters: &EdgeParameters,
resolve_info: &ResolveEdgeInfo,
adapter: &'a Adapter<'b>,
) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> {
match edge_name {
"span" => object_literal::span(contexts, resolve_info),
@ -1283,6 +1284,8 @@ pub(super) fn resolve_object_literal_edge<'a, 'b: 'a>(
.expect("unexpected null or other incorrect datatype for Trustfall type 'String!'");
object_literal::value(contexts, key, resolve_info)
}
"ancestor" => ancestors(contexts, adapter),
"parent" => parents(contexts, adapter),
_ => {
unreachable!(
"attempted to resolve unexpected edge '{edge_name}' on type 'ObjectLiteral'"
@ -1322,7 +1325,7 @@ mod object_literal {
panic!("expected to have an objectliteral vertex, instead have: {v:#?}")
});
Box::new(obj.properties.iter().filter_map(move |property| {
Box::new(obj.object_expression.properties.iter().filter_map(move |property| {
let ObjectPropertyKind::ObjectProperty(prop) = property else { return None };
let has_right_key_name = match &prop.key {

View file

@ -306,6 +306,18 @@ type ObjectLiteral implements Expression & HasSpan {
span: Span!
}
type ObjectLiteralAST implements Expression & HasSpan & ASTNode {
value(key: String!): [Expression!]!
# Expression
as_constant_string: String
# ASTNode
parent: ASTNode
ancestor: [ASTNode!]!
# HasSpan
span: Span!
}
interface Expression implements HasSpan {
"""
Only non-null if the string can be trivially coerced to a constant string

View file

@ -138,6 +138,67 @@ query {
);
}
#[test]
fn test_object_literal_ast() {
let code = "const colors = {blue: 1, green: 2, red: {a: 1}};";
let allocator = Allocator::default();
let source_type = SourceType::default().with_module(true).with_jsx(true).with_typescript(true);
let ret = Parser::new(&allocator, code, source_type).parse();
let program = allocator.alloc(ret.program);
let semantic_ret =
SemanticBuilder::new(code, source_type).with_trivias(&ret.trivias).build(program);
let adapter = Adapter {
path_components: vec![Some("index".to_string())],
semantic: Rc::new(semantic_ret.semantic),
};
let args: BTreeMap<Arc<str>, FieldValue> = BTreeMap::new();
let adapter = Arc::from(&adapter);
#[allow(clippy::items_after_statements)]
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize)]
struct Output {
__typename: String,
value_typename: String,
}
let mut results: Vec<Output> = execute_query(
schema(),
adapter,
r#"
query {
File {
ast_node {
... on ObjectLiteralAST {
value(key: "red") {
value_typename: __typename @output
}
__typename @output
}
}
}
}
"#,
args,
)
.expect("to successfully execute the query")
.map(|row| row.try_into_struct().expect("shape mismatch"))
.collect::<Vec<_>>();
results.sort_unstable();
assert_eq!(
vec![Output {
__typename: "ObjectLiteralAST".to_owned(),
value_typename: "ObjectLiteral".to_owned()
}],
results
);
}
#[test]
fn test_parent_query() {
let code = "interface MyGreatInterface { myGreatProperty: number }";

View file

@ -41,7 +41,7 @@ pub enum Vertex<'a> {
JSXSpreadAttribute(&'a JSXSpreadAttribute<'a>),
JSXSpreadChild(&'a JSXSpreadChild<'a>),
JSXText(&'a JSXText),
ObjectLiteral(&'a ObjectExpression<'a>),
ObjectLiteral(Rc<ObjectLiteralVertex<'a>>),
PathPart(usize),
SearchParameter(Rc<SearchParameterVertex>),
Span(Span),
@ -77,7 +77,7 @@ impl<'a> Vertex<'a> {
Self::JSXSpreadAttribute(data) => data.span,
Self::JSXSpreadChild(data) => data.span,
Self::JSXText(data) => data.span,
Self::ObjectLiteral(data) => data.span,
Self::ObjectLiteral(data) => data.object_expression.span,
Self::SpecificImport(data) => data.span,
Self::TypeAnnotation(data) => data.type_annotation.span,
Self::Type(data) => data.span(),
@ -102,6 +102,7 @@ impl<'a> Vertex<'a> {
Vertex::JSXElement(data) => data.ast_node.map(|x| x.id()),
Vertex::TypeAnnotation(data) => data.ast_node.map(|x| x.id()),
Vertex::VariableDeclaration(data) => data.ast_node.map(|x| x.id()),
Vertex::ObjectLiteral(data) => data.ast_node.map(|x| x.id()),
Vertex::ReturnStatementAST(data) => data.ast_node.map(|x| x.id()),
Vertex::DefaultImport(_)
| Vertex::AssignmentType(_)
@ -112,7 +113,6 @@ impl<'a> Vertex<'a> {
| Vertex::JSXAttribute(_)
| Vertex::JSXExpressionContainer(_)
| Vertex::JSXFragment(_)
| Vertex::ObjectLiteral(_)
| Vertex::JSXText(_)
| Vertex::JSXSpreadChild(_)
| Vertex::JSXSpreadAttribute(_)
@ -168,7 +168,7 @@ impl Typename for Vertex<'_> {
Vertex::JSXSpreadAttribute(_) => "JSXSpreadAttribute",
Vertex::JSXSpreadChild(_) => "JSXSpreadChild",
Vertex::JSXText(_) => "JSXText",
Vertex::ObjectLiteral(_) => "ObjectLiteral",
Vertex::ObjectLiteral(objlit) => objlit.typename(),
Vertex::PathPart(_) => "PathPart",
Vertex::SearchParameter(_) => "SearchParameter",
Vertex::Span(_) => "Span",
@ -206,6 +206,9 @@ impl<'a> From<AstNode<'a>> for Vertex<'a> {
AstKind::Class(class) => {
Self::Class(ClassVertex { ast_node: Some(ast_node), class }.into())
}
AstKind::ObjectExpression(objexpr) => Self::ObjectLiteral(
ObjectLiteralVertex { ast_node: Some(ast_node), object_expression: objexpr }.into(),
),
_ => Vertex::ASTNode(ast_node),
}
}
@ -217,7 +220,9 @@ impl<'a> From<&'a Expression<'a>> for Vertex<'a> {
// NOTE: When string literal / template literal is added, add to as_constant_string
match &expr.get_inner_expression() {
Expression::ObjectExpression(objexpr) => Vertex::ObjectLiteral(objexpr),
Expression::ObjectExpression(object_expression) => Vertex::ObjectLiteral(
ObjectLiteralVertex { ast_node: None, object_expression }.into(),
),
Expression::JSXElement(element) => {
Vertex::JSXElement(JSXElementVertex { ast_node: None, element }.into())
}
@ -376,3 +381,20 @@ impl<'a> Typename for VariableDeclarationVertex<'a> {
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct ObjectLiteralVertex<'a> {
ast_node: Option<AstNode<'a>>,
pub object_expression: &'a ObjectExpression<'a>,
}
impl<'a> Typename for ObjectLiteralVertex<'a> {
fn typename(&self) -> &'static str {
if self.ast_node.is_some() {
"ObjectLiteralAST"
} else {
"ObjectLiteral"
}
}
}