]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/html/render/cache.rs
New upstream version 1.49.0~beta.4+dfsg1
[rustc.git] / src / librustdoc / html / render / cache.rs
CommitLineData
dfeec247 1use std::collections::BTreeMap;
3dfed10e 2use std::path::Path;
60c5eb7d 3
3dfed10e
XL
4use rustc_data_structures::fx::FxHashMap;
5use rustc_span::symbol::sym;
60c5eb7d 6use serde::Serialize;
e74abb32 7
3dfed10e
XL
8use crate::clean::types::GetDefId;
9use crate::clean::{self, AttributesExt};
10use crate::formats::cache::Cache;
11use crate::formats::item_type::ItemType;
1b1a35ee 12use crate::html::render::{plain_text_summary, shorten};
3dfed10e 13use crate::html::render::{Generic, IndexItem, IndexItemFunctionType, RenderType, TypeWithKind};
e74abb32
XL
14
15/// Indicates where an external crate can be found.
16pub enum ExternalLocation {
17 /// Remote URL root of the external crate
18 Remote(String),
19 /// This external crate can be found in the local doc/ folder
20 Local,
21 /// The external crate could not be found.
22 Unknown,
23}
24
e74abb32
XL
25/// Attempts to find where an external crate is located, given that we're
26/// rendering in to the specified source destination.
3dfed10e 27pub fn extern_location(
dfeec247
XL
28 e: &clean::ExternalCrate,
29 extern_url: Option<&str>,
30 dst: &Path,
31) -> ExternalLocation {
e74abb32
XL
32 use ExternalLocation::*;
33 // See if there's documentation generated into the local directory
34 let local_location = dst.join(&e.name);
35 if local_location.is_dir() {
36 return Local;
37 }
38
39 if let Some(url) = extern_url {
40 let mut url = url.to_string();
74b04a01 41 if !url.ends_with('/') {
e74abb32
XL
42 url.push('/');
43 }
44 return Remote(url);
45 }
46
47 // Failing that, see if there's an attribute specifying where to find this
48 // external crate
dfeec247
XL
49 e.attrs
50 .lists(sym::doc)
3dfed10e 51 .filter(|a| a.has_name(sym::html_root_url))
dfeec247
XL
52 .filter_map(|a| a.value_str())
53 .map(|url| {
54 let mut url = url.to_string();
74b04a01 55 if !url.ends_with('/') {
dfeec247
XL
56 url.push('/')
57 }
58 Remote(url)
59 })
60 .next()
61 .unwrap_or(Unknown) // Well, at least we tried.
e74abb32
XL
62}
63
64/// Builds the search index from the collected metadata
3dfed10e 65pub fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
74b04a01 66 let mut defid_to_pathid = FxHashMap::default();
e74abb32 67 let mut crate_items = Vec::with_capacity(cache.search_index.len());
60c5eb7d 68 let mut crate_paths = vec![];
e74abb32 69
f9f354fc
XL
70 let Cache { ref mut search_index, ref orphan_impl_items, ref paths, ref mut aliases, .. } =
71 *cache;
e74abb32
XL
72
73 // Attach all orphan items to the type's definition if the type
74 // has since been learned.
75 for &(did, ref item) in orphan_impl_items {
76 if let Some(&(ref fqp, _)) = paths.get(&did) {
77 search_index.push(IndexItem {
78 ty: item.type_(),
79 name: item.name.clone().unwrap(),
80 path: fqp[..fqp.len() - 1].join("::"),
1b1a35ee 81 desc: shorten(plain_text_summary(item.doc_value())),
e74abb32
XL
82 parent: Some(did),
83 parent_idx: None,
84 search_type: get_index_search_type(&item),
85 });
f9f354fc
XL
86 for alias in item.attrs.get_doc_aliases() {
87 aliases
88 .entry(alias.to_lowercase())
89 .or_insert(Vec::new())
90 .push(search_index.len() - 1);
91 }
e74abb32
XL
92 }
93 }
94
74b04a01 95 // Reduce `DefId` in paths into smaller sequential numbers,
e74abb32
XL
96 // and prune the paths that do not appear in the index.
97 let mut lastpath = String::new();
98 let mut lastpathid = 0usize;
99
100 for item in search_index {
ba9703b0 101 item.parent_idx = item.parent.and_then(|defid| {
74b04a01 102 if defid_to_pathid.contains_key(&defid) {
ba9703b0 103 defid_to_pathid.get(&defid).copied()
e74abb32
XL
104 } else {
105 let pathid = lastpathid;
74b04a01 106 defid_to_pathid.insert(defid, pathid);
e74abb32
XL
107 lastpathid += 1;
108
ba9703b0
XL
109 if let Some(&(ref fqp, short)) = paths.get(&defid) {
110 crate_paths.push((short, fqp.last().unwrap().clone()));
111 Some(pathid)
112 } else {
113 None
114 }
e74abb32
XL
115 }
116 });
117
118 // Omit the parent path if it is same to that of the prior item.
119 if lastpath == item.path {
120 item.path.clear();
121 } else {
122 lastpath = item.path.clone();
123 }
60c5eb7d 124 crate_items.push(&*item);
e74abb32
XL
125 }
126
dfeec247
XL
127 let crate_doc = krate
128 .module
129 .as_ref()
1b1a35ee 130 .map(|module| shorten(plain_text_summary(module.doc_value())))
29967ef6 131 .unwrap_or_default();
e74abb32 132
60c5eb7d
XL
133 #[derive(Serialize)]
134 struct CrateData<'a> {
135 doc: String,
136 #[serde(rename = "i")]
137 items: Vec<&'a IndexItem>,
138 #[serde(rename = "p")]
139 paths: Vec<(ItemType, String)>,
f9f354fc
XL
140 // The String is alias name and the vec is the list of the elements with this alias.
141 //
142 // To be noted: the `usize` elements are indexes to `items`.
143 #[serde(rename = "a")]
144 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
145 aliases: &'a BTreeMap<String, Vec<usize>>,
60c5eb7d 146 }
e74abb32
XL
147
148 // Collect the index into a string
60c5eb7d 149 format!(
ba9703b0 150 r#""{}":{}"#,
60c5eb7d
XL
151 krate.name,
152 serde_json::to_string(&CrateData {
153 doc: crate_doc,
154 items: crate_items,
155 paths: crate_paths,
f9f354fc 156 aliases,
60c5eb7d 157 })
dfeec247 158 .expect("failed serde conversion")
ba9703b0
XL
159 // All these `replace` calls are because we have to go through JS string for JSON content.
160 .replace(r"\", r"\\")
161 .replace("'", r"\'")
162 // We need to escape double quotes for the JSON.
163 .replace("\\\"", "\\\\\"")
60c5eb7d 164 )
e74abb32
XL
165}
166
3dfed10e 167crate fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> {
e74abb32
XL
168 let (all_types, ret_types) = match item.inner {
169 clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types),
170 clean::MethodItem(ref m) => (&m.all_types, &m.ret_types),
171 clean::TyMethodItem(ref m) => (&m.all_types, &m.ret_types),
172 _ => return None,
173 };
174
ba9703b0
XL
175 let inputs = all_types
176 .iter()
177 .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty), *kind)))
178 .filter(|a| a.ty.name.is_some())
179 .collect();
dfeec247
XL
180 let output = ret_types
181 .iter()
ba9703b0
XL
182 .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty), *kind)))
183 .filter(|a| a.ty.name.is_some())
dfeec247
XL
184 .collect::<Vec<_>>();
185 let output = if output.is_empty() { None } else { Some(output) };
e74abb32
XL
186
187 Some(IndexItemFunctionType { inputs, output })
188}
189
ba9703b0
XL
190fn get_index_type(clean_type: &clean::Type) -> RenderType {
191 RenderType {
192 ty: clean_type.def_id(),
193 idx: None,
e74abb32
XL
194 name: get_index_type_name(clean_type, true).map(|s| s.to_ascii_lowercase()),
195 generics: get_generics(clean_type),
ba9703b0 196 }
e74abb32
XL
197}
198
199fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<String> {
200 match *clean_type {
201 clean::ResolvedPath { ref path, .. } => {
202 let segments = &path.segments;
3dfed10e
XL
203 let path_segment = segments.iter().last().unwrap_or_else(|| {
204 panic!(
e74abb32
XL
205 "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path",
206 clean_type, accept_generic
3dfed10e
XL
207 )
208 });
e74abb32
XL
209 Some(path_segment.name.clone())
210 }
211 clean::Generic(ref s) if accept_generic => Some(s.clone()),
212 clean::Primitive(ref p) => Some(format!("{:?}", p)),
213 clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
214 // FIXME: add all from clean::Type.
dfeec247 215 _ => None,
e74abb32
XL
216 }
217}
218
ba9703b0 219fn get_generics(clean_type: &clean::Type) -> Option<Vec<Generic>> {
dfeec247
XL
220 clean_type.generics().and_then(|types| {
221 let r = types
222 .iter()
ba9703b0
XL
223 .filter_map(|t| {
224 get_index_type_name(t, false).map(|name| Generic {
225 name: name.to_ascii_lowercase(),
226 defid: t.def_id(),
227 idx: None,
228 })
229 })
dfeec247
XL
230 .collect::<Vec<_>>();
231 if r.is_empty() { None } else { Some(r) }
232 })
e74abb32 233}