]> git.proxmox.com Git - cargo.git/blob - src/cargo/core/compiler/rustdoc.rs
fix clippy warnings
[cargo.git] / src / cargo / core / compiler / rustdoc.rs
1 //! Utilities for building with rustdoc.
2
3 use crate::core::compiler::context::Context;
4 use crate::core::compiler::unit::Unit;
5 use crate::core::compiler::CompileKind;
6 use crate::sources::CRATES_IO_REGISTRY;
7 use crate::util::errors::{internal, CargoResult};
8 use crate::util::ProcessBuilder;
9 use std::collections::HashMap;
10 use std::fmt;
11 use std::hash;
12 use url::Url;
13
14 /// Mode used for `std`.
15 #[derive(Debug, Hash)]
16 pub enum RustdocExternMode {
17 /// Use a local `file://` URL.
18 Local,
19 /// Use a remote URL to https://doc.rust-lang.org/ (default).
20 Remote,
21 /// An arbitrary URL.
22 Url(String),
23 }
24
25 impl From<String> for RustdocExternMode {
26 fn from(s: String) -> RustdocExternMode {
27 match s.as_ref() {
28 "local" => RustdocExternMode::Local,
29 "remote" => RustdocExternMode::Remote,
30 _ => RustdocExternMode::Url(s),
31 }
32 }
33 }
34
35 impl fmt::Display for RustdocExternMode {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 match self {
38 RustdocExternMode::Local => "local".fmt(f),
39 RustdocExternMode::Remote => "remote".fmt(f),
40 RustdocExternMode::Url(s) => s.fmt(f),
41 }
42 }
43 }
44
45 impl<'de> serde::de::Deserialize<'de> for RustdocExternMode {
46 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
47 where
48 D: serde::de::Deserializer<'de>,
49 {
50 let s = String::deserialize(deserializer)?;
51 Ok(s.into())
52 }
53 }
54
55 #[derive(serde::Deserialize, Debug)]
56 pub struct RustdocExternMap {
57 registries: HashMap<String, String>,
58 std: Option<RustdocExternMode>,
59 }
60
61 impl hash::Hash for RustdocExternMap {
62 fn hash<H: hash::Hasher>(&self, into: &mut H) {
63 self.std.hash(into);
64 for (key, value) in &self.registries {
65 key.hash(into);
66 value.hash(into);
67 }
68 }
69 }
70
71 pub fn add_root_urls(
72 cx: &Context<'_, '_>,
73 unit: &Unit,
74 rustdoc: &mut ProcessBuilder,
75 ) -> CargoResult<()> {
76 let config = cx.bcx.config;
77 if !config.cli_unstable().rustdoc_map {
78 log::debug!("`doc.extern-map` ignored, requires -Zrustdoc-map flag");
79 return Ok(());
80 }
81 let map = config.doc_extern_map()?;
82 if map.registries.is_empty() && map.std.is_none() {
83 // Skip doing unnecessary work.
84 return Ok(());
85 }
86 let mut unstable_opts = false;
87 // Collect mapping of registry name -> index url.
88 let name2url: HashMap<&String, Url> = map
89 .registries
90 .keys()
91 .filter_map(|name| {
92 if let Ok(index_url) = config.get_registry_index(name) {
93 Some((name, index_url))
94 } else {
95 log::warn!(
96 "`doc.extern-map.{}` specifies a registry that is not defined",
97 name
98 );
99 None
100 }
101 })
102 .collect();
103 for dep in cx.unit_deps(unit) {
104 if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
105 for (registry, location) in &map.registries {
106 let sid = dep.unit.pkg.package_id().source_id();
107 let matches_registry = || -> bool {
108 if !sid.is_registry() {
109 return false;
110 }
111 if sid.is_default_registry() {
112 return registry == CRATES_IO_REGISTRY;
113 }
114 if let Some(index_url) = name2url.get(registry) {
115 return index_url == sid.url();
116 }
117 false
118 };
119 if matches_registry() {
120 let mut url = location.clone();
121 if !url.contains("{pkg_name}") && !url.contains("{version}") {
122 if !url.ends_with('/') {
123 url.push('/');
124 }
125 url.push_str("{pkg_name}/{version}/");
126 }
127 let url = url
128 .replace("{pkg_name}", &dep.unit.pkg.name())
129 .replace("{version}", &dep.unit.pkg.version().to_string());
130 rustdoc.arg("--extern-html-root-url");
131 rustdoc.arg(format!("{}={}", dep.unit.target.crate_name(), url));
132 unstable_opts = true;
133 }
134 }
135 }
136 }
137 let std_url = match &map.std {
138 None | Some(RustdocExternMode::Remote) => None,
139 Some(RustdocExternMode::Local) => {
140 let sysroot = &cx.bcx.target_data.info(CompileKind::Host).sysroot;
141 let html_root = sysroot.join("share").join("doc").join("rust").join("html");
142 if html_root.exists() {
143 let url = Url::from_file_path(&html_root).map_err(|()| {
144 internal(format!(
145 "`{}` failed to convert to URL",
146 html_root.display()
147 ))
148 })?;
149 Some(url.to_string())
150 } else {
151 log::warn!(
152 "`doc.extern-map.std` is \"local\", but local docs don't appear to exist at {}",
153 html_root.display()
154 );
155 None
156 }
157 }
158 Some(RustdocExternMode::Url(s)) => Some(s.to_string()),
159 };
160 if let Some(url) = std_url {
161 for name in &["std", "core", "alloc", "proc_macro"] {
162 rustdoc.arg("--extern-html-root-url");
163 rustdoc.arg(format!("{}={}", name, url));
164 unstable_opts = true;
165 }
166 }
167
168 if unstable_opts {
169 rustdoc.arg("-Zunstable-options");
170 }
171 Ok(())
172 }