rustdoc/
externalfiles.rs

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    /// Content that will be included inline in the `<head>` section of a
13    /// rendered Markdown file or generated documentation
14    pub(crate) in_header: String,
15    /// Content that will be included inline between `<body>` and the content of
16    /// a rendered Markdown file or generated documentation
17    pub(crate) before_content: String,
18    /// Content that will be included inline between the content and `</body>` of
19    /// a rendered Markdown file or generated documentation
20    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}