Repository: apollographql/apollo-rs
Pull request #848 (2024-03-28 13:42:35)

feat(compiler): serialize with initial indentation
This helps use indentation in something that is partly but not entirely GraphQL syntax, such as the debugging representation of an Apollo Router query plan. Example: `document.serialize().initial_indent_level(2).to_string()`


Patch:
diff --git a/crates/apollo-compiler/CHANGELOG.md b/crates/apollo-compiler/CHANGELOG.md
index b887b51..4145bc7 100644
--- a/crates/apollo-compiler/CHANGELOG.md
+++ b/crates/apollo-compiler/CHANGELOG.md
@@ -17,6 +17,19 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 ## Maintenance
 ## Documentation-->
 
+# [x.x.x] (unreleased) - 2024-mm-dd
+
+## Features
+
+- **Serialize with initial indentation - [SimonSapin], [pull/848]**
+  This helps use indentation in something that is partly but not entirely GraphQL syntax,
+  such as the debugging representation of an Apollo Router query plan.
+  Example: `document.serialize().initial_indent_level(2).to_string()`
+
+[SimonSapin]: https://github.com/SimonSapin
+[pull/838]: https://github.com/apollographql/apollo-rs/pull/848
+
+
 # [1.0.0-beta.14](https://crates.io/crates/apollo-compiler/1.0.0-beta.14) - 2024-03-13
 
 ## BREAKING
diff --git a/crates/apollo-compiler/src/ast/serialize.rs b/crates/apollo-compiler/src/ast/serialize.rs
index 04169f5..4350ad3 100644
--- a/crates/apollo-compiler/src/ast/serialize.rs
+++ b/crates/apollo-compiler/src/ast/serialize.rs
@@ -13,6 +13,7 @@ pub struct Serialize<'a, T> {
 #[derive(Debug, Clone)]
 pub(crate) struct Config<'a> {
     indent_prefix: Option<&'a str>,
+    initial_indent_level: usize,
 }
 
 pub(crate) struct State<'config, 'fmt, 'fmt2> {
@@ -38,12 +39,18 @@ impl<'a, T> Serialize<'a, T> {
         self.config.indent_prefix = None;
         self
     }
+
+    pub fn initial_indent_level(mut self, initial_indent_level: usize) -> Self {
+        self.config.initial_indent_level = initial_indent_level;
+        self
+    }
 }
 
 impl Default for Config<'_> {
     fn default() -> Self {
         Self {
             indent_prefix: Some("  "),
+            initial_indent_level: 0,
         }
     }
 }
@@ -979,12 +986,20 @@ macro_rules! impl_display {
             /// Serialize to GraphQL syntax
             impl Display for Serialize<'_, $ty> {
                 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                    self.node.serialize_impl(&mut State {
+                    let mut state = State {
                         config: self.config.clone(),
-                        indent_level: 0,
+                        indent_level: self.config.initial_indent_level,
                         output: f,
                         output_empty: true,
-                    })
+                    };
+                    // Indent the first line.
+                    // Subsequent lines will be indented when writing a line break.
+                    if let Some(prefix) = state.config.indent_prefix {
+                        for _ in 0..state.indent_level {
+                            state.write(prefix)?;
+                        }
+                    }
+                    self.node.serialize_impl(&mut state)
                 }
             }
         )+


Test patch:
diff --git a/crates/apollo-compiler/tests/misc.rs b/crates/apollo-compiler/tests/misc.rs
index 1210a50..92ab509 100644
--- a/crates/apollo-compiler/tests/misc.rs
+++ b/crates/apollo-compiler/tests/misc.rs
@@ -1,3 +1,4 @@
+use apollo_compiler::ast::Document;
 use apollo_compiler::executable::Selection;
 use apollo_compiler::name;
 use apollo_compiler::parse_mixed_validate;
@@ -825,3 +826,25 @@ scalar URL
         assert_eq!(result.unwrap().as_deref(), Some("URL"));
     }
 }
+
+#[test]
+fn initial_indent() {
+    let ast = Document::parse("query Op { field ... @defer { otherField }}", "").unwrap();
+    let formatted = format!(
+        "Request:\n  operationName: {}\n  query:\n{}",
+        "Op",
+        ast.serialize().initial_indent_level(2)
+    );
+    let expected = expect_test::expect![[r#"
+        Request:
+          operationName: Op
+          query:
+            query Op {
+              field
+              ... @defer {
+                otherField
+              }
+            }
+    "#]];
+    expected.assert_eq(&formatted);
+}