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        let mut loc_iter = locations.iter();
268        if let Some(first) = loc_iter.next() {
269            state.write(" on ")?;
270            state.write(first.name())?;
271            for location in loc_iter {
272                state.write(" | ")?;
273                state.write(location.name())?;
274            }
275        }
276        Ok(())
277    }
278}
279
280fn serialize_arguments_definition(
281    state: &mut State,
282    arguments: &[Node<InputValueDefinition>],
283) -> fmt::Result {
284    if !arguments.is_empty() {
285        let serialize_arguments = |state: &mut State| {
286            comma_separated(state, "(", ")", arguments, |state, arg| {
287                arg.serialize_impl(state)
288            })
289        };
290        if arguments
291            .iter()
292            .any(|arg| arg.description.is_some() || !arg.directives.is_empty())
293        {
294            serialize_arguments(state)?
295        } else {
296            state.on_single_line(serialize_arguments)?
297        }
298    }
299    Ok(())
300}
301
302impl SchemaDefinition {
303    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
304        let Self {
305            description,
306            directives,
307            root_operations,
308        } = self;
309        serialize_description(state, description)?;
310        state.write("schema")?;
311        directives.serialize_impl(state)?;
312        state.write(" ")?;
313        curly_brackets_space_separated(state, root_operations, |state, op| {
314            let (operation_type, operation_name) = &**op;
315            display!(state, "{}: {}", operation_type, operation_name)
316        })
317    }
318}
319
320impl ScalarTypeDefinition {
321    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
322        let Self {
323            description,
324            name,
325            directives,
326        } = self;
327        serialize_description(state, description)?;
328        state.write("scalar ")?;
329        state.write(name)?;
330        directives.serialize_impl(state)
331    }
332}
333
334impl ObjectTypeDefinition {
335    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
336        let Self {
337            description,
338            name,
339            implements_interfaces,
340            directives,
341            fields,
342        } = self;
343        serialize_description(state, description)?;
344        state.write("type ")?;
345        serialize_object_type_like(state, name, implements_interfaces, directives, fields)
346    }
347}
348
349fn serialize_object_type_like(
350    state: &mut State,
351    name: &str,
352    implements_interfaces: &[Name],
353    directives: &DirectiveList,
354    fields: &[Node<FieldDefinition>],
355) -> Result<(), fmt::Error> {
356    state.write(name)?;
357    if let Some((first, rest)) = implements_interfaces.split_first() {
358        state.write(" implements ")?;
359        state.write(first)?;
360        for name in rest {
361            state.write(" & ")?;
362            state.write(name)?;
363        }
364    }
365    directives.serialize_impl(state)?;
366
367    if !fields.is_empty() {
368        state.write(" ")?;
369        curly_brackets_space_separated(state, fields, |state, field| field.serialize_impl(state))?;
370    }
371    Ok(())
372}
373
374impl InterfaceTypeDefinition {
375    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
376        let Self {
377            description,
378            name,
379            implements_interfaces,
380            directives,
381            fields,
382        } = self;
383        serialize_description(state, description)?;
384        state.write("interface ")?;
385        serialize_object_type_like(state, name, implements_interfaces, directives, fields)
386    }
387}
388
389impl UnionTypeDefinition {
390    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
391        let Self {
392            description,
393            name,
394            directives,
395            members,
396        } = self;
397        serialize_description(state, description)?;
398        state.write("union ")?;
399        serialize_union(state, name, directives, members)
400    }
401}
402
403fn serialize_union(
404    state: &mut State,
405    name: &str,
406    directives: &DirectiveList,
407    members: &[Name],
408) -> fmt::Result {
409    state.write(name)?;
410    directives.serialize_impl(state)?;
411    if let Some((first, rest)) = members.split_first() {
412        state.write(" = ")?;
413        state.write(first)?;
414        for member in rest {
415            state.write(" | ")?;
416            state.write(member)?;
417        }
418    }
419    Ok(())
420}
421
422impl EnumTypeDefinition {
423    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
424        let Self {
425            description,
426            name,
427            directives,
428            values,
429        } = self;
430        serialize_description(state, description)?;
431        state.write("enum ")?;
432        state.write(name)?;
433        directives.serialize_impl(state)?;
434        if !values.is_empty() {
435            state.write(" ")?;
436            curly_brackets_space_separated(state, values, |state, value| {
437                value.serialize_impl(state)
438            })?;
439        }
440        Ok(())
441    }
442}
443
444impl InputObjectTypeDefinition {
445    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
446        let Self {
447            description,
448            name,
449            directives,
450            fields,
451        } = self;
452        serialize_description(state, description)?;
453        state.write("input ")?;
454        state.write(name)?;
455        directives.serialize_impl(state)?;
456        if !fields.is_empty() {
457            state.write(" ")?;
458            curly_brackets_space_separated(state, fields, |state, f| f.serialize_impl(state))?;
459        }
460        Ok(())
461    }
462}
463
464impl SchemaExtension {
465    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
466        let Self {
467            directives,
468            root_operations,
469        } = self;
470        state.write("extend schema")?;
471        directives.serialize_impl(state)?;
472        if !root_operations.is_empty() {
473            state.write(" ")?;
474            curly_brackets_space_separated(state, root_operations, |state, op| {
475                let (operation_type, operation_name) = &**op;
476                display!(state, "{}: {}", operation_type, operation_name)
477            })?;
478        }
479        Ok(())
480    }
481}
482
483impl ScalarTypeExtension {
484    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
485        let Self { name, directives } = self;
486        state.write("extend scalar ")?;
487        state.write(name)?;
488        directives.serialize_impl(state)
489    }
490}
491
492impl ObjectTypeExtension {
493    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
494        let Self {
495            name,
496            implements_interfaces,
497            directives,
498            fields,
499        } = self;
500        state.write("extend type ")?;
501        serialize_object_type_like(state, name, implements_interfaces, directives, fields)
502    }
503}
504
505impl InterfaceTypeExtension {
506    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
507        let Self {
508            name,
509            implements_interfaces,
510            directives,
511            fields,
512        } = self;
513        state.write("extend interface ")?;
514        serialize_object_type_like(state, name, implements_interfaces, directives, fields)
515    }
516}
517
518impl UnionTypeExtension {
519    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
520        let Self {
521            name,
522            directives,
523            members,
524        } = self;
525        state.write("extend union ")?;
526        serialize_union(state, name, directives, members)
527    }
528}
529
530impl EnumTypeExtension {
531    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
532        let Self {
533            name,
534            directives,
535            values,
536        } = self;
537        state.write("extend enum ")?;
538        state.write(name)?;
539        directives.serialize_impl(state)?;
540        if !values.is_empty() {
541            state.write(" ")?;
542            curly_brackets_space_separated(state, values, |state, value| {
543                value.serialize_impl(state)
544            })?;
545        }
546        Ok(())
547    }
548}
549
550impl InputObjectTypeExtension {
551    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
552        let Self {
553            name,
554            directives,
555            fields,
556        } = self;
557        state.write("extend input ")?;
558        state.write(name)?;
559        directives.serialize_impl(state)?;
560        if !fields.is_empty() {
561            state.write(" ")?;
562            curly_brackets_space_separated(state, fields, |state, f| f.serialize_impl(state))?;
563        }
564        Ok(())
565    }
566}
567
568impl DirectiveList {
569    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
570        for dir in self {
571            state.write(" ")?;
572            dir.serialize_impl(state)?;
573        }
574        Ok(())
575    }
576}
577
578impl Directive {
579    pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
580        let Self { name, arguments } = self;
581        state.write("@")?;
582        state.write(name)?;
583        serialize_arguments(state, arguments)
584    }
585}
586
587impl VariableDefinition {
588    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
589        let Self {
590            description,
591            name,
592            ty,
593            default_value,
594            directives,
595        } = self;
596        serialize_description(state, description)?;
597        state.write("$")?;
598        state.write(name)?;
599        state.write(": ")?;
600        display!(state, ty)?;
601        if let Some(value) = default_value {
602            state.write(" = ")?;
603            value.serialize_impl(state)?
604        }
605        directives.serialize_impl(state)
606    }
607}
608
609impl FieldDefinition {
610    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
611        let Self {
612            description,
613            name,
614            arguments,
615            ty,
616            directives,
617        } = self;
618        serialize_description(state, description)?;
619        state.write(name)?;
620        serialize_arguments_definition(state, arguments)?;
621        state.write(": ")?;
622        display!(state, ty)?;
623        directives.serialize_impl(state)
624    }
625}
626
627impl InputValueDefinition {
628    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
629        let Self {
630            description,
631            name,
632            ty,
633            default_value,
634            directives,
635        } = self;
636        serialize_description(state, description)?;
637        state.write(name)?;
638        state.write(": ")?;
639        display!(state, ty)?;
640        if let Some(value) = default_value {
641            state.write(" = ")?;
642            value.serialize_impl(state)?
643        }
644        directives.serialize_impl(state)
645    }
646}
647
648impl EnumValueDefinition {
649    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
650        let Self {
651            description,
652            value,
653            directives,
654        } = self;
655        serialize_description(state, description)?;
656        state.write(value)?;
657        directives.serialize_impl(state)
658    }
659}
660
661impl Selection {
662    pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
663        match self {
664            Selection::Field(x) => x.serialize_impl(state),
665            Selection::FragmentSpread(x) => x.serialize_impl(state),
666            Selection::InlineFragment(x) => x.serialize_impl(state),
667        }
668    }
669}
670
671impl Field {
672    pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
673        let Self {
674            alias,
675            name,
676            arguments,
677            directives,
678            selection_set,
679        } = self;
680        if let Some(alias) = alias {
681            state.write(alias)?;
682            state.write(": ")?;
683        }
684        state.write(name)?;
685        serialize_arguments(state, arguments)?;
686        directives.serialize_impl(state)?;
687        if !selection_set.is_empty() {
688            state.write(" ")?;
689            curly_brackets_space_separated(state, selection_set, |state, sel| {
690                sel.serialize_impl(state)
691            })?
692        }
693        Ok(())
694    }
695}
696
697impl FragmentSpread {
698    pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
699        let Self {
700            fragment_name,
701            directives,
702        } = self;
703        state.write("...")?;
704        state.write(fragment_name)?;
705        directives.serialize_impl(state)
706    }
707}
708
709impl InlineFragment {
710    pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
711        let Self {
712            type_condition,
713            directives,
714            selection_set,
715        } = self;
716        if let Some(type_name) = type_condition {
717            state.write("... on ")?;
718            state.write(type_name)?;
719        } else {
720            state.write("...")?;
721        }
722        directives.serialize_impl(state)?;
723        state.write(" ")?;
724        curly_brackets_space_separated(state, selection_set, |state, sel| sel.serialize_impl(state))
725    }
726}
727
728impl Value {
729    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
730        match self {
731            Value::Null => state.write("null"),
732            Value::Boolean(true) => state.write("true"),
733            Value::Boolean(false) => state.write("false"),
734            Value::Enum(name) => state.write(name),
735            Value::String(value) => {
736                let is_description = false;
737                serialize_string_value(state, is_description, value)
738            }
739            Value::Variable(name) => display!(state, "${}", name),
740            Value::Float(value) => display!(state, value),
741            Value::Int(value) => display!(state, value),
742            Value::List(value) => comma_separated(state, "[", "]", value, |state, value| {
743                value.serialize_impl(state)
744            }),
745            Value::Object(value) => {
746                comma_separated(state, "{", "}", value, |state, (name, value)| {
747                    state.write(name)?;
748                    state.write(": ")?;
749                    value.serialize_impl(state)
750                })
751            }
752        }
753    }
754}
755
756impl Argument {
757    fn serialize_impl(&self, state: &mut State) -> fmt::Result {
758        state.write(&self.name)?;
759        state.write(": ")?;
760        self.value.serialize_impl(state)
761    }
762}
763
764fn serialize_arguments(state: &mut State, arguments: &[Node<Argument>]) -> fmt::Result {
765    if !arguments.is_empty() {
766        state.on_single_line(|state| {
767            comma_separated(state, "(", ")", arguments, |state, argument| {
768                argument.serialize_impl(state)
769            })
770        })?
771    }
772    Ok(())
773}
774
775/// Example output: `[a, b, c]` or
776///
777/// ```text
778/// [
779///     a,
780///     b,
781///     c,
782/// ]
783/// ```
784fn comma_separated<T>(
785    state: &mut State,
786    open: &str,
787    close: &str,
788    values: &[T],
789    serialize_one: impl Fn(&mut State, &T) -> fmt::Result,
790) -> fmt::Result {
791    state.write(open)?;
792    if let Some((first, rest)) = values.split_first() {
793        state.indent()?;
794        serialize_one(state, first)?;
795        for value in rest {
796            state.write(",")?;
797            state.new_line_or_space()?;
798            serialize_one(state, value)?;
799        }
800        // Trailing comma
801        if state.newlines_enabled() {
802            state.write(",")?;
803        }
804        state.dedent()?;
805    }
806    state.write(close)
807}
808
809/// Example output: `{ a b c }` or
810///
811/// ```text
812/// {
813///     a
814///     b
815///     c
816/// }
817/// ```
818pub(crate) fn curly_brackets_space_separated<T>(
819    state: &mut State,
820    values: &[T],
821    serialize_one: impl Fn(&mut State, &T) -> fmt::Result,
822) -> fmt::Result {
823    state.write("{")?;
824    if let Some((first, rest)) = values.split_first() {
825        state.indent_or_space()?;
826        serialize_one(state, first)?;
827        for value in rest {
828            state.new_line_or_space()?;
829            serialize_one(state, value)?;
830        }
831        state.dedent_or_space()?;
832    }
833    state.write("}")
834}
835
836fn serialize_string_value(state: &mut State, is_description: bool, mut str: &str) -> fmt::Result {
837    let contains_newline = str.contains('\n');
838    let prefer_block_string = is_description || contains_newline;
839    if state.newlines_enabled() && prefer_block_string && can_be_block_string(str) {
840        return serialize_block_string(state, contains_newline, str);
841    }
842    state.write("\"")?;
843    loop {
844        if let Some(i) = str.find(|c| (c < ' ' && c != '\t') || c == '"' || c == '\\') {
845            let (without_escaping, rest) = str.split_at(i);
846            state.write(without_escaping)?;
847            // All characters that need escaping are in the ASCII range,
848            // and so take a single byte in UTF-8.
849            match rest.as_bytes()[0] {
850                b'\x08' => state.write("\\b")?,
851                b'\n' => state.write("\\n")?,
852                b'\x0C' => state.write("\\f")?,
853                b'\r' => state.write("\\r")?,
854                b'"' => state.write("\\\"")?,
855                b'\\' => state.write("\\\\")?,
856                byte => display!(state, "\\u{:04X}", byte)?,
857            }
858            str = &rest[1..]
859        } else {
860            state.write(str)?;
861            break;
862        }
863    }
864    state.write("\"")
865}
866
867fn serialize_block_string(state: &mut State, contains_newline: bool, str: &str) -> fmt::Result {
868    const TRIPLE_QUOTE: &str = "\"\"\"";
869    const ESCAPED_TRIPLE_QUOTE: &str = "\\\"\"\"";
870    const _: () = assert!(TRIPLE_QUOTE.len() == 3);
871    const _: () = assert!(ESCAPED_TRIPLE_QUOTE.len() == 4);
872
873    fn serialize_line(state: &mut State, mut line: &str) -> Result<(), fmt::Error> {
874        while let Some((before, after)) = line.split_once(TRIPLE_QUOTE) {
875            state.write(before)?;
876            state.write(ESCAPED_TRIPLE_QUOTE)?;
877            line = after;
878        }
879        state.write(line)
880    }
881
882    let multi_line =
883        contains_newline || str.len() > 70 || str.ends_with('"') || str.ends_with('\\');
884
885    state.write(TRIPLE_QUOTE)?;
886    if !multi_line {
887        // """example""""
888        serialize_line(state, str)?
889    } else {
890        // """
891        // example
892        // """
893
894        // `can_be_block_string` excludes \r, so the only remaining line terminator is \n
895        for line in str.split('\n') {
896            if line.is_empty() {
897                // Skip indentation which would be trailing whitespace
898                state.write("\n")?;
899            } else {
900                state.require_new_line()?;
901                serialize_line(state, line)?;
902            }
903        }
904        state.require_new_line()?;
905    }
906    state.write(TRIPLE_QUOTE)
907}
908
909/// Is it possible to create a serialization that, when fed through
910/// [BlockStringValue](https://spec.graphql.org/September2025/#BlockStringValue()),
911/// returns exactly `value`?
912fn can_be_block_string(value: &str) -> bool {
913    // `BlockStringValue` splits its inputs at any `LineTerminator` (\n, \r\n, or \r)
914    // and eventually joins lines but always with \n. So its output can never contain \r
915    if value.contains('\r') {
916        return false;
917    }
918
919    /// <https://spec.graphql.org/September2025/#Whitespace>
920    fn trim_start_graphql_whitespace(value: &str) -> &str {
921        value.trim_start_matches([' ', '\t'])
922    }
923
924    // With the above, \n is the only remaining LineTerminator
925    let mut lines = value.split('\n');
926    if lines
927        .next()
928        .is_some_and(|first| trim_start_graphql_whitespace(first).is_empty())
929        || lines
930            .next_back()
931            .is_some_and(|last| trim_start_graphql_whitespace(last).is_empty())
932    {
933        // Leading or trailing whitespace-only line would be trimmed by `BlockStringValue`
934        return false;
935    }
936
937    let common_indent = {
938        let lines = value.split('\n');
939        let each_line_indent_utf8_len = lines.filter_map(|line| {
940            let after_indent = trim_start_graphql_whitespace(line);
941            if !after_indent.is_empty() {
942                Some(line.len() - after_indent.len())
943            } else {
944                None // skip whitespace-only lines
945            }
946        });
947        each_line_indent_utf8_len.min().unwrap_or(0)
948    };
949    // If there is common indent `BlockStringValue` would remove it
950    // and incorrectly round-trip to a different value.
951    common_indent == 0
952}
953
954fn serialize_description(state: &mut State, description: &Option<Node<str>>) -> fmt::Result {
955    if let Some(description) = description {
956        let is_description = true;
957        serialize_string_value(state, is_description, description)?;
958        state.new_line_or_space()?;
959    }
960    Ok(())
961}
962
963impl fmt::Display for Type {
964    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
965        match self {
966            Type::Named(name) => std::write!(f, "{name}"),
967            Type::NonNullNamed(name) => std::write!(f, "{name}!"),
968            Type::List(inner) => std::write!(f, "[{inner}]"),
969            Type::NonNullList(inner) => std::write!(f, "[{inner}]!"),
970        }
971    }
972}
973
974impl fmt::Display for OperationType {
975    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
976        self.name().fmt(f)
977    }
978}
979
980impl fmt::Display for DirectiveLocation {
981    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
982        self.name().fmt(f)
983    }
984}
985
986macro_rules! impl_display {
987    ($($ty: path)+) => {
988        $(
989            /// Serialize to GraphQL syntax with the default configuration
990            impl Display for $ty {
991                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
992                    self.serialize().fmt(f)
993                }
994            }
995
996            /// Serialize to GraphQL syntax
997            impl Display for Serialize<'_, $ty> {
998                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
999                    let mut state = State {
1000                        config: self.config.clone(),
1001                        indent_level: self.config.initial_indent_level,
1002                        output: f,
1003                        output_empty: true,
1004                    };
1005                    // Indent the first line.
1006                    // Subsequent lines will be indented when writing a line break.
1007                    if let Some(prefix) = state.config.indent_prefix {
1008                        for _ in 0..state.indent_level {
1009                            state.write(prefix)?;
1010                        }
1011                    }
1012                    self.node.serialize_impl(&mut state)
1013                }
1014            }
1015        )+
1016    }
1017}
1018
1019impl_display! {
1020    Document
1021    Definition
1022    OperationDefinition
1023    FragmentDefinition
1024    DirectiveDefinition
1025    SchemaDefinition
1026    ScalarTypeDefinition
1027    ObjectTypeDefinition
1028    InterfaceTypeDefinition
1029    UnionTypeDefinition
1030    EnumTypeDefinition
1031    InputObjectTypeDefinition
1032    SchemaExtension
1033    ScalarTypeExtension
1034    ObjectTypeExtension
1035    InterfaceTypeExtension
1036    UnionTypeExtension
1037    EnumTypeExtension
1038    InputObjectTypeExtension
1039    DirectiveList
1040    Directive
1041    VariableDefinition
1042    FieldDefinition
1043    InputValueDefinition
1044    EnumValueDefinition
1045    Selection
1046    Field
1047    FragmentSpread
1048    InlineFragment
1049    Value
1050    crate::Schema
1051    crate::ExecutableDocument
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}