rustdoc/doctest/
extracted.rs

1//! Rustdoc's doctest extraction.
2//!
3//! This module contains the logic to extract doctests and output a JSON containing this
4//! information.
5
6use serde::Serialize;
7
8use super::{BuildDocTestBuilder, ScrapedDocTest};
9use crate::config::Options as RustdocOptions;
10use crate::html::markdown;
11
12/// The version of JSON output that this code generates.
13///
14/// This integer is incremented with every breaking change to the API,
15/// and is returned along with the JSON blob into the `format_version` root field.
16/// Consuming code should assert that this value matches the format version(s) that it supports.
17const FORMAT_VERSION: u32 = 1;
18
19#[derive(Serialize)]
20pub(crate) struct ExtractedDocTests {
21    format_version: u32,
22    doctests: Vec<ExtractedDocTest>,
23}
24
25impl ExtractedDocTests {
26    pub(crate) fn new() -> Self {
27        Self { format_version: FORMAT_VERSION, doctests: Vec::new() }
28    }
29
30    pub(crate) fn add_test(
31        &mut self,
32        scraped_test: ScrapedDocTest,
33        opts: &super::GlobalTestOptions,
34        options: &RustdocOptions,
35    ) {
36        let edition = scraped_test.edition(options);
37
38        let ScrapedDocTest { filename, line, langstr, text, name, .. } = scraped_test;
39
40        let doctest = BuildDocTestBuilder::new(&text)
41            .crate_name(&opts.crate_name)
42            .edition(edition)
43            .lang_str(&langstr)
44            .build(None);
45        let (full_test_code, size) = doctest.generate_unique_doctest(
46            &text,
47            langstr.test_harness,
48            opts,
49            Some(&opts.crate_name),
50        );
51        self.doctests.push(ExtractedDocTest {
52            file: filename.prefer_remapped_unconditionaly().to_string(),
53            line,
54            doctest_attributes: langstr.into(),
55            doctest_code: if size != 0 { Some(full_test_code) } else { None },
56            original_code: text,
57            name,
58        });
59    }
60}
61
62#[derive(Serialize)]
63pub(crate) struct ExtractedDocTest {
64    file: String,
65    line: usize,
66    doctest_attributes: LangString,
67    original_code: String,
68    /// `None` if the code syntax is invalid.
69    doctest_code: Option<String>,
70    name: String,
71}
72
73#[derive(Serialize)]
74pub(crate) enum Ignore {
75    All,
76    None,
77    Some(Vec<String>),
78}
79
80impl From<markdown::Ignore> for Ignore {
81    fn from(original: markdown::Ignore) -> Self {
82        match original {
83            markdown::Ignore::All => Self::All,
84            markdown::Ignore::None => Self::None,
85            markdown::Ignore::Some(values) => Self::Some(values),
86        }
87    }
88}
89
90#[derive(Serialize)]
91struct LangString {
92    pub(crate) original: String,
93    pub(crate) should_panic: bool,
94    pub(crate) no_run: bool,
95    pub(crate) ignore: Ignore,
96    pub(crate) rust: bool,
97    pub(crate) test_harness: bool,
98    pub(crate) compile_fail: bool,
99    pub(crate) standalone_crate: bool,
100    pub(crate) error_codes: Vec<String>,
101    pub(crate) edition: Option<String>,
102    pub(crate) added_css_classes: Vec<String>,
103    pub(crate) unknown: Vec<String>,
104}
105
106impl From<markdown::LangString> for LangString {
107    fn from(original: markdown::LangString) -> Self {
108        let markdown::LangString {
109            original,
110            should_panic,
111            no_run,
112            ignore,
113            rust,
114            test_harness,
115            compile_fail,
116            standalone_crate,
117            error_codes,
118            edition,
119            added_classes,
120            unknown,
121        } = original;
122
123        Self {
124            original,
125            should_panic,
126            no_run,
127            ignore: ignore.into(),
128            rust,
129            test_harness,
130            compile_fail,
131            standalone_crate,
132            error_codes,
133            edition: edition.map(|edition| edition.to_string()),
134            added_css_classes: added_classes,
135            unknown,
136        }
137    }
138}