mod callbacks;
mod gut;
mod html;
mod markdown;
mod resolve;
use crate::documentation::{Method, Property};
use pulldown_cmark::{Alignment, CowStr, Event, LinkType, Options as MarkdownOptions, Parser, Tag};
pub(super) use gut::GutCallbacks;
pub(super) use html::HtmlCallbacks;
pub(super) use markdown::MarkdownCallbacks;
pub use crate::documentation::Documentation;
pub use callbacks::Callbacks;
pub use resolve::Resolver;
macro_rules! broken_link_callback {
($resolver:expr) => {
move |broken_link: ::pulldown_cmark::BrokenLink| {
use ::pulldown_cmark::CowStr;
let mut link = broken_link.reference;
if link.starts_with('`') && link.ends_with('`') && link.len() > 1 {
link = &link[1..link.len() - 1];
}
$resolver
.resolve(link)
.map(|string| (CowStr::from(string), CowStr::Borrowed("")))
}
};
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BuiltinBackend {
Markdown,
Html,
Gut,
}
#[derive(Debug)]
pub struct Generator<'a> {
pub resolver: &'a Resolver,
pub documentation: &'a Documentation,
pub markdown_options: MarkdownOptions,
}
impl<'a> Generator<'a> {
pub(crate) fn new(
resolver: &'a Resolver,
documentation: &'a Documentation,
markdown_options: MarkdownOptions,
) -> Self {
Self {
resolver,
documentation,
markdown_options,
}
}
pub fn generate_root_file(&mut self, extension: &str, callbacks: &mut dyn Callbacks) -> String {
let mut root_file = String::new();
let resolver = self.resolver;
let mut broken_link_callback = broken_link_callback!(resolver);
let class_iterator = EventIterator {
context: resolver,
parser: pulldown_cmark::Parser::new_with_broken_link_callback(
&self.documentation.root_documentation,
self.markdown_options,
Some(&mut broken_link_callback),
),
};
let mut events: Vec<_> = class_iterator.into_iter().collect();
events.extend(vec![
Event::Start(Tag::Heading(1)),
Event::Text(CowStr::Borrowed("Classes:")),
Event::End(Tag::Heading(1)),
Event::Start(Tag::List(None)),
]);
for class_name in self.documentation.classes.keys() {
let link = Tag::Link(
LinkType::Inline,
format!("./{}.{}", class_name, extension).into(),
CowStr::Borrowed(""),
);
events.extend(vec![
Event::Start(Tag::Item),
Event::Start(link.clone()),
Event::Text(CowStr::Borrowed(&class_name)),
Event::End(link.clone()),
Event::End(Tag::Item),
])
}
events.push(Event::End(Tag::List(None)));
callbacks.encode(&mut root_file, events);
root_file
}
pub fn generate_files(&mut self, callbacks: &mut dyn Callbacks) -> Vec<(String, String)> {
let mut results = Vec::new();
for (name, class) in &self.documentation.classes {
let mut class_file = String::new();
let resolver = &self.resolver;
let inherit_link = resolver.resolve(&class.inherit);
let mut events = vec![
Event::Start(Tag::Heading(1)),
Event::Text(CowStr::Borrowed(&name)),
Event::End(Tag::Heading(1)),
Event::Start(Tag::Paragraph),
Event::Start(Tag::Strong),
Event::Text(CowStr::Borrowed("Inherit:")),
Event::End(Tag::Strong),
Event::Text(CowStr::Borrowed(" ")),
];
if let Some(inherit_link) = inherit_link.as_ref() {
events.extend(vec![
Event::Start(Tag::Link(
LinkType::Shortcut,
CowStr::Borrowed(&inherit_link),
CowStr::Borrowed(""),
)),
Event::Text(CowStr::Borrowed(&class.inherit)),
Event::End(Tag::Link(
LinkType::Shortcut,
CowStr::Borrowed(&inherit_link),
CowStr::Borrowed(""),
)),
])
} else {
events.push(Event::Text(CowStr::Borrowed(&class.inherit)))
}
events.extend(vec![
Event::End(Tag::Paragraph),
Event::Start(Tag::Heading(2)),
Event::Text(CowStr::Borrowed("Description")),
Event::End(Tag::Heading(2)),
]);
callbacks.encode(&mut class_file, events);
let mut broken_link_callback = broken_link_callback!(resolver);
let class_documentation = EventIterator {
context: resolver,
parser: pulldown_cmark::Parser::new_with_broken_link_callback(
&class.documentation,
self.markdown_options,
Some(&mut broken_link_callback),
),
}
.into_iter()
.collect();
callbacks.encode(&mut class_file, class_documentation);
if !class.properties.is_empty() {
callbacks.encode(
&mut class_file,
Self::properties_table(&class.properties, resolver),
)
}
callbacks.encode(
&mut class_file,
Self::methods_table(&class.methods, resolver),
);
if !class.properties.is_empty() {
callbacks.encode(
&mut class_file,
vec![
Event::Start(Tag::Heading(2)),
Event::Text(CowStr::Borrowed("Properties Descriptions")),
Event::End(Tag::Heading(2)),
],
);
for property in &class.properties {
callbacks.start_property(&mut class_file, resolver, property);
let mut broken_link_callback = broken_link_callback!(resolver);
let property_documentation = EventIterator {
context: resolver,
parser: pulldown_cmark::Parser::new_with_broken_link_callback(
&property.documentation,
self.markdown_options,
Some(&mut broken_link_callback),
),
}
.into_iter()
.collect();
callbacks.encode(&mut class_file, property_documentation);
}
}
callbacks.encode(
&mut class_file,
vec![
Event::Start(Tag::Heading(2)),
Event::Text(CowStr::Borrowed("Methods Descriptions")),
Event::End(Tag::Heading(2)),
],
);
for method in &class.methods {
callbacks.start_method(&mut class_file, resolver, method);
let mut broken_link_callback = broken_link_callback!(resolver);
let method_documentation = EventIterator {
context: resolver,
parser: pulldown_cmark::Parser::new_with_broken_link_callback(
&method.documentation,
self.markdown_options,
Some(&mut broken_link_callback),
),
}
.into_iter()
.collect();
callbacks.encode(&mut class_file, method_documentation);
}
results.push((name.clone(), class_file))
}
results
}
fn properties_table<'ev>(
properties: &'ev [Property],
resolver: &'ev Resolver,
) -> Vec<Event<'ev>> {
let mut events = vec![
Event::Start(Tag::Heading(2)),
Event::Text(CowStr::Borrowed("Properties")),
Event::End(Tag::Heading(2)),
Event::Start(Tag::Table(vec![Alignment::Left, Alignment::Left])),
Event::Start(Tag::TableHead),
Event::Start(Tag::TableCell),
Event::Text(CowStr::Borrowed("type")),
Event::End(Tag::TableCell),
Event::Start(Tag::TableCell),
Event::Text(CowStr::Borrowed("property")),
Event::End(Tag::TableCell),
Event::End(Tag::TableHead),
];
for property in properties {
let link = Tag::Link(
LinkType::Reference,
format!("#property-{}", property.name).into(),
property.name.as_str().into(),
);
events.push(Event::Start(Tag::TableRow));
events.push(Event::Start(Tag::TableCell));
events.extend(resolver.encode_type(&property.typ));
events.extend(vec![
Event::End(Tag::TableCell),
Event::Start(Tag::TableCell),
Event::Start(link.clone()),
Event::Text(CowStr::Borrowed(property.name.as_str())),
Event::End(link),
Event::End(Tag::TableCell),
Event::End(Tag::TableRow),
]);
}
events.push(Event::End(Tag::Table(vec![
Alignment::Left,
Alignment::Left,
])));
events
}
fn methods_table<'ev>(methods: &'ev [Method], resolver: &'ev Resolver) -> Vec<Event<'ev>> {
let mut events = vec![
Event::Start(Tag::Heading(2)),
Event::Text(CowStr::Borrowed("Methods")),
Event::End(Tag::Heading(2)),
Event::Start(Tag::Table(vec![Alignment::Left, Alignment::Left])),
Event::Start(Tag::TableHead),
Event::Start(Tag::TableCell),
Event::Text(CowStr::Borrowed("returns")),
Event::End(Tag::TableCell),
Event::Start(Tag::TableCell),
Event::Text(CowStr::Borrowed("method")),
Event::End(Tag::TableCell),
Event::End(Tag::TableHead),
];
for method in methods {
let link = format!("#func-{}", method.name);
events.push(Event::Start(Tag::TableRow));
events.push(Event::Start(Tag::TableCell));
events.extend(resolver.encode_type(&method.return_type));
events.push(Event::End(Tag::TableCell));
events.push(Event::Start(Tag::TableCell));
let link = Tag::Link(
LinkType::Reference,
link.into(),
method.name.as_str().into(),
);
events.extend(vec![
Event::Start(link.clone()),
Event::Text(CowStr::Borrowed(&method.name)),
Event::End(link),
Event::Text(CowStr::Borrowed("( ")),
]);
for (index, (name, typ, _)) in method.parameters.iter().enumerate() {
events.push(Event::Text(format!("{}: ", name).into()));
events.extend(resolver.encode_type(typ));
if index + 1 != method.parameters.len() {
events.push(Event::Text(CowStr::Borrowed(", ")));
}
}
events.extend(vec![
Event::Text(CowStr::Borrowed(" )")),
Event::End(Tag::TableCell),
Event::End(Tag::TableRow),
]);
}
events.push(Event::End(Tag::Table(vec![
Alignment::Left,
Alignment::Left,
])));
events
}
}
struct EventIterator<'resolver, 'parser> {
context: &'resolver Resolver,
parser: Parser<'parser>,
}
impl<'resolver, 'parser> Iterator for EventIterator<'resolver, 'parser> {
type Item = Event<'parser>;
fn next(&mut self) -> Option<Self::Item> {
let mut next_event = self.parser.next()?;
next_event = match next_event {
Event::Start(Tag::Link(LinkType::ShortcutUnknown, dest, title)) => {
Event::Start(Tag::Link(LinkType::Shortcut, dest, title))
}
Event::End(Tag::Link(LinkType::ShortcutUnknown, dest, title)) => {
Event::End(Tag::Link(LinkType::Shortcut, dest, title))
}
_ => next_event,
};
self.context.resolve_event(&mut next_event);
Some(next_event)
}
}