Skip to content

Instantly share code, notes, and snippets.

@cyqsimon
Last active August 8, 2024 04:28
Show Gist options
  • Save cyqsimon/046867e86ba2d79d997e9a36b7b83ddc to your computer and use it in GitHub Desktop.
Save cyqsimon/046867e86ba2d79d997e9a36b7b83ddc to your computer and use it in GitHub Desktop.
Cargo build script to test all Rust code snippets in your README.md file
fn main() {
extract_readme_example();
}
fn extract_readme_example() {
use std::{env, fmt::Write, fs, path::Path};
println!("cargo:rerun-if-changed=README.md");
// load README
let readme = fs::read_to_string("README.md").unwrap();
let lines = readme.lines().collect::<Vec<_>>();
// find code fragment indices
let mut lines_it = lines.iter().enumerate();
let mut examples_ranges = vec![];
loop {
let start =
lines_it.find_map(|(idx, &line)| matches!(line, "```rust" | "```rs").then_some(idx));
let Some(start) = start else { break };
let end = lines_it.find_map(|(idx, &line)| matches!(line, "```").then_some(idx));
let Some(end) = end else {
panic!(
"Cannot find a matching code block terminator (```) after line {}.",
start + 1 // line numbers are conventionally 1-indexed
);
};
// exclude code fragment markers (```rust / ```rs / ```)
examples_ranges.push(start + 1..end);
}
// collect fragments
let examples =
examples_ranges
.into_iter()
.enumerate()
.fold(String::new(), |mut out, (idx, range)| {
let code = lines[range].join("\n");
write!(
&mut out,
"\
#[test]
fn example_fragment{idx}() {{
{code}
}}
"
)
.unwrap();
out
});
// write
let out_path = Path::new(&env::var_os("OUT_DIR").unwrap()).join("readme_examples.rs");
fs::write(out_path, examples).unwrap();
}
#[cfg(test)]
include!(concat!(env!("OUT_DIR"), "/readme_examples.rs"));
@cyqsimon
Copy link
Author

cyqsimon commented Aug 8, 2024

Originally written for cyqsimon/documented@3c44bb0 under MIT license.

MSRV

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment