Skip to main content

sourcey_rustdoc/
links.rs

1use rustdoc_types::{Crate, Id, ItemKind};
2use std::collections::HashMap;
3
4use crate::spec::{ExternalCrateRef, ItemId};
5
6/// Build a lookup table for intra-doc link resolution.
7/// Given an Item.links entry (label -> Id), we want the full path so the
8/// renderer can produce a sourcey URL or fall back to an external one.
9pub struct LinkContext<'a> {
10    pub krate: &'a Crate,
11    pub external_crate_index: HashMap<u32, ExternalCrateRef>,
12}
13
14impl<'a> LinkContext<'a> {
15    pub fn new(krate: &'a Crate) -> Self {
16        let external_crate_index = krate
17            .external_crates
18            .iter()
19            .map(|(crate_id, ec)| {
20                (
21                    *crate_id,
22                    ExternalCrateRef {
23                        crate_id: *crate_id,
24                        name: ec.name.clone(),
25                        html_root_url: ec.html_root_url.clone(),
26                    },
27                )
28            })
29            .collect();
30        Self {
31            krate,
32            external_crate_index,
33        }
34    }
35
36    pub fn external_crates(&self) -> Vec<ExternalCrateRef> {
37        let mut out: Vec<_> = self.external_crate_index.values().cloned().collect();
38        out.sort_by(|a, b| a.crate_id.cmp(&b.crate_id));
39        out
40    }
41
42    pub fn resolve_id(&self, id: &Id) -> Option<ResolvedLink> {
43        if let Some(summary) = self.krate.paths.get(id) {
44            let crate_id = summary.crate_id;
45            let path = summary.path.clone();
46            let kind = summary.kind.clone();
47            let external = crate_id != 0;
48            let html_root_url = if external {
49                self.external_crate_index
50                    .get(&crate_id)
51                    .and_then(|ec| ec.html_root_url.clone())
52            } else {
53                None
54            };
55            return Some(ResolvedLink {
56                id: ItemId(format_id(id)),
57                crate_id,
58                path,
59                kind,
60                external,
61                html_root_url,
62            });
63        }
64        if let Some(item) = self.krate.index.get(id) {
65            // Sub-items (struct fields, variants, methods) frequently lack a
66            // paths entry. Best effort: surface what we know.
67            return Some(ResolvedLink {
68                id: ItemId(format_id(id)),
69                crate_id: item.crate_id,
70                path: item.name.clone().map(|n| vec![n]).unwrap_or_default(),
71                kind: classify_inner_kind(item),
72                external: item.crate_id != 0,
73                html_root_url: self
74                    .external_crate_index
75                    .get(&item.crate_id)
76                    .and_then(|ec| ec.html_root_url.clone()),
77            });
78        }
79        None
80    }
81}
82
83#[derive(Debug, Clone)]
84pub struct ResolvedLink {
85    pub id: ItemId,
86    pub crate_id: u32,
87    pub path: Vec<String>,
88    pub kind: ItemKind,
89    pub external: bool,
90    pub html_root_url: Option<String>,
91}
92
93pub fn format_id(id: &Id) -> String {
94    format!("{}", id.0)
95}
96
97fn classify_inner_kind(item: &rustdoc_types::Item) -> ItemKind {
98    use rustdoc_types::ItemEnum;
99    match &item.inner {
100        ItemEnum::Module(_) => ItemKind::Module,
101        ItemEnum::Struct(_) => ItemKind::Struct,
102        ItemEnum::StructField(_) => ItemKind::StructField,
103        ItemEnum::Union(_) => ItemKind::Union,
104        ItemEnum::Enum(_) => ItemKind::Enum,
105        ItemEnum::Variant(_) => ItemKind::Variant,
106        ItemEnum::Function(_) => ItemKind::Function,
107        ItemEnum::Trait(_) => ItemKind::Trait,
108        ItemEnum::TraitAlias(_) => ItemKind::TraitAlias,
109        ItemEnum::Impl(_) => ItemKind::Impl,
110        ItemEnum::TypeAlias(_) => ItemKind::TypeAlias,
111        ItemEnum::Constant { .. } => ItemKind::Constant,
112        ItemEnum::Static(_) => ItemKind::Static,
113        ItemEnum::Macro(_) => ItemKind::Macro,
114        ItemEnum::ProcMacro(pm) => match pm.kind {
115            rustdoc_types::MacroKind::Bang => ItemKind::Macro,
116            rustdoc_types::MacroKind::Attr => ItemKind::ProcAttribute,
117            rustdoc_types::MacroKind::Derive => ItemKind::ProcDerive,
118        },
119        ItemEnum::AssocConst { .. } => ItemKind::AssocConst,
120        ItemEnum::AssocType { .. } => ItemKind::AssocType,
121        ItemEnum::ExternCrate { .. } => ItemKind::ExternCrate,
122        ItemEnum::Use(_) => ItemKind::Use,
123        ItemEnum::Primitive(_) => ItemKind::Primitive,
124        ItemEnum::ExternType => ItemKind::ExternType,
125    }
126}