Skip to main content

docql/
renderer.rs

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}