Skip to content

Commit

Permalink
Support Markdown in API documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
jbearer committed Dec 17, 2023
1 parent cc05e55 commit 285cc09
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 9 deletions.
8 changes: 6 additions & 2 deletions examples/hello-world/api.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
[meta]
NAME = "hello-world"
DESCRIPTION = "An example of a simple Tide Disco module"
FORMAT_VERSION = "0.1.0"
NAME = "hello-world"
DESCRIPTION = """
An example of a simple Tide Disco module
This example demonstrates the basic functionality of Tide Disco, with `GET` and `POST` routes.
"""

[route.greeting]
PATH = ["greeting/:name"]
Expand Down
43 changes: 38 additions & 5 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ mod meta_defaults {
alt='Espresso Systems Logo'
/></a></div>
<h1>{{NAME}} API {{VERSION}} Reference</h1>
<p>{{DESCRIPTION}}</p>
<p>{{SHORT_DESCRIPTION}}</p><br/>
{{LONG_DESCRIPTION}}
"
.to_string()
}
Expand Down Expand Up @@ -258,6 +259,8 @@ pub struct Api<State, Error> {
health_check: Option<HealthCheckHandler<State>>,
api_version: Option<Version>,
public: Option<PathBuf>,
short_description: String,
long_description: String,
}

impl<'a, State, Error> IntoIterator for &'a Api<State, Error> {
Expand Down Expand Up @@ -354,6 +357,31 @@ impl<State, Error> Api<State, Error> {
}
}
}

// Parse description: the first line is a short description, to display when briefly
// describing this API in a list. The rest is the long description, to display on this API's
// own documentation page. Both are rendered to HTML via Markdown.
let blocks = markdown::tokenize(&meta.description);
let (short_description, long_description) = match blocks.split_first() {
Some((short, long)) => {
let render = |blocks| markdown::to_html(&markdown::generate_markdown(blocks));

let short = render(vec![short.clone()]);
let long = render(long.to_vec());

// The short description is only one block, and sometimes we would like to display
// it inline (as a `span`). Markdown automatically wraps blocks in `<p>`. We will
// strip this outer tag so that we can wrap it in either `<p>` or `<span>`,
// depending on the context.
let short = short.strip_prefix("<p>").unwrap_or(&short);
let short = short.strip_suffix("</p>").unwrap_or(short);
let short = short.to_string();

(short, long)
}
None => Default::default(),
};

Ok(Self {
name: meta.name.clone(),
meta,
Expand All @@ -362,6 +390,8 @@ impl<State, Error> Api<State, Error> {
health_check: None,
api_version: None,
public: None,
short_description,
long_description,
})
}

Expand Down Expand Up @@ -1063,6 +1093,8 @@ impl<State, Error> Api<State, Error> {
health_check: self.health_check,
api_version: self.api_version,
public: self.public,
short_description: self.short_description,
long_description: self.long_description,
}
}

Expand All @@ -1075,7 +1107,8 @@ impl<State, Error> Api<State, Error> {
html! {
(PreEscaped(self.meta.html_top
.replace("{{NAME}}", &self.name)
.replace("{{DESCRIPTION}}", &self.meta.description)
.replace("{{SHORT_DESCRIPTION}}", &self.short_description)
.replace("{{LONG_DESCRIPTION}}", &self.long_description)
.replace("{{VERSION}}", &match &self.api_version {
Some(version) => version.to_string(),
None => "(no version)".to_string(),
Expand All @@ -1089,9 +1122,9 @@ impl<State, Error> Api<State, Error> {
}
}

/// The description of this API from the specification.
pub fn description(&self) -> &str {
&self.meta.description
/// The short description of this API from the specification.
pub fn short_description(&self) -> &str {
&self.short_description
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use async_std::sync::Arc;
use futures::future::BoxFuture;
use include_dir::{include_dir, Dir};
use lazy_static::lazy_static;
use maud::html;
use maud::{html, PreEscaped};
use semver::Version;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};
Expand Down Expand Up @@ -350,7 +350,7 @@ impl<State: Send + Sync + 'static, Error: 'static + crate::Error> App<State, Err
li {
a href=(format!("/{}", name)) {(name)}
" "
(api.description())
(PreEscaped(api.short_description()))
}
}
}
Expand Down

0 comments on commit 285cc09

Please sign in to comment.