1mod callbacks;
15mod gut;
16mod html;
17mod markdown;
18mod resolve;
19
20use crate::documentation::{Documentation, GdnativeClass, Method, Property};
21use pulldown_cmark::{
22 Alignment, CowStr, Event, HeadingLevel, LinkType, Options as MarkdownOptions, Parser, Tag,
23};
24
25pub(super) use gut::GutCallbacks;
26pub(super) use html::HtmlCallbacks;
27pub(super) use markdown::MarkdownCallbacks;
28
29pub use callbacks::Callbacks;
30pub use resolve::Resolver;
31
32macro_rules! broken_link_callback {
38 ($resolver:expr) => {
39 move |broken_link: ::pulldown_cmark::BrokenLink| {
40 use ::pulldown_cmark::CowStr;
41
42 let mut link: &str = &broken_link.reference;
43 if link.starts_with('`') && link.ends_with('`') && link.len() > 1 {
44 link = &link[1..link.len() - 1];
45 }
46 $resolver
47 .resolve(link)
48 .map(|string| (CowStr::from(string), CowStr::Borrowed("")))
49 }
50 };
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
59pub enum BuiltinBackend {
60 Markdown,
65 Html,
72 Gut,
103}
104
105#[derive(Debug)]
109pub struct Generator<'a> {
110 pub resolver: &'a Resolver,
112 pub documentation: &'a Documentation,
114 pub markdown_options: MarkdownOptions,
116 pub opening_comment: bool,
121}
122
123impl<'a> Generator<'a> {
124 pub(crate) fn new(
125 resolver: &'a Resolver,
126 documentation: &'a Documentation,
127 markdown_options: MarkdownOptions,
128 opening_comment: bool,
129 ) -> Self {
130 Self {
131 resolver,
132 documentation,
133 markdown_options,
134 opening_comment,
135 }
136 }
137
138 pub fn generate_root_file(&self, extension: &str, callbacks: &mut dyn Callbacks) -> String {
151 let resolver = self.resolver;
152 let mut broken_link_callback = broken_link_callback!(resolver);
153 let class_iterator = EventIterator {
154 context: resolver,
155 parser: pulldown_cmark::Parser::new_with_broken_link_callback(
156 &self.documentation.root_documentation,
157 self.markdown_options,
158 Some(&mut broken_link_callback),
159 ),
160 };
161 let mut events: Vec<_> = class_iterator.into_iter().collect();
162 events.extend(vec![
163 Event::Start(Tag::Heading(HeadingLevel::H1, None, Vec::new())),
164 Event::Text(CowStr::Borrowed("Classes:")),
165 Event::End(Tag::Heading(HeadingLevel::H1, None, Vec::new())),
166 Event::Start(Tag::List(None)),
167 ]);
168 for class_name in self.documentation.classes.keys() {
169 let link = Tag::Link(
170 LinkType::Inline,
171 format!("./{}.{}", class_name, extension).into(),
172 CowStr::Borrowed(""),
173 );
174 events.extend(vec![
175 Event::Start(Tag::Item),
176 Event::Start(link.clone()),
177 Event::Text(CowStr::Borrowed(class_name)),
178 Event::End(link.clone()),
179 Event::End(Tag::Item),
180 ])
181 }
182 events.push(Event::End(Tag::List(None)));
183 let mut root_file = String::new();
184 callbacks.encode(&mut root_file, events);
185 root_file
186 }
187
188 pub fn generate_file(
219 &self,
220 name: &str,
221 class: &GdnativeClass,
222 callbacks: &mut dyn Callbacks,
223 ) -> String {
224 let mut class_file = String::new();
225 let resolver = &self.resolver;
226
227 let inherit_link = resolver.resolve(&class.inherit);
228
229 let mut events = vec![
231 Event::Start(Tag::Heading(HeadingLevel::H1, None, Vec::new())),
232 Event::Text(CowStr::Borrowed(name)),
233 Event::End(Tag::Heading(HeadingLevel::H1, None, Vec::new())),
234 Event::Start(Tag::Paragraph),
235 Event::Start(Tag::Strong),
236 Event::Text(CowStr::Borrowed("Inherit:")),
237 Event::End(Tag::Strong),
238 Event::Text(CowStr::Borrowed(" ")),
239 ];
240 if let Some(inherit_link) = inherit_link.as_ref() {
241 let link = Tag::Link(
242 LinkType::Shortcut,
243 CowStr::Borrowed(inherit_link),
244 CowStr::Borrowed(""),
245 );
246 events.extend(vec![
247 Event::Start(link.clone()),
248 Event::Text(CowStr::Borrowed(&class.inherit)),
249 Event::End(link),
250 ])
251 } else {
252 events.push(Event::Text(CowStr::Borrowed(&class.inherit)))
253 }
254 events.extend(vec![
255 Event::End(Tag::Paragraph),
256 Event::Start(Tag::Heading(HeadingLevel::H2, None, Vec::new())),
257 Event::Text(CowStr::Borrowed("Description")),
258 Event::End(Tag::Heading(HeadingLevel::H2, None, Vec::new())),
259 ]);
260 callbacks.encode(&mut class_file, events);
261
262 let mut broken_link_callback = broken_link_callback!(resolver);
264 let class_documentation = EventIterator {
265 context: resolver,
266 parser: pulldown_cmark::Parser::new_with_broken_link_callback(
267 &class.documentation,
268 self.markdown_options,
269 Some(&mut broken_link_callback),
270 ),
271 }
272 .into_iter()
273 .collect();
274 callbacks.encode(&mut class_file, class_documentation);
275
276 if !class.properties.is_empty() {
278 callbacks.encode(
279 &mut class_file,
280 Self::properties_table(&class.properties, resolver),
281 )
282 }
283
284 callbacks.encode(
286 &mut class_file,
287 Self::methods_table(&class.methods, resolver),
288 );
289
290 if !class.properties.is_empty() {
292 callbacks.encode(
293 &mut class_file,
294 vec![
295 Event::Start(Tag::Heading(HeadingLevel::H2, None, Vec::new())),
296 Event::Text(CowStr::Borrowed("Properties Descriptions")),
297 Event::End(Tag::Heading(HeadingLevel::H2, None, Vec::new())),
298 ],
299 );
300 for property in &class.properties {
301 callbacks.start_property(&mut class_file, resolver, property);
302 let mut broken_link_callback = broken_link_callback!(resolver);
303 let property_documentation = EventIterator {
304 context: resolver,
305 parser: pulldown_cmark::Parser::new_with_broken_link_callback(
306 &property.documentation,
307 self.markdown_options,
308 Some(&mut broken_link_callback),
309 ),
310 }
311 .into_iter()
312 .collect();
313 callbacks.encode(&mut class_file, property_documentation);
314 }
315 }
316
317 callbacks.encode(
319 &mut class_file,
320 vec![
321 Event::Start(Tag::Heading(HeadingLevel::H2, None, Vec::new())),
322 Event::Text(CowStr::Borrowed("Methods Descriptions")),
323 Event::End(Tag::Heading(HeadingLevel::H2, None, Vec::new())),
324 ],
325 );
326 for method in &class.methods {
327 callbacks.start_method(&mut class_file, resolver, method);
328 let mut broken_link_callback = broken_link_callback!(resolver);
329 let method_documentation = EventIterator {
330 context: resolver,
331 parser: pulldown_cmark::Parser::new_with_broken_link_callback(
332 &method.documentation,
333 self.markdown_options,
334 Some(&mut broken_link_callback),
335 ),
336 }
337 .into_iter()
338 .collect();
339 callbacks.encode(&mut class_file, method_documentation);
340 }
341 class_file
342 }
343
344 fn properties_table<'ev>(
346 properties: &'ev [Property],
347 resolver: &'ev Resolver,
348 ) -> Vec<Event<'ev>> {
349 let mut events = vec![
350 Event::Start(Tag::Heading(HeadingLevel::H2, None, Vec::new())),
351 Event::Text(CowStr::Borrowed("Properties")),
352 Event::End(Tag::Heading(HeadingLevel::H2, None, Vec::new())),
353 Event::Start(Tag::Table(vec![Alignment::Left, Alignment::Left])),
354 Event::Start(Tag::TableHead),
355 Event::Start(Tag::TableCell),
356 Event::Text(CowStr::Borrowed("type")),
357 Event::End(Tag::TableCell),
358 Event::Start(Tag::TableCell),
359 Event::Text(CowStr::Borrowed("property")),
360 Event::End(Tag::TableCell),
361 Event::End(Tag::TableHead),
362 ];
363
364 for property in properties {
365 let link = Tag::Link(
366 LinkType::Reference,
367 format!("#property-{}", property.name).into(),
368 property.name.as_str().into(),
369 );
370 events.push(Event::Start(Tag::TableRow));
371 events.push(Event::Start(Tag::TableCell));
372 events.extend(resolver.encode_type(&property.typ));
373 events.extend(vec![
374 Event::End(Tag::TableCell),
375 Event::Start(Tag::TableCell),
376 Event::Start(link.clone()),
377 Event::Text(CowStr::Borrowed(property.name.as_str())),
378 Event::End(link),
379 Event::End(Tag::TableCell),
380 Event::End(Tag::TableRow),
381 ]);
382 }
383
384 events.push(Event::End(Tag::Table(vec![
385 Alignment::Left,
386 Alignment::Left,
387 ])));
388
389 events
390 }
391
392 fn methods_table<'ev>(methods: &'ev [Method], resolver: &'ev Resolver) -> Vec<Event<'ev>> {
394 let mut events = vec![
395 Event::Start(Tag::Heading(HeadingLevel::H2, None, Vec::new())),
396 Event::Text(CowStr::Borrowed("Methods")),
397 Event::End(Tag::Heading(HeadingLevel::H2, None, Vec::new())),
398 Event::Start(Tag::Table(vec![Alignment::Left, Alignment::Left])),
399 Event::Start(Tag::TableHead),
400 Event::Start(Tag::TableCell),
401 Event::Text(CowStr::Borrowed("returns")),
402 Event::End(Tag::TableCell),
403 Event::Start(Tag::TableCell),
404 Event::Text(CowStr::Borrowed("method")),
405 Event::End(Tag::TableCell),
406 Event::End(Tag::TableHead),
407 ];
408
409 for method in methods {
410 let link = format!("#func-{}", method.name);
411 events.push(Event::Start(Tag::TableRow));
412 events.push(Event::Start(Tag::TableCell));
413 events.extend(resolver.encode_type(&method.return_type));
414 events.push(Event::End(Tag::TableCell));
415 events.push(Event::Start(Tag::TableCell));
416
417 let link = Tag::Link(
418 LinkType::Reference,
419 link.into(),
420 method.name.as_str().into(),
421 );
422 events.extend(vec![
423 Event::Start(link.clone()),
424 Event::Text(CowStr::Borrowed(&method.name)),
425 Event::End(link),
426 Event::Text(CowStr::Borrowed("( ")),
427 ]);
428 for (index, (name, typ, _)) in method.parameters.iter().enumerate() {
429 events.push(Event::Text(format!("{}: ", name).into()));
430 events.extend(resolver.encode_type(typ));
431 if index + 1 != method.parameters.len() {
432 events.push(Event::Text(CowStr::Borrowed(", ")));
433 }
434 }
435
436 events.extend(vec![
437 Event::Text(CowStr::Borrowed(" )")),
438 Event::End(Tag::TableCell),
439 Event::End(Tag::TableRow),
440 ]);
441 }
442
443 events.push(Event::End(Tag::Table(vec![
444 Alignment::Left,
445 Alignment::Left,
446 ])));
447
448 events
449 }
450}
451
452struct EventIterator<'resolver, 'input, 'cb> {
455 context: &'resolver Resolver,
456 parser: Parser<'input, 'cb>,
457}
458
459impl<'resolver, 'input, 'cb> Iterator for EventIterator<'resolver, 'input, 'cb> {
460 type Item = Event<'input>;
461
462 fn next(&mut self) -> Option<Self::Item> {
463 let mut next_event = self.parser.next()?;
464 next_event = match next_event {
465 Event::Start(Tag::Link(LinkType::ShortcutUnknown, dest, title)) => {
468 Event::Start(Tag::Link(LinkType::Shortcut, dest, title))
469 }
470 Event::End(Tag::Link(LinkType::ShortcutUnknown, dest, title)) => {
471 Event::End(Tag::Link(LinkType::Shortcut, dest, title))
472 }
473 _ => next_event,
474 };
475 self.context.resolve_event(&mut next_event);
476 Some(next_event)
477 }
478}