1 use std::collections::BTreeMap;
4 use rustc_data_structures::fx::FxHashMap;
5 use rustc_span::symbol::{sym, Symbol};
8 use crate::clean::types::GetDefId;
9 use crate::clean::{self, AttributesExt};
10 use crate::formats::cache::Cache;
11 use crate::formats::item_type::ItemType;
12 use crate::html::markdown::short_markdown_summary;
13 use crate::html::render::{Generic, IndexItem, IndexItemFunctionType, RenderType, TypeWithKind};
15 /// Indicates where an external crate can be found.
16 crate enum ExternalLocation {
17 /// Remote URL root of the external crate
19 /// This external crate can be found in the local doc/ folder
21 /// The external crate could not be found.
25 /// Attempts to find where an external crate is located, given that we're
26 /// rendering in to the specified source destination.
27 crate fn extern_location(
28 e: &clean::ExternalCrate,
29 extern_url: Option<&str>,
31 ) -> ExternalLocation {
32 use ExternalLocation::*;
33 // See if there's documentation generated into the local directory
34 let local_location = dst.join(&*e.name.as_str());
35 if local_location.is_dir() {
39 if let Some(url) = extern_url {
40 let mut url = url.to_string();
41 if !url.ends_with('/') {
47 // Failing that, see if there's an attribute specifying where to find this
51 .filter(|a| a.has_name(sym::html_root_url))
52 .filter_map(|a| a.value_str())
54 let mut url = url.to_string();
55 if !url.ends_with('/') {
61 .unwrap_or(Unknown) // Well, at least we tried.
64 /// Builds the search index from the collected metadata
65 crate fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
66 let mut defid_to_pathid = FxHashMap::default();
67 let mut crate_items = Vec::with_capacity(cache.search_index.len());
68 let mut crate_paths = vec![];
70 // Attach all orphan items to the type's definition if the type
71 // has since been learned.
72 for &(did, ref item) in &cache.orphan_impl_items {
73 if let Some(&(ref fqp, _)) = cache.paths.get(&did) {
74 cache.search_index.push(IndexItem {
76 name: item.name.unwrap().to_string(),
77 path: fqp[..fqp.len() - 1].join("::"),
78 desc: item.doc_value().map_or_else(String::new, |s| short_markdown_summary(&s)),
81 search_type: get_index_search_type(&item, None),
83 for alias in item.attrs.get_doc_aliases() {
86 .entry(alias.to_lowercase())
87 .or_insert(Vec::new())
88 .push(cache.search_index.len() - 1);
93 let Cache { ref mut search_index, ref paths, ref mut aliases, .. } = *cache;
95 // Reduce `DefId` in paths into smaller sequential numbers,
96 // and prune the paths that do not appear in the index.
97 let mut lastpath = String::new();
98 let mut lastpathid = 0usize;
100 for item in search_index {
101 item.parent_idx = item.parent.and_then(|defid| {
102 if defid_to_pathid.contains_key(&defid) {
103 defid_to_pathid.get(&defid).copied()
105 let pathid = lastpathid;
106 defid_to_pathid.insert(defid, pathid);
109 if let Some(&(ref fqp, short)) = paths.get(&defid) {
110 crate_paths.push((short, fqp.last().unwrap().clone()));
118 // Omit the parent path if it is same to that of the prior item.
119 if lastpath == item.path {
122 lastpath = item.path.clone();
124 crate_items.push(&*item);
127 let crate_doc = krate
130 .map(|module| module.doc_value().map_or_else(String::new, |s| short_markdown_summary(&s)))
131 .unwrap_or_default();
134 struct CrateData<'a> {
136 #[serde(rename = "i")]
137 items: Vec<&'a IndexItem>,
138 #[serde(rename = "p")]
139 paths: Vec<(ItemType, String)>,
140 // The String is alias name and the vec is the list of the elements with this alias.
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>>,
148 // Collect the index into a string
152 serde_json::to_string(&CrateData {
158 .expect("failed serde conversion")
159 // All these `replace` calls are because we have to go through JS string for JSON content.
160 .replace(r"\", r"\\")
162 // We need to escape double quotes for the JSON.
163 .replace("\\\"", "\\\\\"")
167 crate fn get_index_search_type(
169 cache: Option<&Cache>,
170 ) -> Option<IndexItemFunctionType> {
171 let (all_types, ret_types) = match *item.kind {
172 clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types),
173 clean::MethodItem(ref m, _) => (&m.all_types, &m.ret_types),
174 clean::TyMethodItem(ref m) => (&m.all_types, &m.ret_types),
178 let inputs = all_types
180 .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty, &cache), *kind)))
181 .filter(|a| a.ty.name.is_some())
183 let output = ret_types
185 .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty, &cache), *kind)))
186 .filter(|a| a.ty.name.is_some())
187 .collect::<Vec<_>>();
188 let output = if output.is_empty() { None } else { Some(output) };
190 Some(IndexItemFunctionType { inputs, output })
193 fn get_index_type(clean_type: &clean::Type, cache: &Option<&Cache>) -> RenderType {
195 ty: cache.map_or_else(|| clean_type.def_id(), |cache| clean_type.def_id_full(cache)),
197 name: get_index_type_name(clean_type, true).map(|s| s.as_str().to_ascii_lowercase()),
198 generics: get_generics(clean_type, cache),
202 fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<Symbol> {
204 clean::ResolvedPath { ref path, .. } => {
205 let segments = &path.segments;
206 let path_segment = segments.iter().last().unwrap_or_else(|| {
208 "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path",
209 clean_type, accept_generic
212 Some(path_segment.name)
214 clean::Generic(s) if accept_generic => Some(s),
215 clean::Primitive(ref p) => Some(p.as_sym()),
216 clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
217 // FIXME: add all from clean::Type.
222 fn get_generics(clean_type: &clean::Type, cache: &Option<&Cache>) -> Option<Vec<Generic>> {
223 clean_type.generics().and_then(|types| {
227 get_index_type_name(t, false).map(|name| Generic {
228 name: name.as_str().to_ascii_lowercase(),
229 defid: cache.map_or_else(|| t.def_id(), |cache| t.def_id_full(cache)),
233 .collect::<Vec<_>>();
234 if r.is_empty() { None } else { Some(r) }