]>
Commit | Line | Data |
---|---|---|
fc512014 XL |
1 | //! Rustdoc's JSON backend |
2 | //! | |
3 | //! This module contains the logic for rendering a crate as JSON rather than the normal static HTML | |
4 | //! output. See [the RFC](https://github.com/rust-lang/rfcs/pull/2963) and the [`types`] module | |
5 | //! docs for usage and details. | |
6 | ||
7 | mod conversions; | |
fc512014 XL |
8 | |
9 | use std::cell::RefCell; | |
5099ac24 FG |
10 | use std::fs::{create_dir_all, File}; |
11 | use std::io::{BufWriter, Write}; | |
fc512014 XL |
12 | use std::path::PathBuf; |
13 | use std::rc::Rc; | |
14 | ||
15 | use rustc_data_structures::fx::FxHashMap; | |
17df50a5 | 16 | use rustc_hir::def_id::DefId; |
5869c6ff | 17 | use rustc_middle::ty::TyCtxt; |
fc512014 | 18 | use rustc_session::Session; |
fc512014 | 19 | |
5869c6ff XL |
20 | use rustdoc_json_types as types; |
21 | ||
a2a8927a | 22 | use crate::clean::types::{ExternalCrate, ExternalLocation}; |
6a06907d | 23 | use crate::config::RenderOptions; |
5099ac24 | 24 | use crate::docfs::PathError; |
3dfed10e XL |
25 | use crate::error::Error; |
26 | use crate::formats::cache::Cache; | |
27 | use crate::formats::FormatRenderer; | |
136023e0 | 28 | use crate::json::conversions::{from_item_id, IntoWithTcx}; |
5099ac24 | 29 | use crate::{clean, try_err}; |
3dfed10e XL |
30 | |
31 | #[derive(Clone)] | |
fc512014 | 32 | crate struct JsonRenderer<'tcx> { |
5869c6ff | 33 | tcx: TyCtxt<'tcx>, |
fc512014 XL |
34 | /// A mapping of IDs that contains all local items for this crate which gets output as a top |
35 | /// level field of the JSON blob. | |
36 | index: Rc<RefCell<FxHashMap<types::Id, types::Item>>>, | |
37 | /// The directory where the blob will be written to. | |
38 | out_path: PathBuf, | |
5869c6ff | 39 | cache: Rc<Cache>, |
fc512014 XL |
40 | } |
41 | ||
a2a8927a | 42 | impl<'tcx> JsonRenderer<'tcx> { |
6a06907d | 43 | fn sess(&self) -> &'tcx Session { |
fc512014 XL |
44 | self.tcx.sess |
45 | } | |
3dfed10e | 46 | |
17df50a5 | 47 | fn get_trait_implementors(&mut self, id: DefId) -> Vec<types::Id> { |
5869c6ff | 48 | Rc::clone(&self.cache) |
fc512014 XL |
49 | .implementors |
50 | .get(&id) | |
51 | .map(|implementors| { | |
52 | implementors | |
53 | .iter() | |
54 | .map(|i| { | |
55 | let item = &i.impl_item; | |
5869c6ff | 56 | self.item(item.clone()).unwrap(); |
136023e0 | 57 | from_item_id(item.def_id) |
fc512014 XL |
58 | }) |
59 | .collect() | |
60 | }) | |
61 | .unwrap_or_default() | |
62 | } | |
63 | ||
17df50a5 | 64 | fn get_impls(&mut self, id: DefId) -> Vec<types::Id> { |
5869c6ff | 65 | Rc::clone(&self.cache) |
fc512014 XL |
66 | .impls |
67 | .get(&id) | |
68 | .map(|impls| { | |
69 | impls | |
70 | .iter() | |
71 | .filter_map(|i| { | |
72 | let item = &i.impl_item; | |
c295e0f8 XL |
73 | |
74 | // HACK(hkmatsumoto): For impls of primitive types, we index them | |
75 | // regardless of whether they're local. This is because users can | |
76 | // document primitive items in an arbitrary crate by using | |
77 | // `doc(primitive)`. | |
78 | let mut is_primitive_impl = false; | |
79 | if let clean::types::ItemKind::ImplItem(ref impl_) = *item.kind { | |
80 | if impl_.trait_.is_none() { | |
81 | if let clean::types::Type::Primitive(_) = impl_.for_ { | |
82 | is_primitive_impl = true; | |
83 | } | |
84 | } | |
85 | } | |
86 | ||
87 | if item.def_id.is_local() || is_primitive_impl { | |
5869c6ff | 88 | self.item(item.clone()).unwrap(); |
136023e0 | 89 | Some(from_item_id(item.def_id)) |
fc512014 XL |
90 | } else { |
91 | None | |
92 | } | |
93 | }) | |
94 | .collect() | |
95 | }) | |
96 | .unwrap_or_default() | |
97 | } | |
98 | ||
5869c6ff XL |
99 | fn get_trait_items(&mut self) -> Vec<(types::Id, types::Item)> { |
100 | Rc::clone(&self.cache) | |
fc512014 XL |
101 | .traits |
102 | .iter() | |
103 | .filter_map(|(&id, trait_item)| { | |
104 | // only need to synthesize items for external traits | |
105 | if !id.is_local() { | |
6a06907d | 106 | let trait_item = &trait_item.trait_; |
5869c6ff | 107 | trait_item.items.clone().into_iter().for_each(|i| self.item(i).unwrap()); |
fc512014 | 108 | Some(( |
136023e0 | 109 | from_item_id(id.into()), |
fc512014 | 110 | types::Item { |
136023e0 | 111 | id: from_item_id(id.into()), |
fc512014 | 112 | crate_id: id.krate.as_u32(), |
5869c6ff XL |
113 | name: self |
114 | .cache | |
fc512014 XL |
115 | .paths |
116 | .get(&id) | |
117 | .unwrap_or_else(|| { | |
5869c6ff | 118 | self.cache |
fc512014 XL |
119 | .external_paths |
120 | .get(&id) | |
121 | .expect("Trait should either be in local or external paths") | |
122 | }) | |
123 | .0 | |
124 | .last() | |
5099ac24 | 125 | .map(|s| s.to_string()), |
fc512014 | 126 | visibility: types::Visibility::Public, |
cdc7bbd5 XL |
127 | inner: types::ItemEnum::Trait(trait_item.clone().into_tcx(self.tcx)), |
128 | span: None, | |
fc512014 XL |
129 | docs: Default::default(), |
130 | links: Default::default(), | |
131 | attrs: Default::default(), | |
132 | deprecation: Default::default(), | |
133 | }, | |
134 | )) | |
135 | } else { | |
136 | None | |
137 | } | |
138 | }) | |
139 | .collect() | |
140 | } | |
141 | } | |
142 | ||
143 | impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { | |
5869c6ff XL |
144 | fn descr() -> &'static str { |
145 | "json" | |
146 | } | |
147 | ||
cdc7bbd5 XL |
148 | const RUN_ON_MODULE: bool = false; |
149 | ||
3dfed10e | 150 | fn init( |
fc512014 XL |
151 | krate: clean::Crate, |
152 | options: RenderOptions, | |
5869c6ff XL |
153 | cache: Cache, |
154 | tcx: TyCtxt<'tcx>, | |
3dfed10e | 155 | ) -> Result<(Self, clean::Crate), Error> { |
fc512014 XL |
156 | debug!("Initializing json renderer"); |
157 | Ok(( | |
158 | JsonRenderer { | |
159 | tcx, | |
160 | index: Rc::new(RefCell::new(FxHashMap::default())), | |
161 | out_path: options.output, | |
5869c6ff | 162 | cache: Rc::new(cache), |
fc512014 XL |
163 | }, |
164 | krate, | |
165 | )) | |
3dfed10e XL |
166 | } |
167 | ||
6a06907d XL |
168 | fn make_child_renderer(&self) -> Self { |
169 | self.clone() | |
170 | } | |
171 | ||
fc512014 XL |
172 | /// Inserts an item into the index. This should be used rather than directly calling insert on |
173 | /// the hashmap because certain items (traits and types) need to have their mappings for trait | |
174 | /// implementations filled out before they're inserted. | |
5869c6ff | 175 | fn item(&mut self, item: clean::Item) -> Result<(), Error> { |
fc512014 | 176 | // Flatten items that recursively store other items |
5869c6ff | 177 | item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap()); |
fc512014 XL |
178 | |
179 | let id = item.def_id; | |
180 | if let Some(mut new_item) = self.convert_item(item) { | |
6a06907d | 181 | if let types::ItemEnum::Trait(ref mut t) = new_item.inner { |
136023e0 | 182 | t.implementors = self.get_trait_implementors(id.expect_def_id()) |
6a06907d | 183 | } else if let types::ItemEnum::Struct(ref mut s) = new_item.inner { |
136023e0 | 184 | s.impls = self.get_impls(id.expect_def_id()) |
6a06907d | 185 | } else if let types::ItemEnum::Enum(ref mut e) = new_item.inner { |
136023e0 | 186 | e.impls = self.get_impls(id.expect_def_id()) |
c295e0f8 XL |
187 | } else if let types::ItemEnum::Union(ref mut u) = new_item.inner { |
188 | u.impls = self.get_impls(id.expect_def_id()) | |
fc512014 | 189 | } |
136023e0 | 190 | let removed = self.index.borrow_mut().insert(from_item_id(id), new_item.clone()); |
cdc7bbd5 | 191 | |
fc512014 | 192 | // FIXME(adotinthevoid): Currently, the index is duplicated. This is a sanity check |
cdc7bbd5 XL |
193 | // to make sure the items are unique. The main place this happens is when an item, is |
194 | // reexported in more than one place. See `rustdoc-json/reexport/in_root_and_mod` | |
fc512014 XL |
195 | if let Some(old_item) = removed { |
196 | assert_eq!(old_item, new_item); | |
197 | } | |
198 | } | |
199 | ||
200 | Ok(()) | |
3dfed10e XL |
201 | } |
202 | ||
cdc7bbd5 XL |
203 | fn mod_item_in(&mut self, _item: &clean::Item) -> Result<(), Error> { |
204 | unreachable!("RUN_ON_MODULE = false should never call mod_item_in") | |
3dfed10e XL |
205 | } |
206 | ||
cdc7bbd5 | 207 | fn after_krate(&mut self) -> Result<(), Error> { |
fc512014 | 208 | debug!("Done with crate"); |
c295e0f8 XL |
209 | |
210 | for primitive in Rc::clone(&self.cache).primitive_locations.values() { | |
211 | self.get_impls(*primitive); | |
212 | } | |
213 | ||
fc512014 | 214 | let mut index = (*self.index).clone().into_inner(); |
5869c6ff XL |
215 | index.extend(self.get_trait_items()); |
216 | // This needs to be the default HashMap for compatibility with the public interface for | |
5099ac24 | 217 | // rustdoc-json-types |
5869c6ff | 218 | #[allow(rustc::default_hash_types)] |
fc512014 XL |
219 | let output = types::Crate { |
220 | root: types::Id(String::from("0:0")), | |
6a06907d | 221 | crate_version: self.cache.crate_version.clone(), |
5869c6ff XL |
222 | includes_private: self.cache.document_private, |
223 | index: index.into_iter().collect(), | |
224 | paths: self | |
225 | .cache | |
fc512014 XL |
226 | .paths |
227 | .clone() | |
228 | .into_iter() | |
5869c6ff | 229 | .chain(self.cache.external_paths.clone().into_iter()) |
fc512014 XL |
230 | .map(|(k, (path, kind))| { |
231 | ( | |
136023e0 | 232 | from_item_id(k.into()), |
cdc7bbd5 XL |
233 | types::ItemSummary { |
234 | crate_id: k.krate.as_u32(), | |
5099ac24 | 235 | path: path.iter().map(|s| s.to_string()).collect(), |
cdc7bbd5 XL |
236 | kind: kind.into_tcx(self.tcx), |
237 | }, | |
fc512014 XL |
238 | ) |
239 | }) | |
240 | .collect(), | |
5869c6ff XL |
241 | external_crates: self |
242 | .cache | |
fc512014 XL |
243 | .extern_locations |
244 | .iter() | |
17df50a5 XL |
245 | .map(|(crate_num, external_location)| { |
246 | let e = ExternalCrate { crate_num: *crate_num }; | |
fc512014 | 247 | ( |
17df50a5 | 248 | crate_num.as_u32(), |
fc512014 | 249 | types::ExternalCrate { |
17df50a5 XL |
250 | name: e.name(self.tcx).to_string(), |
251 | html_root_url: match external_location { | |
fc512014 XL |
252 | ExternalLocation::Remote(s) => Some(s.clone()), |
253 | _ => None, | |
254 | }, | |
255 | }, | |
256 | ) | |
257 | }) | |
258 | .collect(), | |
c295e0f8 | 259 | format_version: types::FORMAT_VERSION, |
fc512014 | 260 | }; |
5099ac24 FG |
261 | let out_dir = self.out_path.clone(); |
262 | try_err!(create_dir_all(&out_dir), out_dir); | |
263 | ||
264 | let mut p = out_dir; | |
fc512014 XL |
265 | p.push(output.index.get(&output.root).unwrap().name.clone().unwrap()); |
266 | p.set_extension("json"); | |
5099ac24 FG |
267 | let mut file = BufWriter::new(try_err!(File::create(&p), p)); |
268 | serde_json::ser::to_writer(&mut file, &output).unwrap(); | |
269 | try_err!(file.flush(), p); | |
270 | ||
fc512014 | 271 | Ok(()) |
3dfed10e XL |
272 | } |
273 | ||
5869c6ff XL |
274 | fn cache(&self) -> &Cache { |
275 | &self.cache | |
3dfed10e XL |
276 | } |
277 | } |