Skip to content

Commit

Permalink
Merge pull request #20 from brson/next
Browse files Browse the repository at this point in the history
Add template files and bump version
  • Loading branch information
brson authored Mar 25, 2017
2 parents b91faea + bd7a664 commit 5aae851
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 12 deletions.
55 changes: 53 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,56 @@ function, as in all the examples above. Implicit wrapping of examples
in `main`, and custom injection of `extern crate` statements and crate
attributes are controlled through document-level templates.

Templates for a document are located in a separate file, that lives
next to the document on the filesystem, and has the same full name as
the document file, but with an additional ".skt.md" template.

So for example, this file, `README.md`, stores its templates
in `README.md.skt.md`.

This scheme allows the markdown to be displayed naturally be stock
Markdown renderers without displaying the template itself. The weird
file extension is similarly so that the templates themselves are
interpreted as valid markdown while being easy to ignore based on file
system.

Consider this example:

```rust,skt-foo
let p = PathBuf::from("foo");
println!("{:?}", p);
```

This example won't compile without defining `main` and importing
`PathBuf`, but the example itself does not contain that
boilerplate. Instead it is annotated `skt-foo`, for _skeptic template
foo_, like so:

<code>```rust,skt-foo</code>
```rust,ignore
let p = PathBuf::from("foo");
println!("{:?}", p);
```
<code>```</code>

This tells skeptic to look in the template file for another
markdown block with the same `skt-foo` annotation, and composes
them together using the standard Rust `format!` macro. Here's
what the template looks like:

<code>```rust,skt-foo</code>
```rust,ignore
use std::path::PathBuf;
fn main() {{
{}
}}
```

Note that in a template, real braces need to be doubled.

## Old-style Skeptic Templates

Since the examples in this README run as written, it doesn't need a
template, but we can specifiy a no-op template like so:

Expand All @@ -157,8 +207,9 @@ template, but we can specifiy a no-op template like so:

Templates are [Rust format
specifiers](http://doc.rust-lang.org/std/fmt/index.html) that must
take a single argument (i.e. they need to contain the string "{}"). See
[the template example](template-example.md) for more on templates.
take a single argument (i.e. they need to contain the string
"{}"). See [the (old) template example](template-example.md) for more
on templates.

Rust Skeptic uses
[`pulldown-cmark`](https://github.com/google/pulldown-cmark) for
Expand Down
7 changes: 7 additions & 0 deletions README.md.skt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```rust,skt-foo
use std::path::PathBuf;
fn main() {{
{}
}}
```
2 changes: 1 addition & 1 deletion src/skeptic/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "skeptic"
version = "0.7.1"
version = "0.8.0"
authors = ["Brian Anderson <[email protected]>"]
description = "Test your Rust markdown documentation via Cargo"
license = "MIT/Apache-2.0"
Expand Down
84 changes: 75 additions & 9 deletions src/skeptic/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::fs::File;
use std::io::{self, Read, Write, Error as IoError};
use std::path::{PathBuf, Path};
use cmark::{Parser, Event, Tag};
use std::collections::HashMap;

pub fn generate_doc_tests<T>(docs: &[T]) where T : AsRef<str> {
// This shortcut is specifically so examples in skeptic's on
Expand Down Expand Up @@ -55,15 +56,17 @@ struct Test {
ignore: bool,
no_run: bool,
should_panic: bool,
template: Option<String>,
}

struct DocTestSuite {
doc_tests: Vec<DocTest>,
}

struct DocTest {
template: Option<String>,
old_template: Option<String>,
tests: Vec<Test>,
templates: HashMap<String, String>,
}

fn extract_tests(config: &Config) -> Result<DocTestSuite, IoError> {
Expand All @@ -79,7 +82,8 @@ fn extract_tests(config: &Config) -> Result<DocTestSuite, IoError> {

fn extract_tests_from_file(path: &Path) -> Result<DocTest, IoError> {
let mut tests = Vec::new();
let mut template = None;
// Oh this isn't actually a test but a legacy template
let mut old_template = None;

let mut file = try!(File::open(path));
let ref mut s = String::new();
Expand All @@ -105,15 +109,16 @@ fn extract_tests_from_file(path: &Path) -> Result<DocTest, IoError> {
Event::End(Tag::CodeBlock(ref info)) => {
let code_block_info = parse_code_block_info(info);
if let Some(buf) = code_buffer.take() {
if code_block_info.is_template {
template = Some(buf.into_iter().collect())
if code_block_info.is_old_template {
old_template = Some(buf.into_iter().collect())
} else {
tests.push(Test {
name: test_name_gen.advance(),
text: buf,
ignore: code_block_info.ignore,
no_run: code_block_info.no_run,
should_panic: code_block_info.should_panic,
template: code_block_info.template,
});
}
}
Expand All @@ -122,12 +127,59 @@ fn extract_tests_from_file(path: &Path) -> Result<DocTest, IoError> {
}
}

let templates = load_templates(path)?;

Ok(DocTest {
template: template,
old_template: old_template,
tests: tests,
templates: templates,
})
}

fn load_templates(path: &Path) -> Result<HashMap<String, String>, IoError> {
let file_name = format!("{}.skt.md", path.file_name().expect("no file name").to_string_lossy());
let path = path.with_file_name(&file_name);
if !path.exists() {
return Ok(HashMap::new());
}

let mut map = HashMap::new();

let mut file = try!(File::open(path));
let ref mut s = String::new();
try!(file.read_to_string(s));
let parser = Parser::new(s);

let mut code_buffer = None;

for event in parser {
match event {
Event::Start(Tag::CodeBlock(ref info)) => {
let code_block_info = parse_code_block_info(info);
if code_block_info.is_rust {
code_buffer = Some(Vec::new());
}
}
Event::Text(text) => {
if let Some(ref mut buf) = code_buffer {
buf.push(text.to_string());
}
}
Event::End(Tag::CodeBlock(ref info)) => {
let code_block_info = parse_code_block_info(info);
if let Some(buf) = code_buffer.take() {
if let Some(t) = code_block_info.template {
map.insert(t, buf.into_iter().collect());
}
}
}
_ => (),
}
}

Ok(map)
}

struct TestNameGen {
root: String,
count: i32,
Expand Down Expand Up @@ -181,7 +233,8 @@ fn parse_code_block_info(info: &str) -> CodeBlockInfo {
should_panic: false,
ignore: false,
no_run: false,
is_template: false,
is_old_template: false,
template: None,
};

for token in tokens {
Expand All @@ -204,9 +257,13 @@ fn parse_code_block_info(info: &str) -> CodeBlockInfo {
seen_rust_tags = true;
}
"skeptic-template" => {
info.is_template = true;
info.is_old_template = true;
seen_rust_tags = true
}
_ if token.starts_with("skt-") => {
info.template = Some(token[4..].to_string());
seen_rust_tags = true;
}
_ => seen_other_tags = true,
}
}
Expand All @@ -221,7 +278,8 @@ struct CodeBlockInfo {
should_panic: bool,
ignore: bool,
no_run: bool,
is_template: bool,
is_old_template: bool,
template: Option<String>,
}

fn emit_tests(config: &Config, suite: DocTestSuite) -> Result<(), IoError> {
Expand All @@ -232,7 +290,15 @@ fn emit_tests(config: &Config, suite: DocTestSuite) -> Result<(), IoError> {

for doc_test in suite.doc_tests {
for test in &doc_test.tests {
let test_string = try!(create_test_string(config, &doc_test.template, test));
let test_string = {
if let Some(ref t) = test.template {
let template = doc_test.templates.get(t)
.expect(&format!("template {} not found", t));
try!(create_test_string(config, &Some(template.to_string()), test))
} else {
try!(create_test_string(config, &doc_test.old_template, test))
}
};
out.push_str(&test_string);
}
}
Expand Down

0 comments on commit 5aae851

Please sign in to comment.