use super::*;
use std::fmt;
use std::fmt::Display;
#[derive(Debug, Clone)]
pub struct Serialize<'a, T> {
pub(crate) node: &'a T,
pub(crate) config: Config<'a>,
}
#[derive(Debug, Clone)]
pub(crate) struct Config<'a> {
ident_prefix: Option<&'a str>,
}
pub(crate) struct State<'config, 'fmt, 'fmt2> {
config: Config<'config>,
indent_level: usize,
output: &'fmt mut fmt::Formatter<'fmt2>,
output_empty: bool,
}
impl<'a, T> Serialize<'a, T> {
pub fn indent_prefix(mut self, prefix: &'a str) -> Self {
self.config.ident_prefix = Some(prefix);
self
}
pub fn no_indent(mut self) -> Self {
self.config.ident_prefix = None;
self
}
}
impl Default for Config<'_> {
fn default() -> Self {
Self {
ident_prefix: Some(" "),
}
}
}
macro_rules! display {
($state: expr, $e: expr) => {
fmt::Display::fmt(&$e, $state.output)
};
($state: expr, $($tt: tt)+) => {
display!($state, format_args!($($tt)+))
};
}
impl<'config, 'fmt, 'fmt2> State<'config, 'fmt, 'fmt2> {
pub(crate) fn write(&mut self, str: &str) -> fmt::Result {
self.output_empty = false;
self.output.write_str(str)
}
pub(crate) fn indent(&mut self) -> fmt::Result {
self.indent_level += 1;
self.new_line_common(false)
}
pub(crate) fn indent_or_space(&mut self) -> fmt::Result {
self.indent_level += 1;
self.new_line_common(true)
}
pub(crate) fn dedent(&mut self) -> fmt::Result {
self.indent_level -= 1; self.new_line_common(false)
}
pub(crate) fn dedent_or_space(&mut self) -> fmt::Result {
self.indent_level -= 1; self.new_line_common(true)
}
pub(crate) fn new_line_or_space(&mut self) -> fmt::Result {
self.new_line_common(true)
}
fn new_line_common(&mut self, space: bool) -> fmt::Result {
if let Some(prefix) = self.config.ident_prefix {
self.write("\n")?;
for _ in 0..self.indent_level {
self.write(prefix)?;
}
} else if space {
self.write(" ")?
}
Ok(())
}
pub(crate) fn newlines_enabled(&self) -> bool {
self.config.ident_prefix.is_some()
}
pub(crate) fn on_single_line<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
let indent_prefix = self.config.ident_prefix.take();
let result = f(self);
self.config.ident_prefix = indent_prefix;
result
}
}
impl Document {
pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
top_level(state, &self.definitions, |state, def| {
def.serialize_impl(state)
})
}
}
pub(crate) fn top_level<T>(
state: &mut State,
iter: impl IntoIterator<Item = T>,
serialize_one: impl Fn(&mut State, T) -> fmt::Result,
) -> fmt::Result {
let mut iter = iter.into_iter();
if let Some(first) = iter.next() {
serialize_one(state, first)?;
iter.try_for_each(|item| {
if state.newlines_enabled() {
state.write("\n")?;
}
state.new_line_or_space()?;
serialize_one(state, item)
})?;
if state.newlines_enabled() {
state.write("\n")?;
}
}
Ok(())
}
impl Definition {
pub(crate) fn serialize_impl(&self, state: &mut State) -> fmt::Result {
match self {
Definition::OperationDefinition(def) => def.serialize_impl(state),
Definition::FragmentDefinition(def) => def.serialize_impl(state),
Definition::DirectiveDefinition(def) => def.serialize_impl(state),
Definition::SchemaDefinition(def) => def.serialize_impl(state),
Definition::ScalarTypeDefinition(def) => def.serialize_impl(state),
Definition::ObjectTypeDefinition(def) => def.serialize_impl(state),
Definition::InterfaceTypeDefinition(def) => def.serialize_impl(state),
Definition::UnionTypeDefinition(def) => def.serialize_impl(state),
Definition::EnumTypeDefinition(def) => def.serialize_impl(state),
Definition::InputObjectTypeDefinition(def) => def.serialize_impl(state),
Definition::SchemaExtension(def) => def.serialize_impl(state),
Definition::ScalarTypeExtension(def) => def.serialize_impl(state),
Definition::ObjectTypeExtension(def) => def.serialize_impl(state),
Definition::InterfaceTypeExtension(def) => def.serialize_impl(state),
Definition::UnionTypeExtension(def) => def.serialize_impl(state),
Definition::EnumTypeExtension(def) => def.serialize_impl(state),
Definition::InputObjectTypeExtension(def) => def.serialize_impl(state),
}
}
}
impl OperationDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
operation_type,
name,
variables,
directives,
selection_set,
} = self;
let shorthand = state.output_empty
&& *operation_type == OperationType::Query
&& name.is_none()
&& variables.is_empty()
&& directives.is_empty();
if !shorthand {
state.write(operation_type.name())?;
if let Some(name) = &name {
state.write(" ")?;
state.write(name)?;
}
if !variables.is_empty() {
state.on_single_line(|state| {
comma_separated(state, "(", ")", variables, |state, var| {
var.serialize_impl(state)
})
})?
}
directives.serialize_impl(state)?;
state.write(" ")?;
}
curly_brackets_space_separated(state, selection_set, |state, sel| sel.serialize_impl(state))
}
}
impl FragmentDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
name,
type_condition,
directives,
selection_set,
} = self;
display!(state, "fragment {} on {}", name, type_condition)?;
directives.serialize_impl(state)?;
state.write(" ")?;
curly_brackets_space_separated(state, selection_set, |state, sel| sel.serialize_impl(state))
}
}
impl DirectiveDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
description,
name,
arguments,
repeatable,
locations,
} = self;
serialize_description(state, description)?;
state.write("directive @")?;
state.write(name)?;
serialize_arguments_definition(state, arguments)?;
if *repeatable {
state.write(" repeatable")?;
}
if let Some((first, rest)) = locations.split_first() {
state.write(" on ")?;
state.write(first.name())?;
for location in rest {
state.write(" | ")?;
state.write(location.name())?;
}
}
Ok(())
}
}
fn serialize_arguments_definition(
state: &mut State,
arguments: &[Node<InputValueDefinition>],
) -> fmt::Result {
if !arguments.is_empty() {
let serialize_arguments = |state: &mut State| {
comma_separated(state, "(", ")", arguments, |state, arg| {
arg.serialize_impl(state)
})
};
if arguments
.iter()
.any(|arg| arg.description.is_some() || !arg.directives.is_empty())
{
serialize_arguments(state)?
} else {
state.on_single_line(serialize_arguments)?
}
}
Ok(())
}
impl SchemaDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
description,
directives,
root_operations,
} = self;
serialize_description(state, description)?;
state.write("schema")?;
directives.serialize_impl(state)?;
state.write(" ")?;
curly_brackets_space_separated(state, root_operations, |state, op| {
let (operation_type, operation_name) = &**op;
display!(state, "{}: {}", operation_type, operation_name)
})
}
}
impl ScalarTypeDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
description,
name,
directives,
} = self;
serialize_description(state, description)?;
state.write("scalar ")?;
state.write(name)?;
directives.serialize_impl(state)
}
}
impl ObjectTypeDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
description,
name,
implements_interfaces,
directives,
fields,
} = self;
serialize_description(state, description)?;
state.write("type ")?;
serialize_object_type_like(state, name, implements_interfaces, directives, fields)
}
}
fn serialize_object_type_like(
state: &mut State,
name: &str,
implements_interfaces: &[Name],
directives: &Directives,
fields: &[Node<FieldDefinition>],
) -> Result<(), fmt::Error> {
state.write(name)?;
if let Some((first, rest)) = implements_interfaces.split_first() {
state.write(" implements ")?;
state.write(first)?;
for name in rest {
state.write(" & ")?;
state.write(name)?;
}
}
directives.serialize_impl(state)?;
if !fields.is_empty() {
state.write(" ")?;
curly_brackets_space_separated(state, fields, |state, field| field.serialize_impl(state))?;
}
Ok(())
}
impl InterfaceTypeDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
description,
name,
implements_interfaces,
directives,
fields,
} = self;
serialize_description(state, description)?;
state.write("interface ")?;
serialize_object_type_like(state, name, implements_interfaces, directives, fields)
}
}
impl UnionTypeDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
description,
name,
directives,
members,
} = self;
serialize_description(state, description)?;
state.write("union ")?;
serialize_union(state, name, directives, members)
}
}
fn serialize_union(
state: &mut State,
name: &str,
directives: &Directives,
members: &[Name],
) -> fmt::Result {
state.write(name)?;
directives.serialize_impl(state)?;
if let Some((first, rest)) = members.split_first() {
state.write(" = ")?;
state.write(first)?;
for member in rest {
state.write(" | ")?;
state.write(member)?;
}
}
Ok(())
}
impl EnumTypeDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
description,
name,
directives,
values,
} = self;
serialize_description(state, description)?;
state.write("enum ")?;
state.write(name)?;
directives.serialize_impl(state)?;
if !values.is_empty() {
state.write(" ")?;
curly_brackets_space_separated(state, values, |state, value| {
value.serialize_impl(state)
})?;
}
Ok(())
}
}
impl InputObjectTypeDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
description,
name,
directives,
fields,
} = self;
serialize_description(state, description)?;
state.write("input ")?;
state.write(name)?;
directives.serialize_impl(state)?;
if !fields.is_empty() {
state.write(" ")?;
curly_brackets_space_separated(state, fields, |state, f| f.serialize_impl(state))?;
}
Ok(())
}
}
impl SchemaExtension {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
directives,
root_operations,
} = self;
state.write("extend schema")?;
directives.serialize_impl(state)?;
if !root_operations.is_empty() {
state.write(" ")?;
curly_brackets_space_separated(state, root_operations, |state, op| {
let (operation_type, operation_name) = &**op;
display!(state, "{}: {}", operation_type, operation_name)
})?;
}
Ok(())
}
}
impl ScalarTypeExtension {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self { name, directives } = self;
state.write("extend scalar ")?;
state.write(name)?;
directives.serialize_impl(state)
}
}
impl ObjectTypeExtension {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
name,
implements_interfaces,
directives,
fields,
} = self;
state.write("extend type ")?;
serialize_object_type_like(state, name, implements_interfaces, directives, fields)
}
}
impl InterfaceTypeExtension {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
name,
implements_interfaces,
directives,
fields,
} = self;
state.write("extend interface ")?;
serialize_object_type_like(state, name, implements_interfaces, directives, fields)
}
}
impl UnionTypeExtension {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
name,
directives,
members,
} = self;
state.write("extend union ")?;
serialize_union(state, name, directives, members)
}
}
impl EnumTypeExtension {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
name,
directives,
values,
} = self;
state.write("extend enum ")?;
state.write(name)?;
directives.serialize_impl(state)?;
if !values.is_empty() {
state.write(" ")?;
curly_brackets_space_separated(state, values, |state, value| {
value.serialize_impl(state)
})?;
}
Ok(())
}
}
impl InputObjectTypeExtension {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
name,
directives,
fields,
} = self;
state.write("extend input ")?;
state.write(name)?;
directives.serialize_impl(state)?;
if !fields.is_empty() {
state.write(" ")?;
curly_brackets_space_separated(state, fields, |state, f| f.serialize_impl(state))?;
}
Ok(())
}
}
impl Directives {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
for dir in self {
state.write(" ")?;
dir.serialize_impl(state)?;
}
Ok(())
}
}
impl Directive {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self { name, arguments } = self;
state.write("@")?;
state.write(name)?;
serialize_arguments(state, arguments)
}
}
impl VariableDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
name,
ty,
default_value,
directives,
} = self;
state.write("$")?;
state.write(name)?;
state.write(": ")?;
display!(state, ty)?;
if let Some(value) = default_value {
state.write(" = ")?;
value.serialize_impl(state)?
}
directives.serialize_impl(state)
}
}
impl FieldDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
description,
name,
arguments,
ty,
directives,
} = self;
serialize_description(state, description)?;
state.write(name)?;
serialize_arguments_definition(state, arguments)?;
state.write(": ")?;
display!(state, ty)?;
directives.serialize_impl(state)
}
}
impl InputValueDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
description,
name,
ty,
default_value,
directives,
} = self;
serialize_description(state, description)?;
state.write(name)?;
state.write(": ")?;
display!(state, ty)?;
if let Some(value) = default_value {
state.write(" = ")?;
value.serialize_impl(state)?
}
directives.serialize_impl(state)
}
}
impl EnumValueDefinition {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
description,
value,
directives,
} = self;
serialize_description(state, description)?;
state.write(value)?;
directives.serialize_impl(state)
}
}
impl Selection {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
match self {
Selection::Field(x) => x.serialize_impl(state),
Selection::FragmentSpread(x) => x.serialize_impl(state),
Selection::InlineFragment(x) => x.serialize_impl(state),
}
}
}
impl Field {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
alias,
name,
arguments,
directives,
selection_set,
} = self;
if let Some(alias) = alias {
state.write(alias)?;
state.write(": ")?;
}
state.write(name)?;
serialize_arguments(state, arguments)?;
directives.serialize_impl(state)?;
if !selection_set.is_empty() {
state.write(" ")?;
curly_brackets_space_separated(state, selection_set, |state, sel| {
sel.serialize_impl(state)
})?
}
Ok(())
}
}
impl FragmentSpread {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
fragment_name,
directives,
} = self;
state.write("...")?;
state.write(fragment_name)?;
directives.serialize_impl(state)
}
}
impl InlineFragment {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
let Self {
type_condition,
directives,
selection_set,
} = self;
if let Some(type_name) = type_condition {
state.write("... on ")?;
state.write(type_name)?;
} else {
state.write("...")?;
}
directives.serialize_impl(state)?;
state.write(" ")?;
curly_brackets_space_separated(state, selection_set, |state, sel| sel.serialize_impl(state))
}
}
impl Value {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
match self {
Value::Null => state.write("null"),
Value::Boolean(true) => state.write("true"),
Value::Boolean(false) => state.write("false"),
Value::Enum(name) => state.write(name),
Value::String(value) => serialize_string_value(state, value),
Value::Variable(name) => display!(state, "${}", name),
Value::Float(value) => display!(state, value),
Value::Int(value) => display!(state, value),
Value::List(value) => comma_separated(state, "[", "]", value, |state, value| {
value.serialize_impl(state)
}),
Value::Object(value) => {
comma_separated(state, "{", "}", value, |state, (name, value)| {
state.write(name)?;
state.write(": ")?;
value.serialize_impl(state)
})
}
}
}
}
impl Argument {
fn serialize_impl(&self, state: &mut State) -> fmt::Result {
state.write(&self.name)?;
state.write(": ")?;
self.value.serialize_impl(state)
}
}
fn serialize_arguments(state: &mut State, arguments: &[Node<Argument>]) -> fmt::Result {
if !arguments.is_empty() {
state.on_single_line(|state| {
comma_separated(state, "(", ")", arguments, |state, argument| {
argument.serialize_impl(state)
})
})?
}
Ok(())
}
fn comma_separated<T>(
state: &mut State,
open: &str,
close: &str,
values: &[T],
serialize_one: impl Fn(&mut State, &T) -> fmt::Result,
) -> fmt::Result {
state.write(open)?;
if let Some((first, rest)) = values.split_first() {
state.indent()?;
serialize_one(state, first)?;
for value in rest {
state.write(",")?;
state.new_line_or_space()?;
serialize_one(state, value)?;
}
if state.newlines_enabled() {
state.write(",")?;
}
state.dedent()?;
}
state.write(close)
}
fn curly_brackets_space_separated<T>(
state: &mut State,
values: &[T],
serialize_one: impl Fn(&mut State, &T) -> fmt::Result,
) -> fmt::Result {
state.write("{")?;
if let Some((first, rest)) = values.split_first() {
state.indent_or_space()?;
serialize_one(state, first)?;
for value in rest {
state.new_line_or_space()?;
serialize_one(state, value)?;
}
state.dedent_or_space()?;
}
state.write("}")
}
fn serialize_string_value(state: &mut State, mut str: &str) -> fmt::Result {
state.write("\"")?;
loop {
if let Some(i) = str.find(|c| (c < ' ' && c != '\t') || c == '"' || c == '\\') {
let (without_escaping, rest) = str.split_at(i);
state.write(without_escaping)?;
match rest.as_bytes()[0] {
b'\x08' => state.write("\\b")?,
b'\n' => state.write("\\n")?,
b'\x0C' => state.write("\\f")?,
b'\r' => state.write("\\r")?,
b'"' => state.write("\\\"")?,
b'\\' => state.write("\\\\")?,
byte => display!(state, "\\u{:04X}", byte)?,
}
str = &rest[1..]
} else {
state.write(str)?;
break;
}
}
state.write("\"")
}
fn serialize_description(state: &mut State, description: &Option<NodeStr>) -> fmt::Result {
if let Some(description) = description {
serialize_string_value(state, description)?;
state.new_line_or_space()?;
}
Ok(())
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Type::Named(name) => std::write!(f, "{name}"),
Type::NonNullNamed(name) => std::write!(f, "{name}!"),
Type::List(inner) => std::write!(f, "[{inner}]"),
Type::NonNullList(inner) => std::write!(f, "[{inner}]!"),
}
}
}
impl fmt::Display for OperationType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.name().fmt(f)
}
}
impl fmt::Display for DirectiveLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.name().fmt(f)
}
}
macro_rules! impl_display {
($($ty: path)+) => {
$(
impl Display for $ty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.serialize().fmt(f)
}
}
impl Display for Serialize<'_, $ty> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.node.serialize_impl(&mut State {
config: self.config.clone(),
indent_level: 0,
output: f,
output_empty: true,
})
}
}
)+
}
}
impl_display! {
Document
Definition
OperationDefinition
FragmentDefinition
DirectiveDefinition
SchemaDefinition
ScalarTypeDefinition
ObjectTypeDefinition
InterfaceTypeDefinition
UnionTypeDefinition
EnumTypeDefinition
InputObjectTypeDefinition
SchemaExtension
ScalarTypeExtension
ObjectTypeExtension
InterfaceTypeExtension
UnionTypeExtension
EnumTypeExtension
InputObjectTypeExtension
Directives
Directive
VariableDefinition
FieldDefinition
InputValueDefinition
EnumValueDefinition
Selection
Field
FragmentSpread
InlineFragment
Value
crate::Schema
crate::ExecutableDocument
}