use std::fs::read_to_string;
use std::sync::{Arc, Mutex};
use rustc_span::FileName;
use tempfile::tempdir;
use super::{
generate_args_file, CreateRunnableDocTests, DocTestVisitor, GlobalTestOptions, ScrapedDocTest,
};
use crate::config::Options;
use crate::html::markdown::{find_testable_code, ErrorCodes, LangString, MdRelLine};
struct MdCollector {
tests: Vec<ScrapedDocTest>,
cur_path: Vec<String>,
filename: FileName,
}
impl DocTestVisitor for MdCollector {
fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) {
let filename = self.filename.clone();
let line = 1 + rel_line.offset();
self.tests.push(ScrapedDocTest::new(filename, line, self.cur_path.clone(), config, test));
}
fn visit_header(&mut self, name: &str, level: u32) {
let name = name
.chars()
.enumerate()
.map(|(i, c)| {
if (i == 0 && rustc_lexer::is_id_start(c))
|| (i != 0 && rustc_lexer::is_id_continue(c))
{
c
} else {
'_'
}
})
.collect::<String>();
let level = level as usize;
if level <= self.cur_path.len() {
self.cur_path.truncate(level);
self.cur_path[level - 1] = name;
} else {
if level - 1 > self.cur_path.len() {
self.cur_path.resize(level - 1, "_".to_owned());
}
self.cur_path.push(name);
}
}
}
pub(crate) fn test(options: Options) -> Result<(), String> {
use rustc_session::config::Input;
let input_str = match &options.input {
Input::File(path) => {
read_to_string(path).map_err(|err| format!("{}: {err}", path.display()))?
}
Input::Str { name: _, input } => input.clone(),
};
let crate_name = options.input.filestem().to_string();
let temp_dir =
tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?;
let args_file = temp_dir.path().join("rustdoc-cfgs");
generate_args_file(&args_file, &options)?;
let opts = GlobalTestOptions {
crate_name,
no_crate_inject: true,
insert_indent_space: false,
attrs: vec![],
args_file,
};
let mut md_collector = MdCollector {
tests: vec![],
cur_path: vec![],
filename: options
.input
.opt_path()
.map(ToOwned::to_owned)
.map(FileName::from)
.unwrap_or(FileName::Custom("input".to_owned())),
};
let codes = ErrorCodes::from(options.unstable_features.is_nightly_build());
find_testable_code(
&input_str,
&mut md_collector,
codes,
options.enable_per_target_ignores,
None,
);
let mut collector = CreateRunnableDocTests::new(options.clone(), opts);
md_collector.tests.into_iter().for_each(|t| collector.add_test(t));
let CreateRunnableDocTests { opts, rustdoc_options, standalone_tests, mergeable_tests, .. } =
collector;
crate::doctest::run_tests(
opts,
&rustdoc_options,
&Arc::new(Mutex::new(Vec::new())),
standalone_tests,
mergeable_tests,
);
Ok(())
}