Skip to main content

apollo_compiler/ast/
serialize.rs

1use super::*;
2use crate::executable;
3use crate::schema;
4use std::fmt;
5use std::fmt::Display;
6
7/// Builder pattern for GraphQL serialization configuration.
8/// Implements [`Display`] and [`ToString`].
9#[derive(Debug, Clone)]
10pub struct Serialize<'a, T> {
11    pub(crate) node: &'a T,
12    pub(crate) config: Config<'a>,
13}
14
15#[derive(Debug, Clone)]
16pub(crate) struct Config<'a> {
17    indent_prefix: Option<&'a str>,
18    initial_indent_level: usize,
19}
20
21pub(crate) struct State<'config, 'fmt, 'fmt2> {
22    config: Config<'config>,
23    indent_level: usize,
24    output: &'fmt mut fmt::Formatter<'fmt2>,
25    /// Have we not written anything yet?
26    output_empty: bool,
27}
28
29impl<'a, T> Serialize<'a, T> {
30    /// Enable indentation and line breaks.
31    ///
32    /// `prefix` is repeated at the start of each line by the number of indentation levels.
33    /// The default is `"  "`, two spaces.
34    pub fn indent_prefix(mut self, prefix: &'a str) -> Self {
35        self.config.indent_prefix = Some(prefix);
36        self
37    }
38
39    /// Disable indentation and line breaks
40    pub fn no_indent(mut self) -> Self {
41        self.config.indent_prefix = None;
42        self
43    }
44
45    pub fn initial_indent_level(mut self, initial_indent_level: usize) -> Self {
46        self.config.initial_indent_level = initial_indent_level;
47        self
48    }
49}
50
51impl Default for Config<'_> {
52    fn default() -> Self {
53        Self {
54            indent_prefix: Some("  "),
55            initial_indent_level: 0,
56        }
57    }
58}
59
60macro_rules! display {
61    ($state: expr, $e: expr) => {
62        fmt::Display::fmt(&$e, $state.output)
63    };
64    ($state: expr, $($tt: tt)+) => {
65        display!($state, format_args!($($tt)+))
66    };
67
68}
69
70impl State<'_, '_, '_> {
71    pub(crate) fn write(&mut self, str: &str) -> fmt::Result {
72        self.output_empty = false;
73        self.output.write_str(str)
74    }
75
76    pub(crate) fn indent(&mut self) -> fmt::Result {
77        self.indent_level += 1;
78        self.new_line_common(false)
79    }
80
81    pub(crate) fn indent_or_space(&mut self) -> fmt::Result {
82        self.indent_level += 1;
83        self.new_line_common(true)
84    }
85
86    pub(crate) fn dedent(&mut self) -> fmt::Result {
87        self.indent_level -= 1; // checked underflow in debug mode
88        self.new_line_common(false)
89    }
90
91    pub(crate) fn dedent_or_space(&mut self) -> fmt::Result {
92        self.indent_level -= 1; // checked underflow in debug mode
93        self.new_line_common(true)
94    }
95
96    pub(crate) fn new_line_or_space(&mut self) -> fmt::Result {
97        self.new_line_common(true)
98    }
99
100    fn new_line_common(&mut self, space: bool) -> fmt::Result {
101        if let Some(prefix) = self.config.indent_prefix {
102            self.write("\n")?;
103            for _ in 0..self.indent_level {
104                self.write(prefix)?;
105            }
106        } else if space {
107            self.write(" ")?
108        }
109        Ok(())
110    }
111
112    /// Panics if newlines are disabled
113    fn require_new_line(&mut self) -> fmt::Result {
114        let prefix = self
115            .config
116            .indent_prefix
117            .expect("require_new_line called with newlines disabled");
118        self.write("\n")?;
119        for _ in 0..self.indent_level {
120            self.write(prefix)?;
121        }
122        Ok(())
123    }
124
125    pub(crate) fn newlines_enabled(&self) -> bool {
126        self.config.indent_prefix.is_some()
127    }
128
129    pub(crate) fn on_single_line<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
130        let indent_prefix = self.config.indent_prefix.take();
131        let result = f(self);
132        self.config.indent_prefix = indent_prefix;
133        result
134    }
135}
136
137impl Document {
138    pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
139        top_level(state, &self.definitions, |state, def| {
140            def.serialize_impl(state)
141        })
142    }
143}
144
145pub(crate) fn top_level<T>(
146    state: &mut State,
147    iter: impl IntoIterator<Item = T>,
148    serialize_one: impl Fn(&mut State, T) -> fmt::Result,
149) -> fmt::Result {
150    let mut iter = iter.into_iter();
151    if let Some(first) = iter.next() {
152        serialize_one(state, first)?;
153        iter.try_for_each(|item| {
154            if state.newlines_enabled() {
155                // Empty line between top-level definitions
156                state.write("\n")?;
157            }
158            state.new_line_or_space()?;
159            serialize_one(state, item)
160        })?;
161        // Trailing newline
162        if state.newlines_enabled() {
163            state.write("\n")?;
164        }
165    }
166    Ok(())
167}
168
169impl Definition {
170    pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
171        match self {
172            Definition::OperationDefinition(def) => def.serialize_impl(state),
173            Definition::FragmentDefinition(def) => def.serialize_impl(state),
174            Definition::DirectiveDefinition(def) => def.serialize_impl(state),
175            Definition::SchemaDefinition(def) => def.serialize_impl(state),
176            Definition::ScalarTypeDefinition(def) => def.serialize_impl(state),
177            Definition::ObjectTypeDefinition(def) => def.serialize_impl(state),
178            Definition::InterfaceTypeDefinition(def) => def.serialize_impl(state),
179            Definition::UnionTypeDefinition(def) => def.serialize_impl(state),
180            Definition::EnumTypeDefinition(def) => def.serialize_impl(state),
181            Definition::InputObjectTypeDefinition(def) => def.serialize_impl(state),
182            Definition::SchemaExtension(def) => def.serialize_impl(state),
183            Definition::ScalarTypeExtension(def) => def.serialize_impl(state),
184            Definition::ObjectTypeExtension(def) => def.serialize_impl(state),
185            Definition::InterfaceTypeExtension(def) => def.serialize_impl(state),
186            Definition::UnionTypeExtension(def) => def.serialize_impl(state),
187            Definition::EnumTypeExtension(def) => def.serialize_impl(state),
188            Definition::InputObjectTypeExtension(def) => def.serialize_impl(state),
189        }
190    }
191}
192
193impl OperationDefinition {
194    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
195        // Deconstruct to get a warning if we forget to serialize something
196        let Self {
197            description,
198            operation_type,
199            name,
200            variables,
201            directives,
202            selection_set,
203        } = self;
204        // Only use shorthand when this is the first item.
205        // If not, it might be following a `[lookahead != "{"]` grammar production
206        let shorthand = state.output_empty
207            && description.is_none()
208            && *operation_type == OperationType::Query
209            && name.is_none()
210            && variables.is_empty()
211            && directives.is_empty();
212        if !shorthand {
213            serialize_description(state, description)?;
214            state.write(operation_type.name())?;
215            if let Some(name) = &name {
216                state.write(" ")?;
217                state.write(name)?;
218            }
219            if !variables.is_empty() {
220                state.on_single_line(|state| {
221                    comma_separated(state, "(", ")", variables, |state, var| {
222                        var.serialize_impl(state)
223                    })
224                })?
225            }
226            directives.serialize_impl(state)?;
227            state.write(" ")?;
228        }
229        curly_brackets_space_separated(state, selection_set, |state, sel| sel.serialize_impl(state))
230    }
231}
232
233impl FragmentDefinition {
234    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
235        let Self {
236            description,
237            name,
238            type_condition,
239            directives,
240            selection_set,
241        } = self;
242        serialize_description(state, description)?;
243        display!(state, "fragment {} on {}", name, type_condition)?;
244        directives.serialize_impl(state)?;
245        state.write(" ")?;
246        curly_brackets_space_separated(state, selection_set, |state, sel| sel.serialize_impl(state))
247    }
248}
249
250impl DirectiveDefinition {
251    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
252        let Self {
253            description,
254            name,
255            arguments,
256            repeatable,
257            locations,
258        } = self;
259        serialize_description(state, description)?;
260        state.write("directive @")?;
261        state.write(name)?;
262        serialize_arguments_definition(state, arguments)?;
263
264        if *repeatable {
265            state.write(" repeatable")?;
266        }
267        if let Some((first, rest)) = locations.split_first() {
268            state.write(" on ")?;
269            state.write(first.name())?;
270            for location in rest {
271                state.write(" | ")?;
272                state.write(location.name())?;
273            }
274        }
275        Ok(())
276    }
277}
278
279fn serialize_arguments_definition(
280    state: &mut State,
281    arguments: &[Node<InputValueDefinition>],
282) -> fmt::Result {
283    if !arguments.is_empty() {
284        let serialize_arguments = |state: &mut State| {
285            comma_separated(state, "(", ")", arguments, |state, arg| {
286                arg.serialize_impl(state)
287            })
288        };
289        if arguments
290            .iter()
291            .any(|arg| arg.description.is_some() || !arg.directives.is_empty())
292        {
293            serialize_arguments(state)?
294        } else {
295            state.on_single_line(serialize_arguments)?
296        }
297    }
298    Ok(())
299}
300
301impl SchemaDefinition {
302    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
303        let Self {
304            description,
305            directives,
306            root_operations,
307        } = self;
308        serialize_description(state, description)?;
309        state.write("schema")?;
310        directives.serialize_impl(state)?;
311        state.write(" ")?;
312        curly_brackets_space_separated(state, root_operations, |state, op| {
313            let (operation_type, operation_name) = &**op;
314            display!(state, "{}: {}", operation_type, operation_name)
315        })
316    }
317}
318
319impl ScalarTypeDefinition {
320    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
321        let Self {
322            description,
323            name,
324            directives,
325        } = self;
326        serialize_description(state, description)?;
327        state.write("scalar ")?;
328        state.write(name)?;
329        directives.serialize_impl(state)
330    }
331}
332
333impl ObjectTypeDefinition {
334    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
335        let Self {
336            description,
337            name,
338            implements_interfaces,
339            directives,
340            fields,
341        } = self;
342        serialize_description(state, description)?;
343        state.write("type ")?;
344        serialize_object_type_like(state, name, implements_interfaces, directives, fields)
345    }
346}
347
348fn serialize_object_type_like(
349    state: &mut State,
350    name: &str,
351    implements_interfaces: &[Name],
352    directives: &DirectiveList,
353    fields: &[Node<FieldDefinition>],
354) -> Result<(), fmt::Error> {
355    state.write(name)?;
356    if let Some((first, rest)) = implements_interfaces.split_first() {
357        state.write(" implements ")?;
358        state.write(first)?;
359        for name in rest {
360            state.write(" & ")?;
361            state.write(name)?;
362        }
363    }
364    directives.serialize_impl(state)?;
365
366    if !fields.is_empty() {
367        state.write(" ")?;
368        curly_brackets_space_separated(state, fields, |state, field| field.serialize_impl(state))?;
369    }
370    Ok(())
371}
372
373impl InterfaceTypeDefinition {
374    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
375        let Self {
376            description,
377            name,
378            implements_interfaces,
379            directives,
380            fields,
381        } = self;
382        serialize_description(state, description)?;
383        state.write("interface ")?;
384        serialize_object_type_like(state, name, implements_interfaces, directives, fields)
385    }
386}
387
388impl UnionTypeDefinition {
389    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
390        let Self {
391            description,
392            name,
393            directives,
394            members,
395        } = self;
396        serialize_description(state, description)?;
397        state.write("union ")?;
398        serialize_union(state, name, directives, members)
399    }
400}
401
402fn serialize_union(
403    state: &mut State,
404    name: &str,
405    directives: &DirectiveList,
406    members: &[Name],
407) -> fmt::Result {
408    state.write(name)?;
409    directives.serialize_impl(state)?;
410    if let Some((first, rest)) = members.split_first() {
411        state.write(" = ")?;
412        state.write(first)?;
413        for member in rest {
414            state.write(" | ")?;
415            state.write(member)?;
416        }
417    }
418    Ok(())
419}
420
421impl EnumTypeDefinition {
422    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
423        let Self {
424            description,
425            name,
426            directives,
427            values,
428        } = self;
429        serialize_description(state, description)?;
430        state.write("enum ")?;
431        state.write(name)?;
432        directives.serialize_impl(state)?;
433        if !values.is_empty() {
434            state.write(" ")?;
435            curly_brackets_space_separated(state, values, |state, value| {
436                value.serialize_impl(state)
437            })?;
438        }
439        Ok(())
440    }
441}
442
443impl InputObjectTypeDefinition {
444    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
445        let Self {
446            description,
447            name,
448            directives,
449            fields,
450        } = self;
451        serialize_description(state, description)?;
452        state.write("input ")?;
453        state.write(name)?;
454        directives.serialize_impl(state)?;
455        if !fields.is_empty() {
456            state.write(" ")?;
457            curly_brackets_space_separated(state, fields, |state, f| f.serialize_impl(state))?;
458        }
459        Ok(())
460    }
461}
462
463impl SchemaExtension {
464    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
465        let Self {
466            directives,
467            root_operations,
468        } = self;
469        state.write("extend schema")?;
470        directives.serialize_impl(state)?;
471        if !root_operations.is_empty() {
472            state.write(" ")?;
473            curly_brackets_space_separated(state, root_operations, |state, op| {
474                let (operation_type, operation_name) = &**op;
475                display!(state, "{}: {}", operation_type, operation_name)
476            })?;
477        }
478        Ok(())
479    }
480}
481
482impl ScalarTypeExtension {
483    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
484        let Self { name, directives } = self;
485        state.write("extend scalar ")?;
486        state.write(name)?;
487        directives.serialize_impl(state)
488    }
489}
490
491impl ObjectTypeExtension {
492    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
493        let Self {
494            name,
495            implements_interfaces,
496            directives,
497            fields,
498        } = self;
499        state.write("extend type ")?;
500        serialize_object_type_like(state, name, implements_interfaces, directives, fields)
501    }
502}
503
504impl InterfaceTypeExtension {
505    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
506        let Self {
507            name,
508            implements_interfaces,
509            directives,
510            fields,
511        } = self;
512        state.write("extend interface ")?;
513        serialize_object_type_like(state, name, implements_interfaces, directives, fields)
514    }
515}
516
517impl UnionTypeExtension {
518    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
519        let Self {
520            name,
521            directives,
522            members,
523        } = self;
524        state.write("extend union ")?;
525        serialize_union(state, name, directives, members)
526    }
527}
528
529impl EnumTypeExtension {
530    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
531        let Self {
532            name,
533            directives,
534            values,
535        } = self;
536        state.write("extend enum ")?;
537        state.write(name)?;
538        directives.serialize_impl(state)?;
539        if !values.is_empty() {
540            state.write(" ")?;
541            curly_brackets_space_separated(state, values, |state, value| {
542                value.serialize_impl(state)
543            })?;
544        }
545        Ok(())
546    }
547}
548
549impl InputObjectTypeExtension {
550    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
551        let Self {
552            name,
553            directives,
554            fields,
555        } = self;
556        state.write("extend input ")?;
557        state.write(name)?;
558        directives.serialize_impl(state)?;
559        if !fields.is_empty() {
560            state.write(" ")?;
561            curly_brackets_space_separated(state, fields, |state, f| f.serialize_impl(state))?;
562        }
563        Ok(())
564    }
565}
566
567impl DirectiveList {
568    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
569        for dir in self {
570            state.write(" ")?;
571            dir.serialize_impl(state)?;
572        }
573        Ok(())
574    }
575}
576
577impl Directive {
578    pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
579        let Self { name, arguments } = self;
580        state.write("@")?;
581        state.write(name)?;
582        serialize_arguments(state, arguments)
583    }
584}
585
586impl VariableDefinition {
587    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
588        let Self {
589            description,
590            name,
591            ty,
592            default_value,
593            directives,
594        } = self;
595        serialize_description(state, description)?;
596        state.write("$")?;
597        state.write(name)?;
598        state.write(": ")?;
599        display!(state, ty)?;
600        if let Some(value) = default_value {
601            state.write(" = ")?;
602            value.serialize_impl(state)?
603        }
604        directives.serialize_impl(state)
605    }
606}
607
608impl FieldDefinition {
609    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
610        let Self {
611            description,
612            name,
613            arguments,
614            ty,
615            directives,
616        } = self;
617        serialize_description(state, description)?;
618        state.write(name)?;
619        serialize_arguments_definition(state, arguments)?;
620        state.write(": ")?;
621        display!(state, ty)?;
622        directives.serialize_impl(state)
623    }
624}
625
626impl InputValueDefinition {
627    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
628        let Self {
629            description,
630            name,
631            ty,
632            default_value,
633            directives,
634        } = self;
635        serialize_description(state, description)?;
636        state.write(name)?;
637        state.write(": ")?;
638        display!(state, ty)?;
639        if let Some(value) = default_value {
640            state.write(" = ")?;
641            value.serialize_impl(state)?
642        }
643        directives.serialize_impl(state)
644    }
645}
646
647impl EnumValueDefinition {
648    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
649        let Self {
650            description,
651            value,
652            directives,
653        } = self;
654        serialize_description(state, description)?;
655        state.write(value)?;
656        directives.serialize_impl(state)
657    }
658}
659
660impl Selection {
661    pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
662        match self {
663            Selection::Field(x) => x.serialize_impl(state),
664            Selection::FragmentSpread(x) => x.serialize_impl(state),
665            Selection::InlineFragment(x) => x.serialize_impl(state),
666        }
667    }
668}
669
670impl Field {
671    pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
672        let Self {
673            alias,
674            name,
675            arguments,
676            directives,
677            selection_set,
678        } = self;
679        if let Some(alias) = alias {
680            state.write(alias)?;
681            state.write(": ")?;
682        }
683        state.write(name)?;
684        serialize_arguments(state, arguments)?;
685        directives.serialize_impl(state)?;
686        if !selection_set.is_empty() {
687            state.write(" ")?;
688            curly_brackets_space_separated(state, selection_set, |state, sel| {
689                sel.serialize_impl(state)
690            })?
691        }
692        Ok(())
693    }
694}
695
696impl FragmentSpread {
697    pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
698        let Self {
699            fragment_name,
700            directives,
701        } = self;
702        state.write("...")?;
703        state.write(fragment_name)?;
704        directives.serialize_impl(state)
705    }
706}
707
708impl InlineFragment {
709    pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
710        let Self {
711            type_condition,
712            directives,
713            selection_set,
714        } = self;
715        if let Some(type_name) = type_condition {
716            state.write("... on ")?;
717            state.write(type_name)?;
718        } else {
719            state.write("...")?;
720        }
721        directives.serialize_impl(state)?;
722        state.write(" ")?;
723        curly_brackets_space_separated(state, selection_set, |state, sel| sel.serialize_impl(state))
724    }
725}
726
727impl Value {
728    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
729        match self {
730            Value::Null => state.write("null"),
731            Value::Boolean(true) => state.write("true"),
732            Value::Boolean(false) => state.write("false"),
733            Value::Enum(name) => state.write(name),
734            Value::String(value) => {
735                let is_description = false;
736                serialize_string_value(state, is_description, value)
737            }
738            Value::Variable(name) => display!(state, "${}", name),
739            Value::Float(value) => display!(state, value),
740            Value::Int(value) => display!(state, value),
741            Value::List(value) => comma_separated(state, "[", "]", value, |state, value| {
742                value.serialize_impl(state)
743            }),
744            Value::Object(value) => {
745                comma_separated(state, "{", "}", value, |state, (name, value)| {
746                    state.write(name)?;
747                    state.write(": ")?;
748                    value.serialize_impl(state)
749                })
750            }
751        }
752    }
753}
754
755impl Argument {
756    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
757        state.write(&self.name)?;
758        state.write(": ")?;
759        self.value.serialize_impl(state)
760    }
761}
762
763fn serialize_arguments(state: &mut State, arguments: &[Node<Argument>]) -> fmt::Result {
764    if !arguments.is_empty() {
765        state.on_single_line(|state| {
766            comma_separated(state, "(", ")", arguments, |state, argument| {
767                argument.serialize_impl(state)
768            })
769        })?
770    }
771    Ok(())
772}
773
774/// Example output: `[a, b, c]` or
775///
776/// ```text
777/// [
778///     a,
779///     b,
780///     c,
781/// ]
782/// ```
783fn comma_separated<T>(
784    state: &mut State,
785    open: &str,
786    close: &str,
787    values: &[T],
788    serialize_one: impl Fn(&mut State, &T) -> fmt::Result,
789) -> fmt::Result {
790    state.write(open)?;
791    if let Some((first, rest)) = values.split_first() {
792        state.indent()?;
793        serialize_one(state, first)?;
794        for value in rest {
795            state.write(",")?;
796            state.new_line_or_space()?;
797            serialize_one(state, value)?;
798        }
799        // Trailing comma
800        if state.newlines_enabled() {
801            state.write(",")?;
802        }
803        state.dedent()?;
804    }
805    state.write(close)
806}
807
808/// Example output: `{ a b c }` or
809///
810/// ```text
811/// {
812///     a
813///     b
814///     c
815/// }
816/// ```
817pub(crate) fn curly_brackets_space_separated<T>(
818    state: &mut State,
819    values: &[T],
820    serialize_one: impl Fn(&mut State, &T) -> fmt::Result,
821) -> fmt::Result {
822    state.write("{")?;
823    if let Some((first, rest)) = values.split_first() {
824        state.indent_or_space()?;
825        serialize_one(state, first)?;
826        for value in rest {
827            state.new_line_or_space()?;
828            serialize_one(state, value)?;
829        }
830        state.dedent_or_space()?;
831    }
832    state.write("}")
833}
834
835fn serialize_string_value(state: &mut State, is_description: bool, mut str: &str) -> fmt::Result {
836    let contains_newline = str.contains('\n');
837    let prefer_block_string = is_description || contains_newline;
838    if state.newlines_enabled() && prefer_block_string && can_be_block_string(str) {
839        return serialize_block_string(state, contains_newline, str);
840    }
841    state.write("\"")?;
842    loop {
843        if let Some(i) = str.find(|c| (c < ' ' && c != '\t') || c == '"' || c == '\\') {
844            let (without_escaping, rest) = str.split_at(i);
845            state.write(without_escaping)?;
846            // All characters that need escaping are in the ASCII range,
847            // and so take a single byte in UTF-8.
848            match rest.as_bytes()[0] {
849                b'\x08' => state.write("\\b")?,
850                b'\n' => state.write("\\n")?,
851                b'\x0C' => state.write("\\f")?,
852                b'\r' => state.write("\\r")?,
853                b'"' => state.write("\\\"")?,
854                b'\\' => state.write("\\\\")?,
855                byte => display!(state, "\\u{:04X}", byte)?,
856            }
857            str = &rest[1..]
858        } else {
859            state.write(str)?;
860            break;
861        }
862    }
863    state.write("\"")
864}
865
866fn serialize_block_string(state: &mut State, contains_newline: bool, str: &str) -> fmt::Result {
867    const TRIPLE_QUOTE: &str = "\"\"\"";
868    const ESCAPED_TRIPLE_QUOTE: &str = "\\\"\"\"";
869    const _: () = assert!(TRIPLE_QUOTE.len() == 3);
870    const _: () = assert!(ESCAPED_TRIPLE_QUOTE.len() == 4);
871
872    fn serialize_line(state: &mut State, mut line: &str) -> Result<(), fmt::Error> {
873        while let Some((before, after)) = line.split_once(TRIPLE_QUOTE) {
874            state.write(before)?;
875            state.write(ESCAPED_TRIPLE_QUOTE)?;
876            line = after;
877        }
878        state.write(line)
879    }
880
881    let multi_line =
882        contains_newline || str.len() > 70 || str.ends_with('"') || str.ends_with('\\');
883
884    state.write(TRIPLE_QUOTE)?;
885    if !multi_line {
886        // """example""""
887        serialize_line(state, str)?
888    } else {
889        // """
890        // example
891        // """
892
893        // `can_be_block_string` excludes \r, so the only remaining line terminator is \n
894        for line in str.split('\n') {
895            if line.is_empty() {
896                // Skip indentation which would be trailing whitespace
897                state.write("\n")?;
898            } else {
899                state.require_new_line()?;
900                serialize_line(state, line)?;
901            }
902        }
903        state.require_new_line()?;
904    }
905    state.write(TRIPLE_QUOTE)
906}
907
908/// Is it possible to create a serialization that, when fed through
909/// [BlockStringValue](https://spec.graphql.org/September2025/#BlockStringValue()),
910/// returns exactly `value`?
911fn can_be_block_string(value: &str) -> bool {
912    // `BlockStringValue` splits its inputs at any `LineTerminator` (\n, \r\n, or \r)
913    // and eventually joins lines but always with \n. So its output can never contain \r
914    if value.contains('\r') {
915        return false;
916    }
917
918    /// <https://spec.graphql.org/September2025/#Whitespace>
919    fn trim_start_graphql_whitespace(value: &str) -> &str {
920        value.trim_start_matches([' ', '\t'])
921    }
922
923    // With the above, \n is the only remaining LineTerminator
924    let mut lines = value.split('\n');
925    if lines
926        .next()
927        .is_some_and(|first| trim_start_graphql_whitespace(first).is_empty())
928        || lines
929            .next_back()
930            .is_some_and(|last| trim_start_graphql_whitespace(last).is_empty())
931    {
932        // Leading or trailing whitespace-only line would be trimmed by `BlockStringValue`
933        return false;
934    }
935
936    let common_indent = {
937        let lines = value.split('\n');
938        let each_line_indent_utf8_len = lines.filter_map(|line| {
939            let after_indent = trim_start_graphql_whitespace(line);
940            if !after_indent.is_empty() {
941                Some(line.len() - after_indent.len())
942            } else {
943                None // skip whitespace-only lines
944            }
945        });
946        each_line_indent_utf8_len.min().unwrap_or(0)
947    };
948    // If there is common indent `BlockStringValue` would remove it
949    // and incorrectly round-trip to a different value.
950    common_indent == 0
951}
952
953fn serialize_description(state: &mut State, description: &Option<Node<str>>) -> fmt::Result {
954    if let Some(description) = description {
955        let is_description = true;
956        serialize_string_value(state, is_description, description)?;
957        state.new_line_or_space()?;
958    }
959    Ok(())
960}
961
962impl fmt::Display for Type {
963    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
964        match self {
965            Type::Named(name) => std::write!(f, "{name}"),
966            Type::NonNullNamed(name) => std::write!(f, "{name}!"),
967            Type::List(inner) => std::write!(f, "[{inner}]"),
968            Type::NonNullList(inner) => std::write!(f, "[{inner}]!"),
969        }
970    }
971}
972
973impl fmt::Display for OperationType {
974    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
975        self.name().fmt(f)
976    }
977}
978
979impl fmt::Display for DirectiveLocation {
980    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
981        self.name().fmt(f)
982    }
983}
984
985macro_rules! impl_display {
986    ($($ty: path)+) => {
987        $(
988            /// Serialize to GraphQL syntax with the default configuration
989            impl Display for $ty {
990                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
991                    self.serialize().fmt(f)
992                }
993            }
994
995            /// Serialize to GraphQL syntax
996            impl Display for Serialize<'_, $ty> {
997                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
998                    let mut state = State {
999                        config: self.config.clone(),
1000                        indent_level: self.config.initial_indent_level,
1001                        output: f,
1002                        output_empty: true,
1003                    };
1004                    // Indent the first line.
1005                    // Subsequent lines will be indented when writing a line break.
1006                    if let Some(prefix) = state.config.indent_prefix {
1007                        for _ in 0..state.indent_level {
1008                            state.write(prefix)?;
1009                        }
1010                    }
1011                    self.node.serialize_impl(&mut state)
1012                }
1013            }
1014        )+
1015    }
1016}
1017
1018impl_display! {
1019    Document
1020    Definition
1021    OperationDefinition
1022    FragmentDefinition
1023    DirectiveDefinition
1024    SchemaDefinition
1025    ScalarTypeDefinition
1026    ObjectTypeDefinition
1027    InterfaceTypeDefinition
1028    UnionTypeDefinition
1029    EnumTypeDefinition
1030    InputObjectTypeDefinition
1031    SchemaExtension
1032    ScalarTypeExtension
1033    ObjectTypeExtension
1034    InterfaceTypeExtension
1035    UnionTypeExtension
1036    EnumTypeExtension
1037    InputObjectTypeExtension
1038    DirectiveList
1039    Directive
1040    VariableDefinition
1041    FieldDefinition
1042    InputValueDefinition
1043    EnumValueDefinition
1044    Selection
1045    Field
1046    FragmentSpread
1047    InlineFragment
1048    Value
1049    crate::Schema
1050    crate::ExecutableDocument
1051    schema::DirectiveList
1052    schema::ExtendedType
1053    schema::ScalarType
1054    schema::ObjectType
1055    schema::InterfaceType
1056    schema::EnumType
1057    schema::UnionType
1058    schema::InputObjectType
1059    executable::Operation
1060    executable::Fragment
1061    executable::SelectionSet
1062    executable::Selection
1063    executable::Field
1064    executable::InlineFragment
1065    executable::FragmentSpread
1066    executable::FieldSet
1067}