1use std::sync::Arc;
2
3use rustdoc_types::Item;
4use trustfall::{
5 provider::{
6 resolve_coercion_with, Adapter, AsVertex, ContextIterator, ContextOutcomeIterator,
7 EdgeParameters, ResolveEdgeInfo, ResolveInfo, Typename, VertexIterator,
8 },
9 FieldValue, Schema,
10};
11
12use crate::PackageIndex;
13
14use self::{
15 origin::Origin,
16 vertex::{Vertex, VertexKind},
17};
18
19mod edges;
20mod enum_variant;
21mod optimizations;
22mod origin;
23mod properties;
24mod receiver;
25mod rust_type_name;
26mod vertex;
27
28#[cfg(test)]
29mod tests;
30
31#[non_exhaustive]
32pub struct RustdocAdapter<'a> {
33 current_crate: &'a PackageIndex<'a>,
34 previous_crate: Option<&'a PackageIndex<'a>>,
35}
36
37impl<'a> RustdocAdapter<'a> {
38 pub fn new(
39 current_crate: &'a PackageIndex<'a>,
40 previous_crate: Option<&'a PackageIndex<'a>>,
41 ) -> Self {
42 Self {
43 current_crate,
44 previous_crate,
45 }
46 }
47
48 pub fn schema() -> Schema {
49 Schema::parse(include_str!("../rustdoc_schema.graphql")).expect("schema not valid")
50 }
51}
52
53impl Drop for RustdocAdapter<'_> {
54 fn drop(&mut self) {}
55}
56
57impl<'a> Adapter<'a> for &'a RustdocAdapter<'a> {
58 type Vertex = Vertex<'a>;
59
60 fn resolve_starting_vertices(
61 &self,
62 edge_name: &Arc<str>,
63 _parameters: &EdgeParameters,
64 _resolve_info: &ResolveInfo,
65 ) -> VertexIterator<'a, Self::Vertex> {
66 match edge_name.as_ref() {
67 "Crate" => Box::new(std::iter::once(Vertex::new_crate(
68 Origin::CurrentCrate,
69 self.current_crate,
70 ))),
71 "CrateDiff" => {
72 let previous_crate = self.previous_crate.expect("no previous crate provided");
73 Box::new(std::iter::once(Vertex {
74 origin: Origin::CurrentCrate,
75 kind: VertexKind::CrateDiff((self.current_crate, previous_crate)),
76 }))
77 }
78 _ => unreachable!("resolve_starting_vertices {edge_name}"),
79 }
80 }
81
82 fn resolve_property<V: AsVertex<Self::Vertex> + 'a>(
83 &self,
84 contexts: ContextIterator<'a, V>,
85 type_name: &Arc<str>,
86 property_name: &Arc<str>,
87 _resolve_info: &ResolveInfo,
88 ) -> ContextOutcomeIterator<'a, V, FieldValue> {
89 if property_name.as_ref() == "__typename" {
90 Box::new(contexts.map(|ctx| match ctx.active_vertex() {
91 Some(vertex) => {
92 let value = vertex.typename().into();
93 (ctx, value)
94 }
95 None => (ctx, FieldValue::Null),
96 }))
97 } else {
98 match type_name.as_ref() {
99 "Crate" => properties::resolve_crate_property(contexts, property_name),
100 "Item" | "GenericItem" => {
101 properties::resolve_item_property(contexts, property_name)
102 }
103 "ImplOwner"
104 | "Struct"
105 | "StructField"
106 | "Enum"
107 | "Variant"
108 | "PlainVariant"
109 | "TupleVariant"
110 | "StructVariant"
111 | "Union"
112 | "Trait"
113 | "Function"
114 | "Method"
115 | "Impl"
116 | "GlobalValue"
117 | "Constant"
118 | "Static"
119 | "AssociatedType"
120 | "AssociatedConstant"
121 | "Module"
122 | "Macro"
123 | "ProcMacro"
124 | "FunctionLikeProcMacro"
125 | "AttributeProcMacro"
126 | "DeriveProcMacro"
127 if matches!(
128 property_name.as_ref(),
129 "id" | "crate_id"
130 | "name"
131 | "docs"
132 | "attrs"
133 | "doc_hidden"
134 | "deprecated"
135 | "public_api_eligible"
136 | "visibility_limit"
137 ) =>
138 {
139 properties::resolve_item_property(contexts, property_name)
141 }
142 "Module" => properties::resolve_module_property(contexts, property_name),
143 "Struct" => properties::resolve_struct_property(contexts, property_name),
144 "StructField" => properties::resolve_struct_field_property(contexts, property_name),
145 "Enum" => properties::resolve_enum_property(contexts, property_name),
146 "Variant" | "PlainVariant" | "TupleVariant" | "StructVariant" => {
147 properties::resolve_enum_variant_property(contexts, property_name)
148 }
149 "Union" => properties::resolve_union_property(contexts, property_name),
150 "Span" => properties::resolve_span_property(contexts, property_name),
151 "Path" => properties::resolve_path_property(contexts, property_name),
152 "ImportablePath" => {
153 properties::resolve_importable_path_property(contexts, property_name)
154 }
155 "FunctionLike" | "Function" | "Method"
156 if matches!(
157 property_name.as_ref(),
158 "const" | "unsafe" | "async" | "has_body" | "signature"
159 ) =>
160 {
161 properties::resolve_function_like_property(contexts, property_name)
162 }
163 "Function" => properties::resolve_function_property(contexts, property_name),
164 "FunctionParameter" => {
165 properties::resolve_function_parameter_property(contexts, property_name)
166 }
167 "FunctionAbi" => properties::resolve_function_abi_property(contexts, property_name),
168 "Impl" => properties::resolve_impl_property(contexts, property_name),
169 "Attribute" => properties::resolve_attribute_property(contexts, property_name),
170 "AttributeMetaItem" => {
171 properties::resolve_attribute_meta_item_property(contexts, property_name)
172 }
173 "Trait" => properties::resolve_trait_property(
174 contexts,
175 property_name,
176 self.current_crate,
177 self.previous_crate,
178 ),
179 "ImplementedTrait" => properties::resolve_implemented_trait_property(
180 contexts,
181 property_name,
182 self.current_crate,
183 self.previous_crate,
184 ),
185 "Static" => properties::resolve_static_property(contexts, property_name),
186 "RawType" | "ResolvedPathType" if matches!(property_name.as_ref(), "name") => {
187 properties::resolve_raw_type_property(contexts, property_name)
189 }
190 "AssociatedType" => {
191 properties::resolve_associated_type_property(contexts, property_name)
192 }
193 "AssociatedConstant" => {
194 properties::resolve_associated_constant_property(contexts, property_name)
195 }
196 "Constant" => properties::resolve_constant_property(contexts, property_name),
197 "Discriminant" => {
198 properties::resolve_discriminant_property(contexts, property_name)
199 }
200 "Feature" => properties::resolve_feature_property(contexts, property_name),
201 "DeriveMacroHelperAttribute" => {
202 properties::resolve_derive_macro_helper_attribute_property(
203 contexts,
204 property_name,
205 )
206 }
207 "GenericParameter"
208 | "GenericTypeParameter"
209 | "GenericLifetimeParameter"
210 | "GenericConstParameter"
211 if matches!(property_name.as_ref(), "name" | "position") =>
212 {
213 properties::resolve_generic_parameter_property(contexts, property_name)
214 }
215 "GenericTypeParameter" => {
216 properties::resolve_generic_type_parameter_property(contexts, property_name)
217 }
218 "GenericConstParameter" => {
219 properties::resolve_generic_const_parameter_property(contexts, property_name)
220 }
221 "Receiver" => properties::resolve_receiver_property(contexts, property_name),
222 _ => unreachable!("resolve_property {type_name} {property_name}"),
223 }
224 }
225 }
226
227 fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'a>(
228 &self,
229 contexts: ContextIterator<'a, V>,
230 type_name: &Arc<str>,
231 edge_name: &Arc<str>,
232 parameters: &EdgeParameters,
233 resolve_info: &ResolveEdgeInfo,
234 ) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Self::Vertex>> {
235 match type_name.as_ref() {
236 "CrateDiff" => edges::resolve_crate_diff_edge(contexts, edge_name),
237 "Crate" => edges::resolve_crate_edge(self, contexts, edge_name, resolve_info),
238 "Importable"
239 | "ImplOwner"
240 | "Struct"
241 | "Enum"
242 | "Union"
243 | "Trait"
244 | "Function"
245 | "GlobalValue"
246 | "Constant"
247 | "Static"
248 | "Module"
249 | "Macro"
250 | "ProcMacro"
251 | "FunctionLikeProcMacro"
252 | "AttributeProcMacro"
253 | "DeriveProcMacro"
254 if matches!(edge_name.as_ref(), "importable_path" | "canonical_path") =>
255 {
256 edges::resolve_importable_edge(
257 contexts,
258 edge_name,
259 self.current_crate,
260 self.previous_crate,
261 )
262 }
263 "Item"
264 | "GenericItem"
265 | "ImplOwner"
266 | "Struct"
267 | "StructField"
268 | "Enum"
269 | "Variant"
270 | "PlainVariant"
271 | "TupleVariant"
272 | "Union"
273 | "StructVariant"
274 | "Trait"
275 | "Function"
276 | "Method"
277 | "Impl"
278 | "GlobalValue"
279 | "Constant"
280 | "Static"
281 | "AssociatedType"
282 | "AssociatedConstant"
283 | "Module"
284 | "Macro"
285 | "ProcMacro"
286 | "FunctionLikeProcMacro"
287 | "AttributeProcMacro"
288 | "DeriveProcMacro"
289 if matches!(edge_name.as_ref(), "span" | "attribute") =>
290 {
291 edges::resolve_item_edge(contexts, edge_name)
292 }
293 "ImplOwner" | "Struct" | "Enum" | "Union"
294 if matches!(edge_name.as_ref(), "impl" | "inherent_impl") =>
295 {
296 edges::resolve_impl_owner_edge(self, contexts, edge_name, resolve_info)
297 }
298 "Function" | "Method" | "FunctionLike"
299 if matches!(edge_name.as_ref(), "parameter" | "abi") =>
300 {
301 edges::resolve_function_like_edge(contexts, edge_name)
302 }
303 "GenericItem" | "ImplOwner" | "Struct" | "Enum" | "Union" | "Trait" | "Function"
304 | "Method" | "Impl"
305 if matches!(edge_name.as_ref(), "generic_parameter") =>
306 {
307 edges::resolve_generic_parameter_edge(contexts, edge_name)
308 }
309 "Method" if matches!(edge_name.as_ref(), "receiver") => {
310 edges::resolve_receiver_edge(contexts, edge_name)
311 }
312 "Module" => edges::resolve_module_edge(
313 contexts,
314 edge_name,
315 self.current_crate,
316 self.previous_crate,
317 ),
318 "Struct" => edges::resolve_struct_edge(
319 contexts,
320 edge_name,
321 self.current_crate,
322 self.previous_crate,
323 ),
324 "Variant" | "PlainVariant" | "TupleVariant" | "StructVariant" => {
325 edges::resolve_variant_edge(
326 contexts,
327 edge_name,
328 self.current_crate,
329 self.previous_crate,
330 )
331 }
332 "Enum" => edges::resolve_enum_edge(
333 contexts,
334 edge_name,
335 self.current_crate,
336 self.previous_crate,
337 ),
338 "Union" => edges::resolve_union_edge(
339 contexts,
340 edge_name,
341 self.current_crate,
342 self.previous_crate,
343 ),
344 "StructField" => edges::resolve_struct_field_edge(contexts, edge_name),
345 "Impl" => edges::resolve_impl_edge(self, contexts, edge_name, resolve_info),
346 "Trait" => edges::resolve_trait_edge(
347 contexts,
348 edge_name,
349 self.current_crate,
350 self.previous_crate,
351 ),
352 "ImplementedTrait" => edges::resolve_implemented_trait_edge(contexts, edge_name),
353 "Attribute" => edges::resolve_attribute_edge(contexts, edge_name),
354 "AttributeMetaItem" => edges::resolve_attribute_meta_item_edge(contexts, edge_name),
355 "Feature" => edges::resolve_feature_edge(
356 contexts,
357 edge_name,
358 self.current_crate,
359 self.previous_crate,
360 ),
361 "DeriveProcMacro" => edges::resolve_derive_proc_macro_edge(contexts, edge_name),
362 "GenericTypeParameter" => edges::resolve_generic_type_parameter_edge(
363 contexts,
364 edge_name,
365 self.current_crate,
366 self.previous_crate,
367 ),
368 _ => unreachable!("resolve_neighbors {type_name} {edge_name} {parameters:?}"),
369 }
370 }
371
372 fn resolve_coercion<V: AsVertex<Self::Vertex> + 'a>(
373 &self,
374 contexts: ContextIterator<'a, V>,
375 type_name: &Arc<str>,
376 coerce_to_type: &Arc<str>,
377 _resolve_info: &ResolveInfo,
378 ) -> ContextOutcomeIterator<'a, V, bool> {
379 let coerce_to_type = coerce_to_type.clone();
380 match type_name.as_ref() {
381 "Item" | "GenericItem" | "Variant" | "FunctionLike" | "Importable" | "ImplOwner"
382 | "RawType" | "GlobalValue" | "ProcMacro" => {
383 resolve_coercion_with(contexts, move |vertex| {
384 let actual_type_name = vertex.typename();
385
386 match coerce_to_type.as_ref() {
387 "GenericItem" => matches!(
388 actual_type_name,
389 "Struct" | "Enum" | "Union" | "Trait" | "Function" | "Method"
390 ),
391 "Variant" => matches!(
392 actual_type_name,
393 "PlainVariant" | "TupleVariant" | "StructVariant"
394 ),
395 "ImplOwner" => matches!(actual_type_name, "Struct" | "Enum" | "Union"),
396 "GlobalValue" => matches!(actual_type_name, "Constant" | "Static",),
397 "ProcMacro" => matches!(
398 actual_type_name,
399 "FunctionLikeProcMacro" | "AttributeProcMacro" | "DeriveProcMacro"
400 ),
401 _ => {
402 actual_type_name == coerce_to_type.as_ref()
406 }
407 }
408 })
409 }
410 "GenericParameter" => resolve_coercion_with(contexts, move |vertex| {
411 let actual_type_name = vertex.typename();
412
413 actual_type_name == coerce_to_type.as_ref()
417 }),
418 _ => unreachable!("resolve_coercion {type_name} {coerce_to_type}"),
419 }
420 }
421}
422
423pub(crate) fn supported_item_kind(item: &Item) -> bool {
424 matches!(
425 item.inner,
426 rustdoc_types::ItemEnum::Struct(..)
427 | rustdoc_types::ItemEnum::StructField(..)
428 | rustdoc_types::ItemEnum::Enum(..)
429 | rustdoc_types::ItemEnum::Variant(..)
430 | rustdoc_types::ItemEnum::Union(..)
431 | rustdoc_types::ItemEnum::Function(..)
432 | rustdoc_types::ItemEnum::Impl(..)
433 | rustdoc_types::ItemEnum::Trait(..)
434 | rustdoc_types::ItemEnum::Constant { .. }
435 | rustdoc_types::ItemEnum::Static(..)
436 | rustdoc_types::ItemEnum::AssocType { .. }
437 | rustdoc_types::ItemEnum::Module { .. }
438 | rustdoc_types::ItemEnum::Macro { .. }
439 | rustdoc_types::ItemEnum::ProcMacro { .. }
440 )
441}