docmatic/
lib.rs

1//! docmatic:
2//!
3//! `docmatic` runs `rustdoc` on your documentation files.
4//!
5//! ## Writing code blocks
6//!
7//! See ["Documentation tests"](https://doc.rust-lang.org/beta/rustdoc/documentation-tests.html)
8//! for how to customize your code blocks being run as tests.
9//!
10//! ## Example
11//!
12//! First, add this to your `Cargo.toml`:
13//!
14//! ```toml
15//! [dev-dependencies]
16//! docmatic = "0.1"
17//! ```
18//!
19//! Next, in your test file:
20//!
21//! ```rust
22//! extern crate docmatic;
23//!
24//! fn test_readme() {
25//!     docmatic::assert_file("README.md");
26//! }
27//! ```
28
29extern crate which;
30
31use std::path;
32use std::ffi::OsStr;
33
34/// A specialized process builder managing a `rustdoc` test session.
35///
36/// # Example
37///
38/// The following code will test the crate README with the `docmatic`
39/// configuration set and a default library path:
40///
41/// ```rust
42/// extern crate docmatic;
43///
44/// use std::default::Default;
45///
46/// fn test_readme() {
47///     docmatic::Assert::default()
48///         .cfg("docmatic")
49///         .test_file("README.md")
50/// }
51/// ```
52pub struct Assert(std::process::Command);
53
54impl Assert {
55    /// Construct a new `Assert` with no flags set.
56    ///
57    /// Will likely fail if you don't provide at least one library path
58    /// containing the tested crate. Instead, you should probably use
59    /// [`Assert::default`]
60    ///
61    /// [`Assert::default`]: #tymethod.default
62    pub fn new() -> Self {
63        let executable = which::which("rustdoc").expect("rustdoc not found");
64        Assert(std::process::Command::new(executable))
65    }
66
67    /// Add a path to the library paths passed to `rustdoc`.
68    pub fn library_path<S>(&mut self, path: S) -> &mut Self
69    where
70        S: AsRef<OsStr>,
71    {
72        self.0.arg("--library-path").arg(path);
73        self
74    }
75
76    /// Add a *cfg* to the configuration passed to `rustdoc`.
77    pub fn cfg<S>(&mut self, cfg: S) -> &mut Self
78    where
79        S: AsRef<OsStr>,
80    {
81        self.0.arg("--cfg").arg(cfg);
82        self
83    }
84
85    /// Test the given file, and panics on failure.
86    pub fn test_file<P>(&mut self, path: P)
87    where
88        P: AsRef<path::Path>,
89    {
90        let process = self.0.arg("--test").arg(path.as_ref()).spawn();
91
92        let result = process
93            .expect("rustdoc is runnable")
94            .wait()
95            .expect("rustdoc can run");
96
97        assert!(
98            result.success(),
99            format!("Failed to run rustdoc tests on '{:?}'", path.as_ref())
100        );
101    }
102}
103
104impl Default for Assert {
105    /// Create an `Assert` instance with the following default parameters:
106    ///
107    /// * `--library-path` set to the current *deps* directory (`target/debug/deps` or
108    ///   `target/release/deps` depending on the test compilation mode).
109    ///
110    fn default() -> Self {
111        let mut assert = Self::new();
112        let current_exe = std::env::current_exe()
113            .and_then(|p| p.canonicalize())
114            .expect("could not get path to test executable");
115        assert.library_path(current_exe.parent().expect("parent exists"));
116        assert
117    }
118}
119
120/// Test a single file with default parameters.
121pub fn assert_file<P>(documentation: P)
122where
123    P: AsRef<path::Path>,
124{
125    Assert::default().test_file(documentation);
126}