Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: add builder pattern for exporting and generating documentation #15

Merged
merged 7 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,29 @@ mod my_module {
// where the documentation will be generated.
let docs_path = std::env::var("DOCS_DIR").unwrap_or("target/docs".to_string());

let mut engine = rhai::Engine::new();

// We register the module defined in the previous code block for this example,
// Create a new engine and register all modules that need to have documentation generated for them.
// In this example, the module defined in the previous code block is registered into the engine,
// but you could register other functions and types ...
let mut engine = rhai::Engine::new();
engine.register_static_module("my_module", exported_module!(my_module).into());

let docs = rhai_autodocs::options()
/// Export the documentation as a [`ModuleDocumentation`]. You could stop here and generate
/// you own docs from this structure.
let docs = rhai_autodocs::export::options()
.include_standard_packages(false)
.generate(&engine)
.export(&engine)
.expect("failed to generate documentation");

// Write the documentation in a file, or output to stdout, etc.
for (name, docs) in rhai_autodocs::generate_for_docusaurus(&docs).expect("failed to generate mdx for docusaurus") {
/// Or you could use pre-defined templates for docusaurus or mdbook.
/// Here, documentation is generated for docusaurus with some options.
let mdx = rhai_autodocs::generate::docusaurus()
.with_slug("/docs/api")
.build(&docs)
.expect("failed to generate mdx for docusaurus");

/// Iterate over the generated documentation for every modules.
for (name, docs) in mdx {
// Write the documentation in a file, or output to stdout, or anything really.
println!("docs for module {name}");
println!("{docs}");
}
Expand Down
4 changes: 2 additions & 2 deletions examples/basic/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ fn main() {
let path = "./my-module.md";

// register custom functions and types ...
let docs = rhai_autodocs::module::options::options()
let docs = rhai_autodocs::export::options()
.include_standard_packages(false)
.order_items_with(rhai_autodocs::module::options::ItemsOrder::ByIndex)
.generate(&engine)
.export(&engine)
.expect("failed to generate documentation");

// Write the documentation in a file.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: global
slug: /global
slug: /docs/api/global
---

import Tabs from '@theme/Tabs';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: my_module
slug: /my_module
slug: /docs/api/my_module
---

import Tabs from '@theme/Tabs';
Expand Down
11 changes: 7 additions & 4 deletions examples/docusaurus/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use rhai::plugin::*;
use rhai_autodocs::generate_for_docusaurus;

/// My own module.
#[export_module]
Expand Down Expand Up @@ -152,17 +151,21 @@ fn main() {
engine.build_type::<DocumentedType>();

// Generate documentation structure.
let docs = rhai_autodocs::module::options()
let docs = rhai_autodocs::export::options()
.include_standard_packages(false)
.order_items_with(rhai_autodocs::module::options::ItemsOrder::ByIndex)
.format_sections_with(rhai_autodocs::module::options::SectionFormat::Tabs)
.generate(&engine)
.export(&engine)
.expect("failed to generate documentation");

let path = "./examples/docusaurus/docusaurus-example/docs/rhai-autodocs";

// Write the documentation in files for docusaurus.
for (name, doc) in generate_for_docusaurus(&docs).unwrap() {
for (name, doc) in rhai_autodocs::generate::docusaurus()
.with_slug("/docs/api")
.build(&docs)
.unwrap()
{
std::fs::write(
std::path::PathBuf::from_iter([path, &format!("{}.mdx", &name)]),
doc,
Expand Down
7 changes: 3 additions & 4 deletions examples/mdbook/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use rhai::plugin::*;
use rhai_autodocs::generate_for_mdbook;

/// My own module.
#[export_module]
Expand Down Expand Up @@ -52,17 +51,17 @@ fn main() {
engine.register_static_module("my_module", exported_module!(my_module).into());

// register custom functions and types ...
let docs = rhai_autodocs::module::options()
let docs = rhai_autodocs::export::options()
.include_standard_packages(false)
.order_items_with(rhai_autodocs::module::options::ItemsOrder::ByIndex)
.format_sections_with(rhai_autodocs::module::options::SectionFormat::Tabs)
.generate(&engine)
.export(&engine)
.expect("failed to generate documentation");

let path = "./examples/mdbook/mdbook-example/src";

// Write the documentation in files.
for (name, doc) in generate_for_mdbook(&docs).unwrap() {
for (name, doc) in rhai_autodocs::generate::mdbook().build(&docs).unwrap() {
std::fs::write(
std::path::PathBuf::from_iter([path, &format!("{}.md", &name)]),
doc,
Expand Down
8 changes: 2 additions & 6 deletions examples/mdbook/mdbook-example/src/my_module.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@

```Namespace: global/my_module```

<div style='box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); padding: 15px; border-radius: 5px;'>
<div markdown="span" style='box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); padding: 15px; border-radius: 5px;'>
<h2 class="func-name"> <code>fn</code> hello_world </h2>

```rust,ignore
fn hello_world()
fn hello_world(message: String)
```



<div group="hello_world" id="hello_world-description" style="display: block;" markdown="span"
class="tabcontent">

Expand All @@ -33,15 +31,13 @@
</div>
</div>
</br>
<div style='box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); padding: 15px; border-radius: 5px;'>
<div markdown="span" style='box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); padding: 15px; border-radius: 5px;'>
<h2 class="func-name"> <code>fn</code> add </h2>

```rust,ignore
fn add(a: int, b: int) -> int
```



<div group="add" id="add-description" style="display: block;" markdown="span"
class="tabcontent">

Expand Down
2 changes: 1 addition & 1 deletion src/handlebars/docusaurus/header.hbs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: {{title}}
slug: /{{slug}}
slug: {{slug}}
---

import Tabs from '@theme/Tabs';
Expand Down
120 changes: 82 additions & 38 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,66 +8,110 @@ pub mod module;

pub use glossary::ModuleGlossary;
pub use module::{
options::{options, ItemsOrder, MarkdownProcessor, SectionFormat},
options::{export, ItemsOrder, MarkdownProcessor, SectionFormat},
ModuleDocumentation,
};
use serde_json::json;

/// Generate documentation for the docusaurus markdown processor.
///
/// Returns a hashmap with the name of the module as the key and its raw documentation as the value.
pub fn generate_for_docusaurus(
module: &ModuleDocumentation,
) -> Result<std::collections::HashMap<String, String>, handlebars::RenderError> {
let mut hbs_registry = handlebars::Handlebars::new();
#[derive(Default)]
pub struct DocusaurusOptions {
pub(crate) slug: Option<String>,
}

impl DocusaurusOptions {
/// Format the slug in the metadata section of the generated MDX document by concatenating the `slug` parameter with the module name.
///
/// For example, if the documentation for a module called `my_module` is generated with
/// the slug `/docs/api/`, the slug set in the document will be `/docs/api/my_module`.
///
/// By default the root `/` path is used.
pub fn with_slug(mut self, slug: &str) -> Self {
self.slug = Some(slug.to_string());

self
}

/// Build MDX documentation for docusaurus from the given module documentation struct.
///
/// Returns a hashmap with the name of the module as the key and its raw documentation as the value.
pub fn build(
self,
module: &ModuleDocumentation,
) -> Result<std::collections::HashMap<String, String>, handlebars::RenderError> {
let mut hbs_registry = handlebars::Handlebars::new();

hbs_registry
.register_template_string(
"docusaurus-module",
include_str!("handlebars/docusaurus/header.hbs"),
)
.expect("template is valid");

hbs_registry
.register_template_string(
// A partial used to keep indentation for mdx to render correctly.
hbs_registry
.register_partial("ContentPartial", "{{{content}}}")
.expect("partial is valid");

generate(
module,
"docusaurus-module",
include_str!("handlebars/docusaurus/header.hbs"),
self.slug.as_deref(),
&hbs_registry,
)
.expect("template is valid");
}
}

// A partial used to keep indentation for mdx to render correctly.
hbs_registry
.register_partial("ContentPartial", "{{{content}}}")
.expect("partial is valid");
#[derive(Default)]
pub struct MDBookOptions;

generate(module, "docusaurus-module", &hbs_registry)
}
impl MDBookOptions {
/// Build html documentation for mdbook from the given module documentation struct.
///
/// Returns a hashmap with the name of the module as the key and its raw documentation as the value.
pub fn build(
self,
module: &ModuleDocumentation,
) -> Result<std::collections::HashMap<String, String>, handlebars::RenderError> {
let mut hbs_registry = handlebars::Handlebars::new();

/// Generate documentation for the mdbook markdown processor.
///
/// Returns a hashmap with the name of the module as the key and its raw documentation as the value.
pub fn generate_for_mdbook(
module: &ModuleDocumentation,
) -> Result<std::collections::HashMap<String, String>, handlebars::RenderError> {
let mut hbs_registry = handlebars::Handlebars::new();
hbs_registry
.register_template_string(
"mdbook-module",
include_str!("handlebars/mdbook/header.hbs"),
)
.expect("template is valid");

hbs_registry
.register_template_string(
"mdbook-module",
include_str!("handlebars/mdbook/header.hbs"),
)
.expect("template is valid");
// A partial used to keep indentation for md to render correctly.
hbs_registry
.register_partial("ContentPartial", "{{{content}}}")
.expect("partial is valid");

// A partial used to keep indentation for md to render correctly.
hbs_registry
.register_partial("ContentPartial", "{{{content}}}")
.expect("partial is valid");
generate(module, "mdbook-module", None, &hbs_registry)
}
}

generate(module, "mdbook-module", &hbs_registry)
pub mod generate {
/// Create a new builder to generate documentation for docusaurus from a [`ModuleDocumentation`] object.
pub fn docusaurus() -> super::DocusaurusOptions {
super::DocusaurusOptions::default()
}

/// Create a new builder to generate documentation for mdbook from a [`ModuleDocumentation`] object.
pub fn mdbook() -> super::MDBookOptions {
super::MDBookOptions::default()
}
}

fn generate(
module: &ModuleDocumentation,
template: &str,
slug: Option<&str>,
hbs_registry: &handlebars::Handlebars,
) -> Result<std::collections::HashMap<String, String>, handlebars::RenderError> {
let mut documentation = std::collections::HashMap::default();
let data = json!({
"title": module.name,
"slug": module.name,
"slug": slug.map_or(format!("/{}", module.name), |slug| format!("{}/{}", slug, module.name)),
"description": module.documentation,
"namespace": module.namespace,
"items": module.items,
Expand All @@ -79,7 +123,7 @@ fn generate(
);

for sub in &module.sub_modules {
documentation.extend(generate(sub, template, hbs_registry)?);
documentation.extend(generate(sub, template, slug, hbs_registry)?);
}

Ok(documentation)
Expand Down
16 changes: 7 additions & 9 deletions src/module.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
pub mod error;
pub mod options;

pub use self::options::options;

use self::{error::AutodocsError, options::Options};
use crate::custom_types::CustomTypesMetadata;
use crate::doc_item::DocItem;
Expand Down Expand Up @@ -47,7 +45,7 @@ pub(crate) struct ModuleMetadata {
/// # Errors
/// * Failed to generate function metadata as json.
/// * Failed to parse module metadata.
pub fn generate_module_documentation(
pub(crate) fn generate_module_documentation(
engine: &rhai::Engine,
options: &Options,
) -> Result<ModuleDocumentation, AutodocsError> {
Expand Down Expand Up @@ -145,9 +143,8 @@ pub(crate) fn group_functions(

#[cfg(test)]
mod test {
use crate::{generate_for_docusaurus, module::options::ItemsOrder};
use crate::{export, generate, module::options::ItemsOrder};

use super::*;
use rhai::plugin::*;

/// My own module.
Expand Down Expand Up @@ -180,16 +177,17 @@ mod test {
fn test_order_by_index() {
let mut engine = rhai::Engine::new();

// register custom functions and types ...
engine.register_static_module("my_module", rhai::exported_module!(my_module).into());

// register custom functions and types ...
let docs = options::options()
// export documentation with option.
let docs = export::options()
.include_standard_packages(false)
.order_items_with(ItemsOrder::ByIndex)
.generate(&engine)
.export(&engine)
.expect("failed to generate documentation");

let docs = generate_for_docusaurus(&docs).unwrap();
let docs = generate::docusaurus().build(&docs).unwrap();

pretty_assertions::assert_eq!(
docs.get("global")
Expand Down
Loading
Loading