parent
0a0172e0fe
commit
eaf552d417
18 changed files with 208 additions and 82 deletions
42
.github/workflows/ci.yml
vendored
42
.github/workflows/ci.yml
vendored
|
@ -26,33 +26,14 @@ jobs:
|
|||
override: true
|
||||
components: clippy
|
||||
|
||||
# Do *not* use `--all-features` here, as the optional dependencies take a
|
||||
# long time to build, and will be tested in the "examples" job anyway
|
||||
- name: Run tests
|
||||
run: cargo test --workspace --all-targets
|
||||
|
||||
- name: Check Clippy
|
||||
run: cargo clippy --workspace --all-targets -- -D warnings
|
||||
|
||||
# Optional features (i.e. web framework integrations) take a long time to
|
||||
# build and rarely break. Speed up CI by checking them separately.
|
||||
all-features:
|
||||
name: All features
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Check build
|
||||
run: cargo check --workspace --all-features --all-targets
|
||||
|
||||
# Please keep this in sync with `publish-docs.yml`
|
||||
documentation:
|
||||
name: Documentation
|
||||
|
@ -76,6 +57,25 @@ jobs:
|
|||
- name: Build documentation
|
||||
run: cd docs && make -j$(nproc)
|
||||
|
||||
examples:
|
||||
name: Examples
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Doctest
|
||||
run: cd doctest && cargo test
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
target
|
||||
/Cargo.lock
|
||||
docs/site
|
||||
doctest/Cargo.lock
|
||||
doctest/lib.rs
|
||||
|
|
|
@ -6,4 +6,5 @@ members = [
|
|||
]
|
||||
exclude = [
|
||||
"docs",
|
||||
"doctest",
|
||||
]
|
||||
|
|
|
@ -12,6 +12,7 @@ enum Princess { Celestia, Luna, Cadance, TwilightSparkle }
|
|||
|
||||
let user = Princess::Celestia;
|
||||
|
||||
# let _ = maud::
|
||||
html! {
|
||||
@if user == Princess::Luna {
|
||||
h1 { "Super secret woona to-do list" }
|
||||
|
@ -26,12 +27,14 @@ html! {
|
|||
p { "Nothing to see here; move along." }
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
`@if let` is supported as well:
|
||||
|
||||
```rust
|
||||
let user = Some("Pinkie Pie");
|
||||
# let _ = maud::
|
||||
html! {
|
||||
p {
|
||||
"Hello, "
|
||||
|
@ -43,6 +46,7 @@ html! {
|
|||
"!"
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
## Looping with `@for`
|
||||
|
@ -51,6 +55,7 @@ Use `@for .. in ..` to loop over the elements of an iterator.
|
|||
|
||||
```rust
|
||||
let names = ["Applejack", "Rarity", "Fluttershy"];
|
||||
# let _ = maud::
|
||||
html! {
|
||||
p { "My favorite ponies are:" }
|
||||
ol {
|
||||
|
@ -59,6 +64,7 @@ html! {
|
|||
}
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
## Declaring variables with `@let`
|
||||
|
@ -67,6 +73,7 @@ Declare a new variable within a template using `@let`. This can be useful when w
|
|||
|
||||
```rust
|
||||
let names = ["Applejack", "Rarity", "Fluttershy"];
|
||||
# let _ = maud::
|
||||
html! {
|
||||
@for name in &names {
|
||||
@let first_letter = name.chars().next().unwrap();
|
||||
|
@ -79,6 +86,7 @@ html! {
|
|||
}
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
## Matching with `@match`
|
||||
|
@ -90,6 +98,7 @@ enum Princess { Celestia, Luna, Cadance, TwilightSparkle }
|
|||
|
||||
let user = Princess::Celestia;
|
||||
|
||||
# let _ = maud::
|
||||
html! {
|
||||
@match user {
|
||||
Princess::Luna => {
|
||||
|
@ -106,4 +115,5 @@ html! {
|
|||
_ => p { "Nothing to see here; move along." }
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
Write an element using curly braces:
|
||||
|
||||
```rust
|
||||
# let _ = maud::
|
||||
html! {
|
||||
h1 { "Poem" }
|
||||
p {
|
||||
|
@ -12,6 +13,7 @@ html! {
|
|||
" you are a rock."
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
Before version 0.18, Maud allowed the curly braces to be omitted. This syntax was [removed][#137] and now causes an error instead.
|
||||
|
@ -23,6 +25,7 @@ Before version 0.18, Maud allowed the curly braces to be omitted. This syntax wa
|
|||
Terminate a void element using a semicolon:
|
||||
|
||||
```rust
|
||||
# let _ = maud::
|
||||
html! {
|
||||
link rel="stylesheet" href="poetry.css";
|
||||
p {
|
||||
|
@ -35,6 +38,7 @@ html! {
|
|||
"Rock."
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
The result will be rendered with HTML syntax – `<br>` not `<br />`.
|
||||
|
@ -48,12 +52,14 @@ Maud also supports ending a void element with a slash: `br /`. This syntax is [d
|
|||
Maud also supports elements and attributes with hyphens in them. This includes [custom elements], [data attributes], and [ARIA annotations].
|
||||
|
||||
```rust
|
||||
# let _ = maud::
|
||||
html! {
|
||||
article data-index="12345" {
|
||||
h1 { "My blog" }
|
||||
tag-cloud { "pinkie pie pony cute" }
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
[custom elements]: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
|
||||
|
@ -65,6 +71,7 @@ html! {
|
|||
Add attributes using the syntax: `attr="value"`. You can attach any number of attributes to an element. The values must be quoted: they are parsed as string literals.
|
||||
|
||||
```rust
|
||||
# let _ = maud::
|
||||
html! {
|
||||
ul {
|
||||
li {
|
||||
|
@ -79,6 +86,7 @@ html! {
|
|||
}
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
## Empty attributes: `checked`
|
||||
|
@ -86,6 +94,7 @@ html! {
|
|||
Declare an empty attribute by omitting the value.
|
||||
|
||||
```rust
|
||||
# let _ = maud::
|
||||
html! {
|
||||
form {
|
||||
input type="checkbox" name="cupcakes" checked;
|
||||
|
@ -93,6 +102,7 @@ html! {
|
|||
label for="cupcakes" { "Do you like cupcakes?" }
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
Before version 0.22.2, Maud required a `?` suffix on empty attributes: `checked?`. This is no longer necessary ([#238]), but still supported for backward compatibility.
|
||||
|
@ -104,17 +114,21 @@ Before version 0.22.2, Maud required a `?` suffix on empty attributes: `checked?
|
|||
Add classes and IDs to an element using `.foo` and `#bar` syntax. You can chain multiple classes and IDs together, and mix and match them with other attributes:
|
||||
|
||||
```rust
|
||||
# let _ = maud::
|
||||
html! {
|
||||
input#cannon.big.scary.bright-red type="button" value="Launch Party Cannon";
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
The classes and IDs can be quoted. This is useful for names with numbers or symbols which otherwise wouldn't parse:
|
||||
|
||||
```rust
|
||||
# let _ = maud::
|
||||
html! {
|
||||
div."col-sm-2" { "Bootstrap column!" }
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
## Implicit `div` elements
|
||||
|
@ -122,10 +136,12 @@ html! {
|
|||
If the element name is omitted, but there is a class or ID, then it is assumed to be a `div`.
|
||||
|
||||
```rust
|
||||
# let _ = maud::
|
||||
html! {
|
||||
#main {
|
||||
"Main content!"
|
||||
.tip { "Storing food in a refrigerator can make it 20% cooler." }
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
|
|
@ -37,7 +37,7 @@ fn main() {
|
|||
|
||||
Run this program with `cargo run`, and you should get the following:
|
||||
|
||||
```
|
||||
```html
|
||||
<p>Hi, Lyra!</p>
|
||||
```
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# A macro for writing HTML
|
||||
|
||||
```rust
|
||||
# let _ = maud::
|
||||
html! {
|
||||
h1 { "Hello, world!" }
|
||||
p.intro {
|
||||
|
@ -11,6 +12,7 @@ html! {
|
|||
" template language."
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
Maud is an HTML [template engine] for Rust. It's implemented as a macro, `html!`, which compiles your markup to specialized Rust code. This unique approach makes Maud templates blazing fast, super type-safe, and easy to deploy.
|
||||
|
|
|
@ -44,6 +44,8 @@ Using the `page` function will return the markup for the whole page.
|
|||
Here's an example:
|
||||
|
||||
```rust
|
||||
# use maud::{html, Markup};
|
||||
# fn page(title: &str, greeting_box: Markup) -> Markup { greeting_box }
|
||||
page("Hello!", html! {
|
||||
div { "Greetings, Maud." }
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ Below are some examples of using `Render`. Feel free to use these snippets in yo
|
|||
When writing a web page, it can be annoying to write `link rel="stylesheet"` over and over again. This example provides a shorthand for linking to CSS stylesheets.
|
||||
|
||||
```rust
|
||||
use maud::{Markup, Render};
|
||||
use maud::{html, Markup, Render};
|
||||
|
||||
/// Links to a CSS stylesheet at the given path.
|
||||
struct Css(&'static str);
|
||||
|
@ -32,8 +32,9 @@ When debugging an application, it can be useful to see its internal state. But t
|
|||
To avoid extra allocation, we override the `.render_to()` method instead of `.render()`. This doesn't do any escaping by default, so we wrap the output in an `Escaper` as well.
|
||||
|
||||
```rust
|
||||
use maud::{Render, Escaper};
|
||||
use maud::{Escaper, html, Render};
|
||||
use std::fmt;
|
||||
use std::fmt::Write as _;
|
||||
|
||||
/// Renders the given value using its `Debug` implementation.
|
||||
struct Debug<T: fmt::Debug>(T);
|
||||
|
@ -53,9 +54,7 @@ impl<T: fmt::Debug> Render for Debug<T> {
|
|||
We also use the [`ammonia`][ammonia] library, which sanitizes the resulting markup.
|
||||
|
||||
```rust
|
||||
extern crate ammonia;
|
||||
extern crate pulldown_cmark;
|
||||
|
||||
use ammonia;
|
||||
use maud::{Markup, PreEscaped, Render};
|
||||
use pulldown_cmark::{Parser, html};
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ Use `(foo)` syntax to insert the value of `foo` at runtime. Any HTML special cha
|
|||
```rust
|
||||
let best_pony = "Pinkie Pie";
|
||||
let numbers = [1, 2, 3, 4];
|
||||
# let _ = maud::
|
||||
html! {
|
||||
p { "Hi, " (best_pony) "!" }
|
||||
p {
|
||||
|
@ -14,11 +15,19 @@ html! {
|
|||
"and the first one is " (numbers[0])
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
Arbitrary Rust code can be included in a splice by using a [block](https://doc.rust-lang.org/reference.html#block-expressions). This can be helpful for complex expressions that would be difficult to read otherwise.
|
||||
|
||||
```rust
|
||||
# struct Foo;
|
||||
# impl Foo { fn time(self) -> Bar { Bar } }
|
||||
# struct Bar;
|
||||
# impl Bar { fn format(self, _: &str) -> &str { "" } }
|
||||
# fn something_convertible_to_foo() -> Option<Foo> { Some(Foo) }
|
||||
# fn test() -> Option<()> {
|
||||
# let _ = maud::
|
||||
html! {
|
||||
p {
|
||||
({
|
||||
|
@ -27,6 +36,9 @@ html! {
|
|||
})
|
||||
}
|
||||
}
|
||||
# ;
|
||||
# Some(())
|
||||
# }
|
||||
```
|
||||
|
||||
### Splices in attributes
|
||||
|
@ -35,22 +47,26 @@ Splices work in attributes as well.
|
|||
|
||||
```rust
|
||||
let secret_message = "Surprise!";
|
||||
# let _ = maud::
|
||||
html! {
|
||||
p title=(secret_message) {
|
||||
"Nothing to see here, move along."
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
To concatenate multiple values within an attribute, wrap the whole thing in braces. This syntax is useful for building URLs.
|
||||
|
||||
```rust
|
||||
const GITHUB: &'static str = "https://github.com";
|
||||
# let _ = maud::
|
||||
html! {
|
||||
a href={ (GITHUB) "/lambda-fairy/maud" } {
|
||||
"Fork me on GitHub"
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
### Splices in classes and IDs
|
||||
|
@ -60,11 +76,13 @@ Splices can also be used in classes and IDs.
|
|||
```rust
|
||||
let name = "rarity";
|
||||
let severity = "critical";
|
||||
# let _ = maud::
|
||||
html! {
|
||||
aside#(name) {
|
||||
p.{ "color-" (severity) } { "This is the worst! Possible! Thing!" }
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
### What can be spliced?
|
||||
|
@ -76,10 +94,12 @@ To change this behavior for some type, you can implement the [`Render`][Render]
|
|||
```rust
|
||||
use maud::PreEscaped;
|
||||
let post = "<p>Pre-escaped</p>";
|
||||
# let _ = maud::
|
||||
html! {
|
||||
h1 { "My super duper blog post" }
|
||||
(PreEscaped(post))
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
[Display]: http://doc.rust-lang.org/std/fmt/trait.Display.html
|
||||
|
@ -94,6 +114,7 @@ This works on empty attributes:
|
|||
|
||||
```rust
|
||||
let allow_editing = true;
|
||||
# let _ = maud::
|
||||
html! {
|
||||
p contenteditable[allow_editing] {
|
||||
"Edit me, I "
|
||||
|
@ -101,13 +122,16 @@ html! {
|
|||
" you."
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
And classes:
|
||||
|
||||
```rust
|
||||
let cuteness = 95;
|
||||
# let _ = maud::
|
||||
html! {
|
||||
p.cute[cuteness > 50] { "Squee!" }
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
Literal strings use the same syntax as Rust. Wrap them in double quotes, and use a backslash for escapes.
|
||||
|
||||
```rust
|
||||
# let _ = maud::
|
||||
html! {
|
||||
"Oatmeal, are you crazy?"
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
## Raw strings
|
||||
|
@ -15,6 +17,7 @@ html! {
|
|||
If the string is long, or contains many special characters, then it may be worth using [raw strings] instead:
|
||||
|
||||
```rust
|
||||
# let _ = maud::
|
||||
html! {
|
||||
pre {
|
||||
r#"
|
||||
|
@ -27,6 +30,7 @@ html! {
|
|||
"#
|
||||
}
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
[raw strings]: https://doc.rust-lang.org/reference/tokens.html#raw-string-literals
|
||||
|
@ -37,10 +41,12 @@ By default, HTML special characters are escaped automatically. Wrap the string i
|
|||
|
||||
```rust
|
||||
use maud::PreEscaped;
|
||||
# let _ = maud::
|
||||
html! {
|
||||
"<script>alert(\"XSS\")</script>" // <script>...
|
||||
(PreEscaped("<script>alert(\"XSS\")</script>")) // <script>...
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
||||
## The `DOCTYPE` constant
|
||||
|
@ -49,7 +55,9 @@ If you want to add a `<!DOCTYPE html>` declaration to your page, you may use the
|
|||
|
||||
```rust
|
||||
use maud::DOCTYPE;
|
||||
# let _ = maud::
|
||||
html! {
|
||||
(DOCTYPE) // <!DOCTYPE html>
|
||||
}
|
||||
# ;
|
||||
```
|
||||
|
|
|
@ -20,23 +20,28 @@ maud = { version = "*", features = ["actix-web"] }
|
|||
|
||||
Actix request handlers can use a `Markup` that implements the `actix_web::Responder` trait.
|
||||
|
||||
```rust
|
||||
```rust,no_run
|
||||
use actix_web::{get, App, HttpServer, Result as AwResult};
|
||||
use maud::{html, Markup};
|
||||
use actix_web::{web, App, HttpServer};
|
||||
use std::io;
|
||||
|
||||
fn index(params: web::Path<(String, u32)>) -> Markup {
|
||||
html! {
|
||||
h1 { "Hello " (params.0) " with id " (params.1) "!" }
|
||||
}
|
||||
#[get("/")]
|
||||
async fn index() -> AwResult<Markup> {
|
||||
Ok(html! {
|
||||
html {
|
||||
body {
|
||||
h1 { "Hello World!" }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
HttpServer::new(|| {
|
||||
App::new()
|
||||
.route("/user/{name}/{id}", web::get().to(index))
|
||||
})
|
||||
.bind("127.0.0.1:8080")?
|
||||
.run()
|
||||
#[actix_web::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
HttpServer::new(|| App::new().service(index))
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -53,7 +58,7 @@ maud = { version = "*", features = ["iron"] }
|
|||
|
||||
With this feature enabled, you can then build a `Response` from a `Markup` object directly. Here's an example application using Iron and Maud:
|
||||
|
||||
```rust
|
||||
```rust,no_run
|
||||
use iron::prelude::*;
|
||||
use iron::status;
|
||||
use maud::html;
|
||||
|
@ -86,7 +91,9 @@ maud = { version = "*", features = ["rocket"] }
|
|||
|
||||
This adds a `Responder` implementation for the `Markup` type, so you can return the result directly:
|
||||
|
||||
```rust
|
||||
```rust,no_run
|
||||
#![feature(decl_macro)]
|
||||
|
||||
use maud::{html, Markup};
|
||||
use rocket::{get, routes};
|
||||
use std::borrow::Cow;
|
||||
|
@ -108,7 +115,7 @@ fn main() {
|
|||
|
||||
Unlike with the other frameworks, Rouille doesn't need any extra features at all! Calling `Response::html` on the rendered `Markup` will Just Work®.
|
||||
|
||||
```rust
|
||||
```rust,no_run
|
||||
use maud::html;
|
||||
use rouille::{Response, router};
|
||||
|
||||
|
@ -116,10 +123,10 @@ fn main() {
|
|||
rouille::start_server("localhost:8000", move |request| {
|
||||
router!(request,
|
||||
(GET) (/{name: String}) => {
|
||||
html! {
|
||||
Response::html(html! {
|
||||
h1 { "Hello, " (name) "!" }
|
||||
p { "Nice to meet you!" }
|
||||
}
|
||||
})
|
||||
},
|
||||
_ => Response::empty_404()
|
||||
)
|
||||
|
|
|
@ -7,6 +7,7 @@ use std::fs::{self, File};
|
|||
use std::io::{self, BufReader};
|
||||
use std::mem;
|
||||
use std::path::Path;
|
||||
use std::str::{self, Utf8Error};
|
||||
use std::string::FromUtf8Error;
|
||||
use syntect::highlighting::{Color, ThemeSet};
|
||||
use syntect::html::highlighted_html_for_string;
|
||||
|
@ -102,13 +103,13 @@ fn load_page<'a>(
|
|||
arena: &'a Arena<AstNode<'a>>,
|
||||
options: &ComrakOptions,
|
||||
path: impl AsRef<Path>,
|
||||
) -> io::Result<Page<'a>> {
|
||||
) -> Result<Page<'a>, Box<dyn Error>> {
|
||||
let page = load_page_raw(arena, options, path)?;
|
||||
|
||||
lower_headings(page.content);
|
||||
rewrite_md_links(page.content)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
|
||||
highlight_code(page.content).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
|
||||
rewrite_md_links(page.content)?;
|
||||
strip_hidden_code(page.content)?;
|
||||
highlight_code(page.content)?;
|
||||
|
||||
Ok(page)
|
||||
}
|
||||
|
@ -173,7 +174,32 @@ fn rewrite_md_links<'a>(root: &'a AstNode<'a>) -> Result<(), FromUtf8Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn highlight_code<'a>(root: &'a AstNode<'a>) -> Result<(), FromUtf8Error> {
|
||||
fn strip_hidden_code<'a>(root: &'a AstNode<'a>) -> Result<(), Box<dyn Error>> {
|
||||
for node in root.descendants() {
|
||||
let mut data = node.data.borrow_mut();
|
||||
if let NodeValue::CodeBlock(NodeCodeBlock { info, literal, .. }) = &mut data.value {
|
||||
let info = parse_code_block_info(info)?;
|
||||
if !info.contains(&"rust") {
|
||||
continue;
|
||||
}
|
||||
*literal = strip_hidden_code_inner(str::from_utf8(literal)?).into_bytes();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn strip_hidden_code_inner(literal: &str) -> String {
|
||||
let lines = literal
|
||||
.split("\n")
|
||||
.filter(|line| {
|
||||
let line = line.trim();
|
||||
line != "#" && !line.starts_with("# ")
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
lines.join("\n")
|
||||
}
|
||||
|
||||
fn highlight_code<'a>(root: &'a AstNode<'a>) -> Result<(), Box<dyn Error>> {
|
||||
let ss = SyntaxSet::load_defaults_newlines();
|
||||
let ts = ThemeSet::load_defaults();
|
||||
let mut theme = ts.themes["InspiredGitHub"].clone();
|
||||
|
@ -186,9 +212,11 @@ fn highlight_code<'a>(root: &'a AstNode<'a>) -> Result<(), FromUtf8Error> {
|
|||
for node in root.descendants() {
|
||||
let mut data = node.data.borrow_mut();
|
||||
if let NodeValue::CodeBlock(NodeCodeBlock { info, literal, .. }) = &mut data.value {
|
||||
let info = String::from_utf8(mem::replace(info, Vec::new()))?;
|
||||
let syntax = ss
|
||||
.find_syntax_by_token(&info)
|
||||
let info = parse_code_block_info(info)?;
|
||||
let syntax = info
|
||||
.into_iter()
|
||||
.filter_map(|token| ss.find_syntax_by_token(&token))
|
||||
.next()
|
||||
.unwrap_or_else(|| ss.find_syntax_plain_text());
|
||||
let mut literal = String::from_utf8(mem::replace(literal, Vec::new()))?;
|
||||
if !literal.ends_with('\n') {
|
||||
|
@ -204,6 +232,10 @@ fn highlight_code<'a>(root: &'a AstNode<'a>) -> Result<(), FromUtf8Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_code_block_info(info: &[u8]) -> Result<Vec<&str>, Utf8Error> {
|
||||
str::from_utf8(info).map(|info| info.split(",").map(str::trim).collect())
|
||||
}
|
||||
|
||||
fn comrak_options() -> ComrakOptions {
|
||||
let mut options = ComrakOptions::default();
|
||||
options.extension.header_ids = Some("".to_string());
|
||||
|
|
17
doctest/Cargo.toml
Normal file
17
doctest/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "doctest"
|
||||
version = "0.1.0"
|
||||
authors = ["Chris Wong <lambda.fairy@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
actix-web = "3"
|
||||
ammonia = "3"
|
||||
iron = "0.6"
|
||||
maud = { path = "../maud", features = ["actix-web", "iron", "rocket"] }
|
||||
pulldown-cmark = "0.8"
|
||||
rocket = "0.4"
|
||||
rouille = "3"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
4
doctest/README.md
Normal file
4
doctest/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
This is a placeholder package that imports the entire [book] as a doc comment.
|
||||
This allows for testing the book's code snippets via `cargo test`.
|
||||
|
||||
[book]: ../docs/content
|
31
doctest/build.rs
Normal file
31
doctest/build.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use std::ffi::OsStr;
|
||||
use std::fmt::Write as _;
|
||||
use std::fs;
|
||||
|
||||
fn main() {
|
||||
const DOCS_DIR: &str = "../docs/content";
|
||||
|
||||
// Rebuild if a chapter is added or removed
|
||||
println!("cargo:rerun-if-changed={}", DOCS_DIR);
|
||||
|
||||
let mut buffer = r#"// Automatically @generated – do not edit
|
||||
|
||||
#![feature(extended_key_value_attributes)]
|
||||
"#.to_string();
|
||||
|
||||
for entry in fs::read_dir(DOCS_DIR).unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
assert!(entry.file_type().unwrap().is_file());
|
||||
|
||||
let path = entry.path();
|
||||
assert_eq!(path.extension(), Some(OsStr::new("md")));
|
||||
|
||||
let path_str = path.to_str().unwrap();
|
||||
let slug_str = path.file_stem().unwrap().to_str().unwrap().replace("-", "_");
|
||||
|
||||
writeln!(buffer, r#"#[doc = include_str!("{}")]"#, path_str).unwrap();
|
||||
writeln!(buffer, r#"mod {} {{ }}"#, slug_str).unwrap();
|
||||
}
|
||||
|
||||
fs::write("lib.rs", buffer).unwrap();
|
||||
}
|
|
@ -30,7 +30,3 @@ trybuild = { version = "1.0.33", features = ["diff"] }
|
|||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[[example]]
|
||||
name = "actix"
|
||||
required-features = ["actix-web"]
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
// do not use this line in your application
|
||||
use actix_web_dep as actix_web;
|
||||
|
||||
use actix_web::{get, App, HttpServer, Result as AwResult};
|
||||
use maud::{html, Markup};
|
||||
use std::io;
|
||||
|
||||
#[get("/")]
|
||||
async fn index() -> AwResult<Markup> {
|
||||
Ok(html! {
|
||||
html {
|
||||
body {
|
||||
h1 { "Hello World!" }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
HttpServer::new(|| App::new().service(index))
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
.await
|
||||
}
|
Loading…
Add table
Reference in a new issue