Closed dejang closed 4 months ago
Hi! Could you say a bit more about what you’re trying to do? Replacing all string values is maybe not the easiest example because apollo-rs currently does not provide a visitor, and string values are potentially found in a number of different places. (And arguably, do you really want to indiscriminately transform field arguments and directive arguments and variable defaults etc in the same way?). As for the more general question:
First of all, consider using apollo-compiler version 1.0 even though it is still in beta at this time. Versions 0.x are not as convenient and won’t get further development.
Next, pick a level of abstraction:
apollo_compiler::ast
has a representation close to GraphQL syntax, where a document can contain any mix of executable definitions (operations and fragments) and schema / type system definitions.apollo_compiler::Schema
and apollo_compiler::ExecutableDocument
have more semantics attached, but the latter requires a corresponding valid schema.Either way, many things are wrapped in either Node<_>
or Component<_>
. This provides reference-counting, so that reusing a sub-tree does not require deep copy. On the other hand, because of this sharing, the implement Deref
but not DerefMut
so their contents can’t be modified directly. To modify, call their .make_mut()
method which clones the content if it was shared, and returns a &mut _
exclusive reference. This is similar to (and if fact based on) the standard library’s Arc::make_mut
.
https://github.com/apollographql/apollo-rs/blob/apollo-compiler%401.0.0-beta.15/crates/apollo-compiler/examples/rename.rs provides an example (using make_mut
) of modifying a type in a schema. Here is an incomplete example for modifying string values in field argument. The string itself has type NodeStr
which is immutable, so this creates new ones by converting from String
with .into()
.
use apollo_compiler::ast::Definition;
use apollo_compiler::ast::Selection;
use apollo_compiler::ast::Value;
let query = r#"query { field(arg: "string") }"#;
let mut doc = apollo_compiler::ast::Document::parse(query, "example.graphql").unwrap();
for def in &mut doc.definitions {
match def {
Definition::OperationDefinition(operation) => {
let operation = operation.make_mut();
modify_selection_set(&mut operation.selection_set)
}
Definition::FragmentDefinition(_fragment) => todo!(),
// Other definition kinds are for schemas
_ => {}
}
}
println!("{doc}")
fn modify_selection_set(selection_set: &mut [Selection]) {
for selection in selection_set {
match selection {
Selection::Field(field) => {
let field = field.make_mut();
for arg in &mut field.arguments {
if let Value::String(string) = arg.make_mut().value.make_mut() {
*string = format!("{string}‽").into()
}
}
modify_selection_set(&mut field.selection_set);
}
Selection::FragmentSpread(_spread) => todo!(),
Selection::InlineFragment(_inline) => todo!(),
}
}
}
Thank you, this was what I needed.
First of all I apologize if this issue was opened in the wrong section.
I am trying out the parser in a small scale personal project in which I'd like to make some changes to the initial query. For the sake of keeping things simple let's assume I am trying to replace all occurrences of string values "John" to "John Smith" in the user provided query for all query arguments.
While looking through the opened issues I found an inspiration for a Visitor module which I could use to parse the AST nicely. Kudos to the owner of that issue for putting it up.
However, mutating the AST with or without such a module is still something I'm confused about. Would it possible to provide an example of how to alter the AST using the simple scenario in the first paragraph?