1use std::cell::RefCell;
17use std::cmp::Ordering;
18use std::ffi::{OsStr, OsString};
19use std::fs::File;
20use std::io::{self, Write as _};
21use std::iter::once;
22use std::marker::PhantomData;
23use std::path::{Component, Path, PathBuf};
24use std::rc::{Rc, Weak};
25use std::str::FromStr;
26use std::{fmt, fs};
27
28use indexmap::IndexMap;
29use regex::Regex;
30use rustc_ast::join_path_syms;
31use rustc_data_structures::flock;
32use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
33use rustc_middle::ty::TyCtxt;
34use rustc_middle::ty::fast_reject::DeepRejectCtxt;
35use rustc_span::Symbol;
36use rustc_span::def_id::DefId;
37use serde::de::DeserializeOwned;
38use serde::ser::SerializeSeq;
39use serde::{Deserialize, Serialize, Serializer};
40
41use super::{Context, RenderMode, collect_paths_for_type, ensure_trailing_slash};
42use crate::clean::{Crate, Item, ItemId, ItemKind};
43use crate::config::{EmitType, PathToParts, RenderOptions, ShouldMerge};
44use crate::docfs::PathError;
45use crate::error::Error;
46use crate::formats::Impl;
47use crate::formats::item_type::ItemType;
48use crate::html::format::{print_impl, print_path};
49use crate::html::layout;
50use crate::html::render::ordered_json::{EscapedJson, OrderedJson};
51use crate::html::render::print_item::compare_names;
52use crate::html::render::search_index::{SerializedSearchIndex, build_index};
53use crate::html::render::sorted_template::{self, FileFormat, SortedTemplate};
54use crate::html::render::{AssocItemLink, ImplRenderingParameters, StylePath};
55use crate::html::static_files::{self, suffix_path};
56use crate::visit::DocVisitor;
57use crate::{try_err, try_none};
58
59pub(crate) fn write_shared(
60 cx: &mut Context<'_>,
61 krate: &Crate,
62 opt: &RenderOptions,
63 tcx: TyCtxt<'_>,
64) -> Result<(), Error> {
65 cx.shared.fs.set_sync_only(true);
67 let lock_file = cx.dst.join(".lock");
68 let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file);
70
71 let search_index = build_index(
72 krate,
73 &mut cx.shared.cache,
74 tcx,
75 &cx.dst,
76 &cx.shared.resource_suffix,
77 &opt.should_merge,
78 )?;
79
80 let crate_name = krate.name(cx.tcx());
81 let crate_name = crate_name.as_str(); let crate_name_json = OrderedJson::serialize(crate_name).unwrap(); let external_crates = hack_get_external_crate_names(&cx.dst, &cx.shared.resource_suffix)?;
84 let info = CrateInfo {
85 version: CrateInfoVersion::V2,
86 src_files_js: SourcesPart::get(cx, &crate_name_json)?,
87 search_index,
88 all_crates: AllCratesPart::get(crate_name_json.clone(), &cx.shared.resource_suffix)?,
89 crates_index: CratesIndexPart::get(crate_name, &external_crates)?,
90 trait_impl: TraitAliasPart::get(cx, &crate_name_json)?,
91 type_impl: TypeAliasPart::get(cx, krate, &crate_name_json)?,
92 };
93
94 if let Some(parts_out_dir) = &opt.parts_out_dir {
95 let mut parts_out_file = parts_out_dir.0.clone();
96 parts_out_file.push(&format!("{crate_name}.json"));
97 create_parents(&parts_out_file)?;
98 try_err!(
99 fs::write(&parts_out_file, serde_json::to_string(&info).unwrap()),
100 &parts_out_dir.0
101 );
102 }
103
104 let mut crates = CrateInfo::read_many(&opt.include_parts_dir)?;
105 crates.push(info);
106
107 if opt.should_merge.write_rendered_cci {
108 write_not_crate_specific(
109 &crates,
110 &cx.dst,
111 opt,
112 &cx.shared.style_files,
113 cx.shared.layout.css_file_extension.as_deref(),
114 &cx.shared.resource_suffix,
115 cx.info.include_sources,
116 )?;
117 match &opt.index_page {
118 Some(index_page) if opt.enable_index_page => {
119 let mut md_opts = opt.clone();
120 md_opts.output = cx.dst.clone();
121 md_opts.external_html = cx.shared.layout.external_html.clone();
122 try_err!(
123 crate::markdown::render_and_write(index_page, md_opts, cx.shared.edition()),
124 &index_page
125 );
126 }
127 None if opt.enable_index_page => {
128 write_rendered_cci::<CratesIndexPart, _>(
129 || CratesIndexPart::blank(cx),
130 &cx.dst,
131 &crates,
132 &opt.should_merge,
133 )?;
134 }
135 _ => {} }
137 }
138
139 cx.shared.fs.set_sync_only(false);
140 Ok(())
141}
142
143pub(crate) fn write_not_crate_specific(
147 crates: &[CrateInfo],
148 dst: &Path,
149 opt: &RenderOptions,
150 style_files: &[StylePath],
151 css_file_extension: Option<&Path>,
152 resource_suffix: &str,
153 include_sources: bool,
154) -> Result<(), Error> {
155 write_rendered_cross_crate_info(crates, dst, opt, include_sources, resource_suffix)?;
156 write_static_files(dst, opt, style_files, css_file_extension, resource_suffix)?;
157 Ok(())
158}
159
160fn write_rendered_cross_crate_info(
161 crates: &[CrateInfo],
162 dst: &Path,
163 opt: &RenderOptions,
164 include_sources: bool,
165 resource_suffix: &str,
166) -> Result<(), Error> {
167 let m = &opt.should_merge;
168 if opt.should_emit_crate() {
169 if include_sources {
170 write_rendered_cci::<SourcesPart, _>(SourcesPart::blank, dst, crates, m)?;
171 }
172 crates
173 .iter()
174 .fold(SerializedSearchIndex::default(), |a, b| a.union(&b.search_index))
175 .sort()
176 .write_to(dst, resource_suffix)?;
177 write_rendered_cci::<AllCratesPart, _>(AllCratesPart::blank, dst, crates, m)?;
178 }
179 write_rendered_cci::<TraitAliasPart, _>(TraitAliasPart::blank, dst, crates, m)?;
180 write_rendered_cci::<TypeAliasPart, _>(TypeAliasPart::blank, dst, crates, m)?;
181 Ok(())
182}
183
184fn write_static_files(
187 dst: &Path,
188 opt: &RenderOptions,
189 style_files: &[StylePath],
190 css_file_extension: Option<&Path>,
191 resource_suffix: &str,
192) -> Result<(), Error> {
193 let static_dir = dst.join("static.files");
194 try_err!(fs::create_dir_all(&static_dir), &static_dir);
195
196 for entry in style_files {
198 let theme = entry.basename()?;
199 let extension =
200 try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path);
201
202 if matches!(theme.as_str(), "light" | "dark" | "ayu") {
204 continue;
205 }
206
207 let bytes = try_err!(fs::read(&entry.path), &entry.path);
208 let filename = format!("{theme}{resource_suffix}.{extension}");
209 let dst_filename = dst.join(filename);
210 try_err!(fs::write(&dst_filename, bytes), &dst_filename);
211 }
212
213 if let Some(css) = css_file_extension {
216 let buffer = try_err!(fs::read_to_string(css), css);
217 let path = static_files::suffix_path("theme.css", resource_suffix);
218 let dst_path = dst.join(path);
219 try_err!(fs::write(&dst_path, buffer), &dst_path);
220 }
221
222 if opt.emit.is_empty() || opt.emit.contains(&EmitType::Toolchain) {
223 static_files::for_each(|f: &static_files::StaticFile| {
224 let filename = static_dir.join(f.output_filename());
225 let contents: &[u8] =
226 if opt.disable_minification { f.src_bytes } else { f.minified_bytes };
227 fs::write(&filename, contents).map_err(|e| PathError::new(e, &filename))
228 })?;
229 }
230
231 Ok(())
232}
233
234#[derive(Serialize, Deserialize, Clone, Debug)]
236pub(crate) struct CrateInfo {
237 version: CrateInfoVersion,
238 src_files_js: PartsAndLocations<SourcesPart>,
239 search_index: SerializedSearchIndex,
240 all_crates: PartsAndLocations<AllCratesPart>,
241 crates_index: PartsAndLocations<CratesIndexPart>,
242 trait_impl: PartsAndLocations<TraitAliasPart>,
243 type_impl: PartsAndLocations<TypeAliasPart>,
244}
245
246impl CrateInfo {
247 pub(crate) fn read_many(parts_paths: &[PathToParts]) -> Result<Vec<Self>, Error> {
249 parts_paths
250 .iter()
251 .fold(Ok(Vec::new()), |acc, parts_path| {
252 let mut acc = acc?;
253 let dir = &parts_path.0;
254 acc.append(&mut try_err!(std::fs::read_dir(dir), dir.as_path())
255 .filter_map(|file| {
256 let to_crate_info = |file: Result<std::fs::DirEntry, std::io::Error>| -> Result<Option<CrateInfo>, Error> {
257 let file = try_err!(file, dir.as_path());
258 if file.path().extension() != Some(OsStr::new("json")) {
259 return Ok(None);
260 }
261 let parts = try_err!(fs::read(file.path()), file.path());
262 let parts: CrateInfo = try_err!(serde_json::from_slice(&parts), file.path());
263 Ok(Some(parts))
264 };
265 to_crate_info(file).transpose()
266 })
267 .collect::<Result<Vec<CrateInfo>, Error>>()?);
268 Ok(acc)
269 })
270 }
271}
272
273#[derive(Serialize, Deserialize, Clone, Debug)]
281enum CrateInfoVersion {
282 V2,
283}
284
285#[derive(Serialize, Deserialize, Debug, Clone)]
287#[serde(transparent)]
288struct PartsAndLocations<P> {
289 parts: Vec<(PathBuf, P)>,
290}
291
292impl<P> Default for PartsAndLocations<P> {
293 fn default() -> Self {
294 Self { parts: Vec::default() }
295 }
296}
297
298impl<T, U> PartsAndLocations<Part<T, U>> {
299 fn push(&mut self, path: PathBuf, item: U) {
300 self.parts.push((path, Part { _artifact: PhantomData, item }));
301 }
302
303 fn with(path: PathBuf, part: U) -> Self {
305 let mut ret = Self::default();
306 ret.push(path, part);
307 ret
308 }
309}
310
311#[derive(Serialize, Deserialize, Debug, Clone)]
315#[serde(transparent)]
316struct Part<T, U> {
317 #[serde(skip)]
318 _artifact: PhantomData<T>,
319 item: U,
320}
321
322impl<T, U: fmt::Display> fmt::Display for Part<T, U> {
323 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325 write!(f, "{}", self.item)
326 }
327}
328
329trait CciPart: Sized + fmt::Display + DeserializeOwned + 'static {
331 type FileFormat: sorted_template::FileFormat;
333 fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self>;
334}
335
336#[derive(Serialize, Deserialize, Clone, Default, Debug)]
337struct AllCrates;
338type AllCratesPart = Part<AllCrates, OrderedJson>;
339impl CciPart for AllCratesPart {
340 type FileFormat = sorted_template::Js;
341 fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
342 &crate_info.all_crates
343 }
344}
345
346impl AllCratesPart {
347 fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
348 SortedTemplate::from_before_after("window.ALL_CRATES = [", "];")
349 }
350
351 fn get(
352 crate_name_json: OrderedJson,
353 resource_suffix: &str,
354 ) -> Result<PartsAndLocations<Self>, Error> {
355 let path = suffix_path("crates.js", resource_suffix);
358 Ok(PartsAndLocations::with(path, crate_name_json))
359 }
360}
361
362fn hack_get_external_crate_names(
369 doc_root: &Path,
370 resource_suffix: &str,
371) -> Result<Vec<String>, Error> {
372 let path = doc_root.join(suffix_path("crates.js", resource_suffix));
373 let Ok(content) = fs::read_to_string(&path) else {
374 return Ok(Vec::default());
376 };
377 let regex = Regex::new(r"\[.*\]").unwrap();
380 let Some(content) = regex.find(&content) else {
381 return Err(Error::new("could not find crates list in crates.js", path));
382 };
383 let content: Vec<String> = try_err!(serde_json::from_str(content.as_str()), &path);
384 Ok(content)
385}
386
387#[derive(Serialize, Deserialize, Clone, Default, Debug)]
388struct CratesIndex;
389type CratesIndexPart = Part<CratesIndex, String>;
390impl CciPart for CratesIndexPart {
391 type FileFormat = sorted_template::Html;
392 fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
393 &crate_info.crates_index
394 }
395}
396
397impl CratesIndexPart {
398 fn blank(cx: &Context<'_>) -> SortedTemplate<<Self as CciPart>::FileFormat> {
399 let page = layout::Page {
400 title: "Index of crates",
401 short_title: "Crates",
402 css_class: "mod sys",
403 root_path: "./",
404 static_root_path: cx.shared.static_root_path.as_deref(),
405 description: "List of crates",
406 resource_suffix: &cx.shared.resource_suffix,
407 rust_logo: true,
408 };
409 let layout = &cx.shared.layout;
410 let style_files = &cx.shared.style_files;
411 const DELIMITER: &str = "\u{FFFC}"; let content = format!(
413 "<div class=\"main-heading\">\
414 <h1>List of all crates</h1>\
415 <rustdoc-toolbar></rustdoc-toolbar>\
416 </div>\
417 <ul class=\"all-items\">{DELIMITER}</ul>"
418 );
419 let template = layout::render(layout, &page, "", content, style_files);
420 SortedTemplate::from_template(&template, DELIMITER)
421 .expect("Object Replacement Character (U+FFFC) should not appear in the --index-page")
422 }
423
424 fn get(crate_name: &str, external_crates: &[String]) -> Result<PartsAndLocations<Self>, Error> {
426 let mut ret = PartsAndLocations::default();
427 let path = Path::new("index.html");
428 for crate_name in external_crates.iter().map(|s| s.as_str()).chain(once(crate_name)) {
429 let part = format!(
430 "<li><a href=\"{trailing_slash}index.html\">{crate_name}</a></li>",
431 trailing_slash = ensure_trailing_slash(crate_name),
432 );
433 ret.push(path.to_path_buf(), part);
434 }
435 Ok(ret)
436 }
437}
438
439#[derive(Serialize, Deserialize, Clone, Default, Debug)]
440struct Sources;
441type SourcesPart = Part<Sources, EscapedJson>;
442impl CciPart for SourcesPart {
443 type FileFormat = sorted_template::Js;
444 fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
445 &crate_info.src_files_js
446 }
447}
448
449impl SourcesPart {
450 fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
451 SortedTemplate::from_before_after(r"createSrcSidebar('[", r"]');")
455 }
456
457 fn get(cx: &Context<'_>, crate_name: &OrderedJson) -> Result<PartsAndLocations<Self>, Error> {
458 let hierarchy = Rc::new(Hierarchy::default());
459 cx.shared
460 .local_sources
461 .iter()
462 .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok())
463 .for_each(|source| hierarchy.add_path(source));
464 let path = suffix_path("src-files.js", &cx.shared.resource_suffix);
465 let hierarchy = hierarchy.to_json_string();
466 let part = OrderedJson::array_unsorted([crate_name, &hierarchy]);
467 let part = EscapedJson::from(part);
468 Ok(PartsAndLocations::with(path, part))
469 }
470}
471
472#[derive(Debug, Default)]
474struct Hierarchy {
475 parent: Weak<Self>,
476 elem: OsString,
477 children: RefCell<FxIndexMap<OsString, Rc<Self>>>,
478 elems: RefCell<FxIndexSet<OsString>>,
479}
480
481impl Hierarchy {
482 fn with_parent(elem: OsString, parent: &Rc<Self>) -> Self {
483 Self { elem, parent: Rc::downgrade(parent), ..Self::default() }
484 }
485
486 fn to_json_string(&self) -> OrderedJson {
487 let subs = self.children.borrow();
488 let files = self.elems.borrow();
489 let name = OrderedJson::serialize(self.elem.to_str().expect("invalid osstring conversion"))
490 .unwrap();
491 let mut out = Vec::from([name]);
492 if !subs.is_empty() || !files.is_empty() {
493 let subs = subs.iter().map(|(_, s)| s.to_json_string());
494 out.push(OrderedJson::array_sorted(subs));
495 }
496 if !files.is_empty() {
497 let files = files
498 .iter()
499 .map(|s| OrderedJson::serialize(s.to_str().expect("invalid osstring")).unwrap());
500 out.push(OrderedJson::array_sorted(files));
501 }
502 OrderedJson::array_unsorted(out)
503 }
504
505 fn add_path(self: &Rc<Self>, path: &Path) {
506 let mut h = Rc::clone(self);
507 let mut elems = path
508 .components()
509 .filter_map(|s| match s {
510 Component::Normal(s) => Some(s.to_owned()),
511 Component::ParentDir => Some(OsString::from("..")),
512 _ => None,
513 })
514 .peekable();
515 loop {
516 let cur_elem = elems.next().expect("empty file path");
517 if cur_elem == ".." {
518 if let Some(parent) = h.parent.upgrade() {
519 h = parent;
520 }
521 continue;
522 }
523 if elems.peek().is_none() {
524 h.elems.borrow_mut().insert(cur_elem);
525 break;
526 } else {
527 let entry = Rc::clone(
528 h.children
529 .borrow_mut()
530 .entry(cur_elem.clone())
531 .or_insert_with(|| Rc::new(Self::with_parent(cur_elem, &h))),
532 );
533 h = entry;
534 }
535 }
536 }
537}
538
539#[derive(Serialize, Deserialize, Clone, Default, Debug)]
540struct TypeAlias;
541type TypeAliasPart = Part<TypeAlias, OrderedJson>;
542impl CciPart for TypeAliasPart {
543 type FileFormat = sorted_template::Js;
544 fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
545 &crate_info.type_impl
546 }
547}
548
549impl TypeAliasPart {
550 fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
551 SortedTemplate::from_before_after(
552 r"(function() {
553 var type_impls = Object.fromEntries([",
554 r"]);
555 if (window.register_type_impls) {
556 window.register_type_impls(type_impls);
557 } else {
558 window.pending_type_impls = type_impls;
559 }
560})()",
561 )
562 }
563
564 fn get(
565 cx: &mut Context<'_>,
566 krate: &Crate,
567 crate_name_json: &OrderedJson,
568 ) -> Result<PartsAndLocations<Self>, Error> {
569 let mut path_parts = PartsAndLocations::default();
570
571 let mut type_impl_collector = TypeImplCollector {
572 aliased_types: IndexMap::default(),
573 visited_aliases: FxHashSet::default(),
574 cx,
575 };
576 DocVisitor::visit_crate(&mut type_impl_collector, krate);
577 let cx = type_impl_collector.cx;
578 let aliased_types = type_impl_collector.aliased_types;
579 for aliased_type in aliased_types.values() {
580 let impls = aliased_type.impl_.values().filter_map(
581 |AliasedTypeImpl { impl_, type_aliases }| {
582 let mut ret: Option<AliasSerializableImpl> = None;
583 for &(type_alias_fqp, type_alias_item) in type_aliases {
587 cx.id_map.borrow_mut().clear();
588 cx.deref_id_map.borrow_mut().clear();
589 let type_alias_fqp = join_path_syms(type_alias_fqp);
590 if let Some(ret) = &mut ret {
591 ret.aliases.push(type_alias_fqp);
592 } else {
593 let target_trait_did =
594 impl_.inner_impl().trait_.as_ref().map(|trait_| trait_.def_id());
595 let provided_methods;
596 let assoc_link = if let Some(target_trait_did) = target_trait_did {
597 provided_methods =
598 impl_.inner_impl().provided_trait_methods(cx.tcx());
599 AssocItemLink::GotoSource(
600 ItemId::DefId(target_trait_did),
601 &provided_methods,
602 )
603 } else {
604 AssocItemLink::Anchor(None)
605 };
606 let text = super::render_impl(
607 cx,
608 impl_,
609 type_alias_item,
610 assoc_link,
611 RenderMode::Normal,
612 None,
613 &[],
614 ImplRenderingParameters {
615 show_def_docs: true,
616 show_default_items: true,
617 show_non_assoc_items: true,
618 toggle_open_by_default: true,
619 },
620 )
621 .to_string();
622 let trait_ = impl_
624 .inner_impl()
625 .trait_
626 .as_ref()
627 .map(|trait_| format!("{:#}", print_path(trait_, cx)));
628 ret = Some(AliasSerializableImpl {
629 text,
630 trait_,
631 aliases: vec![type_alias_fqp],
632 })
633 }
634 }
635 ret
636 },
637 );
638
639 let mut path = PathBuf::from("type.impl");
640 for component in &aliased_type.target_fqp[..aliased_type.target_fqp.len() - 1] {
641 path.push(component.as_str());
642 }
643 let aliased_item_type = aliased_type.target_type;
644 path.push(format!(
645 "{aliased_item_type}.{}.js",
646 aliased_type.target_fqp[aliased_type.target_fqp.len() - 1]
647 ));
648
649 let part = OrderedJson::array_sorted(
650 impls.map(|impl_| OrderedJson::serialize(impl_).unwrap()),
651 );
652 path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
653 }
654 Ok(path_parts)
655 }
656}
657
658#[derive(Serialize, Deserialize, Clone, Default, Debug)]
659struct TraitAlias;
660type TraitAliasPart = Part<TraitAlias, OrderedJson>;
661impl CciPart for TraitAliasPart {
662 type FileFormat = sorted_template::Js;
663 fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
664 &crate_info.trait_impl
665 }
666}
667
668impl TraitAliasPart {
669 fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
670 SortedTemplate::from_before_after(
671 r"(function() {
672 const implementors = Object.fromEntries([",
673 r"]);
674 if (window.register_implementors) {
675 window.register_implementors(implementors);
676 } else {
677 window.pending_implementors = implementors;
678 }
679})()",
680 )
681 }
682
683 fn get(
684 cx: &Context<'_>,
685 crate_name_json: &OrderedJson,
686 ) -> Result<PartsAndLocations<Self>, Error> {
687 let cache = &cx.shared.cache;
688 let mut path_parts = PartsAndLocations::default();
689 for (&did, imps) in &cache.implementors {
692 let (remote_path, remote_item_type) = match cache.exact_paths.get(&did) {
700 Some(p) => match cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) {
701 Some((_, t)) => (p, t),
702 None => continue,
703 },
704 None => match cache.external_paths.get(&did) {
705 Some((p, t)) => (p, t),
706 None => continue,
707 },
708 };
709
710 let mut implementors = imps
711 .iter()
712 .filter_map(|imp| {
713 if imp.impl_item.item_id.krate() == did.krate
721 || !imp.impl_item.item_id.is_local()
722 {
723 None
724 } else {
725 let impl_ = imp.inner_impl();
726 Some(Implementor {
727 text: print_impl(impl_, false, cx).to_string(),
728 synthetic: imp.inner_impl().kind.is_auto(),
729 types: collect_paths_for_type(&imp.inner_impl().for_, cache),
730 is_negative: impl_.is_negative_trait_impl(),
731 })
732 }
733 })
734 .peekable();
735
736 if implementors.peek().is_none() && !cache.paths.contains_key(&did) {
740 continue;
741 }
742
743 let mut path = PathBuf::from("trait.impl");
744 for component in &remote_path[..remote_path.len() - 1] {
745 path.push(component.as_str());
746 }
747 path.push(format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1]));
748
749 let mut implementors = implementors.collect::<Vec<_>>();
750 implementors.sort_unstable_by(|a, b| {
751 match (a.is_negative, b.is_negative) {
753 (false, true) => Ordering::Greater,
754 (true, false) => Ordering::Less,
755 _ => compare_names(&a.text, &b.text),
756 }
757 });
758
759 let part = OrderedJson::array_unsorted(
760 implementors
761 .iter()
762 .map(OrderedJson::serialize)
763 .collect::<Result<Vec<_>, _>>()
764 .unwrap(),
765 );
766 path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
767 }
768 Ok(path_parts)
769 }
770}
771
772struct Implementor {
773 text: String,
774 synthetic: bool,
775 types: Vec<String>,
776 is_negative: bool,
777}
778
779impl Serialize for Implementor {
780 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
781 where
782 S: Serializer,
783 {
784 let mut seq = serializer.serialize_seq(None)?;
785 seq.serialize_element(&self.text)?;
786 seq.serialize_element(if self.is_negative { &1 } else { &0 })?;
787 if self.synthetic {
788 seq.serialize_element(&1)?;
789 seq.serialize_element(&self.types)?;
790 }
791 seq.end()
792 }
793}
794
795struct TypeImplCollector<'cx, 'cache, 'item> {
803 aliased_types: IndexMap<DefId, AliasedType<'cache, 'item>>,
805 visited_aliases: FxHashSet<DefId>,
806 cx: &'cache Context<'cx>,
807}
808
809struct AliasedType<'cache, 'item> {
825 target_fqp: &'cache [Symbol],
827 target_type: ItemType,
828 impl_: IndexMap<ItemId, AliasedTypeImpl<'cache, 'item>>,
831}
832
833struct AliasedTypeImpl<'cache, 'item> {
838 impl_: &'cache Impl,
839 type_aliases: Vec<(&'cache [Symbol], &'item Item)>,
840}
841
842impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> {
843 fn visit_item(&mut self, it: &'item Item) {
844 self.visit_item_recur(it);
845 let cache = &self.cx.shared.cache;
846 let ItemKind::TypeAliasItem(ref t) = it.kind else { return };
847 let Some(self_did) = it.item_id.as_def_id() else { return };
848 if !self.visited_aliases.insert(self_did) {
849 return;
850 }
851 let Some(target_did) = t.type_.def_id(cache) else { return };
852 let get_extern = { || cache.external_paths.get(&target_did) };
853 let Some(&(ref target_fqp, target_type)) = cache.paths.get(&target_did).or_else(get_extern)
854 else {
855 return;
856 };
857 let aliased_type = self.aliased_types.entry(target_did).or_insert_with(|| {
858 let impl_ = cache
859 .impls
860 .get(&target_did)
861 .into_iter()
862 .flatten()
863 .map(|impl_| {
864 (impl_.impl_item.item_id, AliasedTypeImpl { impl_, type_aliases: Vec::new() })
865 })
866 .collect();
867 AliasedType { target_fqp: &target_fqp[..], target_type, impl_ }
868 });
869 let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) };
870 let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) else {
871 return;
872 };
873 let aliased_ty = self.cx.tcx().type_of(self_did).skip_binder();
874 let mut seen_impls: FxHashSet<ItemId> =
878 cache.impls.get(&self_did).into_iter().flatten().map(|i| i.impl_item.item_id).collect();
879 for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ {
880 let Some(impl_did) = impl_item_id.as_def_id() else { continue };
887 let for_ty = self.cx.tcx().type_of(impl_did).skip_binder();
888 let reject_cx = DeepRejectCtxt::relate_infer_infer(self.cx.tcx());
889 if !reject_cx.types_may_unify(aliased_ty, for_ty) {
890 continue;
891 }
892 if !seen_impls.insert(*impl_item_id) {
894 continue;
895 }
896 aliased_type_impl.type_aliases.push((&self_fqp[..], it));
898 }
899 }
900}
901
902struct AliasSerializableImpl {
904 text: String,
905 trait_: Option<String>,
906 aliases: Vec<String>,
907}
908
909impl Serialize for AliasSerializableImpl {
910 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
911 where
912 S: Serializer,
913 {
914 let mut seq = serializer.serialize_seq(None)?;
915 seq.serialize_element(&self.text)?;
916 if let Some(trait_) = &self.trait_ {
917 seq.serialize_element(trait_)?;
918 } else {
919 seq.serialize_element(&0)?;
920 }
921 for type_ in &self.aliases {
922 seq.serialize_element(type_)?;
923 }
924 seq.end()
925 }
926}
927
928fn get_path_parts<T: CciPart>(
929 dst: &Path,
930 crates_info: &[CrateInfo],
931) -> FxIndexMap<PathBuf, Vec<String>> {
932 let mut templates: FxIndexMap<PathBuf, Vec<String>> = FxIndexMap::default();
933 crates_info.iter().flat_map(|crate_info| T::from_crate_info(crate_info).parts.iter()).for_each(
934 |(path, part)| {
935 let path = dst.join(path);
936 let part = part.to_string();
937 templates.entry(path).or_default().push(part);
938 },
939 );
940 templates
941}
942
943fn create_parents(path: &Path) -> Result<(), Error> {
945 let parent = path.parent().expect("should not have an empty path here");
946 try_err!(fs::create_dir_all(parent), parent);
947 Ok(())
948}
949
950fn read_template_or_blank<F, T: FileFormat>(
952 mut make_blank: F,
953 path: &Path,
954 should_merge: &ShouldMerge,
955) -> Result<SortedTemplate<T>, Error>
956where
957 F: FnMut() -> SortedTemplate<T>,
958{
959 if !should_merge.read_rendered_cci {
960 return Ok(make_blank());
961 }
962 match fs::read_to_string(path) {
963 Ok(template) => Ok(try_err!(SortedTemplate::from_str(&template), &path)),
964 Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(make_blank()),
965 Err(e) => Err(Error::new(e, path)),
966 }
967}
968
969fn write_rendered_cci<T: CciPart, F>(
971 mut make_blank: F,
972 dst: &Path,
973 crates_info: &[CrateInfo],
974 should_merge: &ShouldMerge,
975) -> Result<(), Error>
976where
977 F: FnMut() -> SortedTemplate<T::FileFormat>,
978{
979 for (path, parts) in get_path_parts::<T>(dst, crates_info) {
981 create_parents(&path)?;
982 let mut template =
984 read_template_or_blank::<_, T::FileFormat>(&mut make_blank, &path, should_merge)?;
985 for part in parts {
986 template.append(part);
987 }
988 let mut file = try_err!(File::create_buffered(&path), &path);
989 try_err!(write!(file, "{template}"), &path);
990 try_err!(file.flush(), &path);
991 }
992 Ok(())
993}
994
995#[cfg(test)]
996mod tests;