bluejay_printer/definition/
schema_definition.rs1use crate::{
2 definition::{
3 directive_definition::DirectiveDefinitionPrinter,
4 enum_type_definition::EnumTypeDefinitionPrinter,
5 input_object_type_definition::InputObjectTypeDefinitionPrinter,
6 interface_type_definition::InterfaceTypeDefinitionPrinter,
7 object_type_definition::ObjectTypeDefinitionPrinter,
8 scalar_type_definition::ScalarTypeDefinitionPrinter,
9 union_type_definition::UnionTypeDefinitionPrinter,
10 },
11 directive::DirectivesPrinter,
12 string_value::BlockStringValuePrinter,
13};
14use bluejay_core::{
15 definition::{
16 DirectiveDefinition, ObjectTypeDefinition, SchemaDefinition, TypeDefinitionReference,
17 },
18 AsIter,
19};
20use std::fmt::{Display, Formatter, Result};
21
22pub struct SchemaDefinitionPrinter<'a, S: SchemaDefinition>(&'a S);
23
24impl<'a, S: SchemaDefinition> SchemaDefinitionPrinter<'a, S> {
25 pub fn new(schema_definition: &'a S) -> Self {
26 Self(schema_definition)
27 }
28
29 pub fn to_string(schema_definition: &'a S) -> String {
30 Self::new(schema_definition).to_string()
31 }
32
33 fn is_implicit(schema_definition: &S) -> bool {
34 schema_definition.description().is_none()
35 && schema_definition.query().name() == "Query"
36 && schema_definition
37 .mutation()
38 .map(|mutation| mutation.name() == "Mutation")
39 .unwrap_or(true)
40 && schema_definition
41 .subscription()
42 .map(|subscription| subscription.name() == "Subscription")
43 .unwrap_or(true)
44 && schema_definition
45 .directives()
46 .map(AsIter::is_empty)
47 .unwrap_or(true)
48 }
49
50 fn fmt_explicit_schema_definition(schema_definition: &S, f: &mut Formatter<'_>) -> Result {
51 if let Some(description) = schema_definition.description() {
52 write!(f, "{}", BlockStringValuePrinter::new(description, 0))?;
53 }
54
55 write!(f, "schema")?;
56
57 if let Some(directives) = schema_definition.directives() {
58 write!(f, "{}", DirectivesPrinter::new(directives))?;
59 }
60
61 writeln!(f, " {{\n query: {}", schema_definition.query().name())?;
62
63 if let Some(mutation) = schema_definition.mutation() {
64 writeln!(f, " mutation: {}", mutation.name())?;
65 }
66
67 if let Some(subscription) = schema_definition.subscription() {
68 writeln!(f, " subscription: {}", subscription.name())?;
69 }
70
71 writeln!(f, "}}")
72 }
73}
74
75impl<S: SchemaDefinition> Display for SchemaDefinitionPrinter<'_, S> {
76 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
77 let Self(schema_definition) = *self;
78 schema_definition
79 .directive_definitions()
80 .filter(|dd| !dd.is_builtin())
81 .enumerate()
82 .try_for_each(|(idx, dd)| {
83 if idx != 0 {
84 writeln!(f)?;
85 }
86 write!(f, "{}", DirectiveDefinitionPrinter::new(dd))
87 })?;
88
89 let had_directives_to_output = schema_definition
90 .directive_definitions()
91 .any(|dd| !dd.is_builtin());
92
93 schema_definition
94 .type_definitions()
95 .filter(|tdr| !tdr.is_builtin())
96 .enumerate()
97 .try_for_each(|(idx, tdr)| {
98 if had_directives_to_output || idx != 0 {
99 writeln!(f)?;
100 }
101 match tdr {
102 TypeDefinitionReference::BuiltinScalar(_) => Ok(()),
103 TypeDefinitionReference::CustomScalar(cstd) => {
104 write!(f, "{}", ScalarTypeDefinitionPrinter::new(cstd))
105 }
106 TypeDefinitionReference::Enum(etd) => {
107 write!(f, "{}", EnumTypeDefinitionPrinter::new(etd))
108 }
109 TypeDefinitionReference::InputObject(iotd) => {
110 write!(f, "{}", InputObjectTypeDefinitionPrinter::new(iotd))
111 }
112 TypeDefinitionReference::Interface(itd) => {
113 write!(f, "{}", InterfaceTypeDefinitionPrinter::new(itd))
114 }
115 TypeDefinitionReference::Object(otd) => {
116 write!(f, "{}", ObjectTypeDefinitionPrinter::new(otd))
117 }
118 TypeDefinitionReference::Union(utd) => {
119 write!(f, "{}", UnionTypeDefinitionPrinter::new(utd))
120 }
121 }
122 })?;
123
124 if Self::is_implicit(schema_definition) {
125 Ok(())
126 } else {
127 if had_directives_to_output
128 || schema_definition
129 .type_definitions()
130 .any(|td| !td.is_builtin())
131 {
132 writeln!(f)?;
133 }
134 Self::fmt_explicit_schema_definition(schema_definition, f)
135 }
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::SchemaDefinitionPrinter;
142 use bluejay_parser::ast::{
143 definition::{DefinitionDocument, SchemaDefinition},
144 Parse,
145 };
146
147 #[test]
148 fn test_schema_dump() {
149 insta::glob!("test_data/schema_definition/*.graphql", |path| {
150 let input = std::fs::read_to_string(path).unwrap();
151 let document: DefinitionDocument = DefinitionDocument::parse(input.as_str()).unwrap();
152 let schema_definition = SchemaDefinition::try_from(&document).unwrap();
153 similar_asserts::assert_eq!(
154 input,
155 SchemaDefinitionPrinter(&schema_definition).to_string()
156 );
157 });
158 }
159}