1use super::{handlebars_helpers, schema, Result};
2use chrono::NaiveDate;
3use serde::Serialize;
4
5pub struct Renderer<'a> {
6 schema_name: String,
7 date: NaiveDate,
8 schema: &'a schema::Schema,
9 handlebars: handlebars::Handlebars<'a>,
10}
11
12impl<'a> Renderer<'a> {
13 pub fn new(schema_name: String, date: NaiveDate, schema: &'a schema::Schema) -> Result<Self> {
14 let mut handlebars = handlebars::Handlebars::new();
15 handlebars.set_strict_mode(true);
16 handlebars
17 .register_template_string("layout", include_str!("templates/layout.handlebars"))?;
18 handlebars.register_template_string("index", include_str!("templates/index.handlebars"))?;
19 handlebars
20 .register_template_string("object", include_str!("templates/object.handlebars"))?;
21 handlebars.register_template_string(
22 "input_object",
23 include_str!("templates/input_object.handlebars"),
24 )?;
25 handlebars
26 .register_template_string("scalar", include_str!("templates/scalar.handlebars"))?;
27 handlebars.register_template_string("enum", include_str!("templates/enum.handlebars"))?;
28 handlebars.register_template_string(
29 "interface",
30 include_str!("templates/interface.handlebars"),
31 )?;
32 handlebars.register_template_string("union", include_str!("templates/union.handlebars"))?;
33
34 handlebars.register_template_string(
35 "fields",
36 include_str!("templates/partials/fields.handlebars"),
37 )?;
38 handlebars.register_template_string(
39 "possible_types",
40 include_str!("templates/partials/possible_types.handlebars"),
41 )?;
42 handlebars
43 .register_template_string("uses", include_str!("templates/partials/uses.handlebars"))?;
44
45 handlebars.register_helper(
46 "t",
47 Box::new(handlebars_helpers::TypeRefRenderer::new(&schema)),
48 );
49 handlebars.register_helper(
50 "docblock",
51 Box::new(handlebars_helpers::Docblock::default()),
52 );
53 handlebars.register_helper("kind", Box::new(handlebars_helpers::Kind::default()));
54
55 Ok(Self {
56 schema_name,
57 date,
58 schema,
59 handlebars,
60 })
61 }
62}
63
64impl Renderer<'_> {
65 pub fn render_index(&self) -> Result<String> {
66 self.render(
67 "index",
68 &self.schema_name,
69 &IndexContext::new(&self.schema_name, self.schema),
70 )
71 }
72
73 pub fn render_object(&self, object: &schema::FullType) -> Result<String> {
74 self.render(
75 "object",
76 &object.name,
77 &ObjectContext::new(&self.schema_name, object, self.schema.find_uses(object)),
78 )
79 }
80
81 pub fn render_input_object(&self, input_object: &schema::FullType) -> Result<String> {
82 self.render(
83 "input_object",
84 &input_object.name,
85 &InputObjectContext::new(
86 &self.schema_name,
87 input_object,
88 self.schema.find_uses(input_object),
89 ),
90 )
91 }
92
93 pub fn render_scalar(&self, scalar: &schema::FullType) -> Result<String> {
94 self.render(
95 "scalar",
96 &scalar.name,
97 &ScalarContext::new(&self.schema_name, scalar, self.schema.find_uses(scalar)),
98 )
99 }
100
101 pub fn render_enum(&self, enum_type: &schema::FullType) -> Result<String> {
102 self.render(
103 "enum",
104 &enum_type.name,
105 &EnumContext::new(
106 &self.schema_name,
107 enum_type,
108 self.schema.find_uses(enum_type),
109 ),
110 )
111 }
112
113 pub fn render_interface(&self, interface: &schema::FullType) -> Result<String> {
114 self.render(
115 "interface",
116 &interface.name,
117 &InterfaceContext::new(
118 &self.schema_name,
119 interface,
120 self.schema.find_uses(interface),
121 ),
122 )
123 }
124
125 pub fn render_union(&self, union: &schema::FullType) -> Result<String> {
126 self.render(
127 "union",
128 &union.name,
129 &UnionContext::new(&self.schema_name, union, self.schema.find_uses(union)),
130 )
131 }
132
133 #[inline]
134 fn render<T>(&self, template: &str, title: &str, t: &T) -> Result<String>
135 where
136 T: Serialize,
137 {
138 let rendered = self.handlebars.render(template, &t)?;
139 let html = self.handlebars.render(
140 "layout",
141 &LayoutContext {
142 title: title,
143 content: &rendered,
144 date_iso: self.date.format("%Y-%m-%d").to_string(),
145 date_human: self.date.format("%-e %b %Y").to_string(),
146 },
147 )?;
148 Ok(html)
149 }
150}
151
152#[derive(Debug, Serialize)]
153#[serde(rename_all = "camelCase")]
154struct LayoutContext<'a> {
155 title: &'a str,
156 content: &'a str,
157 date_iso: String,
158 date_human: String,
159}
160
161#[derive(Debug, Serialize)]
162#[serde(rename_all = "camelCase")]
163struct IndexContext<'a> {
164 schema_name: &'a str,
165 query_type: Option<&'a str>,
166 mutation_type: Option<&'a str>,
167}
168
169impl<'a> IndexContext<'a> {
170 fn new(schema_name: &'a str, schema: &'a schema::Schema) -> Self {
171 Self {
172 schema_name,
173 query_type: schema.query_type.as_ref().map(|t| t.name.as_str()),
174 mutation_type: schema.mutation_type.as_ref().map(|t| t.name.as_str()),
175 }
176 }
177}
178
179#[derive(Debug, Serialize)]
180#[serde(rename_all = "camelCase")]
181struct ObjectContext<'a> {
182 schema_name: &'a str,
183 object: &'a schema::FullType,
184 uses: Vec<schema::TypeUse<'a>>,
185}
186
187impl<'a> ObjectContext<'a> {
188 fn new(
189 schema_name: &'a str,
190 object: &'a schema::FullType,
191 uses: Vec<schema::TypeUse<'a>>,
192 ) -> Self {
193 Self {
194 schema_name,
195 object,
196 uses,
197 }
198 }
199}
200
201#[derive(Debug, Serialize)]
202#[serde(rename_all = "camelCase")]
203struct InputObjectContext<'a> {
204 schema_name: &'a str,
205 input_object: &'a schema::FullType,
206 uses: Vec<schema::TypeUse<'a>>,
207}
208
209impl<'a> InputObjectContext<'a> {
210 fn new(
211 schema_name: &'a str,
212 input_object: &'a schema::FullType,
213 uses: Vec<schema::TypeUse<'a>>,
214 ) -> Self {
215 Self {
216 schema_name,
217 input_object,
218 uses,
219 }
220 }
221}
222
223#[derive(Debug, Serialize)]
224#[serde(rename_all = "camelCase")]
225struct ScalarContext<'a> {
226 schema_name: &'a str,
227 scalar: &'a schema::FullType,
228 uses: Vec<schema::TypeUse<'a>>,
229}
230
231impl<'a> ScalarContext<'a> {
232 fn new(
233 schema_name: &'a str,
234 scalar: &'a schema::FullType,
235 uses: Vec<schema::TypeUse<'a>>,
236 ) -> Self {
237 Self {
238 schema_name,
239 scalar,
240 uses,
241 }
242 }
243}
244
245#[derive(Debug, Serialize)]
246#[serde(rename_all = "camelCase")]
247struct EnumContext<'a> {
248 schema_name: &'a str,
249 #[serde(rename = "enum")]
250 enum_type: &'a schema::FullType,
251 uses: Vec<schema::TypeUse<'a>>,
252}
253
254impl<'a> EnumContext<'a> {
255 fn new(
256 schema_name: &'a str,
257 enum_type: &'a schema::FullType,
258 uses: Vec<schema::TypeUse<'a>>,
259 ) -> Self {
260 Self {
261 schema_name,
262 enum_type,
263 uses,
264 }
265 }
266}
267
268#[derive(Debug, Serialize)]
269#[serde(rename_all = "camelCase")]
270struct InterfaceContext<'a> {
271 schema_name: &'a str,
272 interface: &'a schema::FullType,
273 uses: Vec<schema::TypeUse<'a>>,
274}
275
276impl<'a> InterfaceContext<'a> {
277 fn new(
278 schema_name: &'a str,
279 interface: &'a schema::FullType,
280 uses: Vec<schema::TypeUse<'a>>,
281 ) -> Self {
282 Self {
283 schema_name,
284 interface,
285 uses,
286 }
287 }
288}
289
290#[derive(Debug, Serialize)]
291#[serde(rename_all = "camelCase")]
292struct UnionContext<'a> {
293 schema_name: &'a str,
294 union: &'a schema::FullType,
295 uses: Vec<schema::TypeUse<'a>>,
296}
297
298impl<'a> UnionContext<'a> {
299 fn new(
300 schema_name: &'a str,
301 union: &'a schema::FullType,
302 uses: Vec<schema::TypeUse<'a>>,
303 ) -> Self {
304 Self {
305 schema_name,
306 union,
307 uses,
308 }
309 }
310}