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::{DocTestBuilder, 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 = DocTestBuilder::new(
41            &text,
42            Some(&opts.crate_name),
43            edition,
44            false,
45            None,
46            Some(&langstr),
47        );
48        let (full_test_code, size) = doctest.generate_unique_doctest(
49            &text,
50            langstr.test_harness,
51            opts,
52            Some(&opts.crate_name),
53        );
54        self.doctests.push(ExtractedDocTest {
55            file: filename.prefer_remapped_unconditionaly().to_string(),
56            line,
57            doctest_attributes: langstr.into(),
58            doctest_code: if size != 0 { Some(full_test_code) } else { None },
59            original_code: text,
60            name,
61        });
62    }
63}
64
65#[derive(Serialize)]
66pub(crate) struct ExtractedDocTest {
67    file: String,
68    line: usize,
69    doctest_attributes: LangString,
70    original_code: String,
71    /// `None` if the code syntax is invalid.
72    doctest_code: Option<String>,
73    name: String,
74}
75
76#[derive(Serialize)]
77pub(crate) enum Ignore {
78    All,
79    None,
80    Some(Vec<String>),
81}
82
83impl From<markdown::Ignore> for Ignore {
84    fn from(original: markdown::Ignore) -> Self {
85        match original {
86            markdown::Ignore::All => Self::All,
87            markdown::Ignore::None => Self::None,
88            markdown::Ignore::Some(values) => Self::Some(values),
89        }
90    }
91}
92
93#[derive(Serialize)]
94struct LangString {
95    pub(crate) original: String,
96    pub(crate) should_panic: bool,
97    pub(crate) no_run: bool,
98    pub(crate) ignore: Ignore,
99    pub(crate) rust: bool,
100    pub(crate) test_harness: bool,
101    pub(crate) compile_fail: bool,
102    pub(crate) standalone_crate: bool,
103    pub(crate) error_codes: Vec<String>,
104    pub(crate) edition: Option<String>,
105    pub(crate) added_css_classes: Vec<String>,
106    pub(crate) unknown: Vec<String>,
107}
108
109impl From<markdown::LangString> for LangString {
110    fn from(original: markdown::LangString) -> Self {
111        let markdown::LangString {
112            original,
113            should_panic,
114            no_run,
115            ignore,
116            rust,
117            test_harness,
118            compile_fail,
119            standalone_crate,
120            error_codes,
121            edition,
122            added_classes,
123            unknown,
124        } = original;
125
126        Self {
127            original,
128            should_panic,
129            no_run,
130            ignore: ignore.into(),
131            rust,
132            test_harness,
133            compile_fail,
134            standalone_crate,
135            error_codes,
136            edition: edition.map(|edition| edition.to_string()),
137            added_css_classes: added_classes,
138            unknown,
139        }
140    }
141}