1use std::path::{Path, PathBuf};
2use std::{fs, str};
3
4use rustc_errors::DiagCtxtHandle;
5use rustc_span::edition::Edition;
6use serde::Serialize;
7
8use crate::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, Playground};
9
10#[derive(Clone, Debug, Serialize)]
11pub(crate) struct ExternalHtml {
12 pub(crate) in_header: String,
15 pub(crate) before_content: String,
18 pub(crate) after_content: String,
21}
22
23impl ExternalHtml {
24 pub(crate) fn load(
25 in_header: &[String],
26 before_content: &[String],
27 after_content: &[String],
28 md_before_content: &[String],
29 md_after_content: &[String],
30 nightly_build: bool,
31 dcx: DiagCtxtHandle<'_>,
32 id_map: &mut IdMap,
33 edition: Edition,
34 playground: &Option<Playground>,
35 loaded_paths: &mut Vec<PathBuf>,
36 ) -> Option<ExternalHtml> {
37 let codes = ErrorCodes::from(nightly_build);
38 let ih = load_external_files(in_header, dcx, loaded_paths)?;
39 let bc = {
40 let mut bc = load_external_files(before_content, dcx, loaded_paths)?;
41 let m_bc = load_external_files(md_before_content, dcx, loaded_paths)?;
42 Markdown {
43 content: &m_bc,
44 links: &[],
45 ids: id_map,
46 error_codes: codes,
47 edition,
48 playground,
49 heading_offset: HeadingOffset::H2,
50 }
51 .write_into(&mut bc)
52 .unwrap();
53 bc
54 };
55 let ac = {
56 let mut ac = load_external_files(after_content, dcx, loaded_paths)?;
57 let m_ac = load_external_files(md_after_content, dcx, loaded_paths)?;
58 Markdown {
59 content: &m_ac,
60 links: &[],
61 ids: id_map,
62 error_codes: codes,
63 edition,
64 playground,
65 heading_offset: HeadingOffset::H2,
66 }
67 .write_into(&mut ac)
68 .unwrap();
69 ac
70 };
71 Some(ExternalHtml { in_header: ih, before_content: bc, after_content: ac })
72 }
73}
74
75pub(crate) enum LoadStringError {
76 ReadFail,
77 BadUtf8,
78}
79
80pub(crate) fn load_string<P: AsRef<Path>>(
81 file_path: P,
82 dcx: DiagCtxtHandle<'_>,
83 loaded_paths: &mut Vec<PathBuf>,
84) -> Result<String, LoadStringError> {
85 let file_path = file_path.as_ref();
86 loaded_paths.push(file_path.to_owned());
87 let contents = match fs::read(file_path) {
88 Ok(bytes) => bytes,
89 Err(e) => {
90 dcx.struct_err(format!(
91 "error reading `{file_path}`: {e}",
92 file_path = file_path.display()
93 ))
94 .emit();
95 return Err(LoadStringError::ReadFail);
96 }
97 };
98 match str::from_utf8(&contents) {
99 Ok(s) => Ok(s.to_string()),
100 Err(_) => {
101 dcx.err(format!("error reading `{}`: not UTF-8", file_path.display()));
102 Err(LoadStringError::BadUtf8)
103 }
104 }
105}
106
107fn load_external_files(
108 names: &[String],
109 dcx: DiagCtxtHandle<'_>,
110 loaded_paths: &mut Vec<PathBuf>,
111) -> Option<String> {
112 let mut out = String::new();
113 for name in names {
114 let Ok(s) = load_string(name, dcx, loaded_paths) else { return None };
115 out.push_str(&s);
116 out.push('\n');
117 }
118 Some(out)
119}