1use std::sync::Arc;
2
3use rustdoc_types::Item;
4use trustfall::{
5 FieldValue, Schema,
6 provider::{
7 Adapter, AsVertex, ContextIterator, ContextOutcomeIterator, EdgeParameters,
8 ResolveEdgeInfo, ResolveInfo, Typename, VertexIterator, resolve_coercion_with,
9 },
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 | "ExportableFunction"
114 | "Function"
115 | "Method"
116 | "Impl"
117 | "GlobalValue"
118 | "Constant"
119 | "Static"
120 | "AssociatedType"
121 | "AssociatedConstant"
122 | "Module"
123 | "Macro"
124 | "ProcMacro"
125 | "FunctionLikeProcMacro"
126 | "AttributeProcMacro"
127 | "DeriveProcMacro"
128 if matches!(
129 property_name.as_ref(),
130 "id" | "crate_id"
131 | "name"
132 | "docs"
133 | "attrs"
134 | "doc_hidden"
135 | "deprecated"
136 | "public_api_eligible"
137 | "visibility_limit"
138 ) =>
139 {
140 properties::resolve_item_property(contexts, property_name)
142 }
143 "Module" => properties::resolve_module_property(contexts, property_name),
144 "Struct" => properties::resolve_struct_property(contexts, property_name),
145 "StructField" => properties::resolve_struct_field_property(contexts, property_name),
146 "Enum" => properties::resolve_enum_property(contexts, property_name),
147 "Variant" | "PlainVariant" | "TupleVariant" | "StructVariant" => {
148 properties::resolve_enum_variant_property(contexts, property_name)
149 }
150 "Union" => properties::resolve_union_property(contexts, property_name),
151 "Span" => properties::resolve_span_property(contexts, property_name),
152 "Path" => properties::resolve_path_property(contexts, property_name),
153 "ImportablePath" => {
154 properties::resolve_importable_path_property(contexts, property_name)
155 }
156 "FunctionLike" | "ExportableFunction" | "Function" | "Method"
157 if matches!(
158 property_name.as_ref(),
159 "const" | "unsafe" | "async" | "has_body" | "signature"
160 ) =>
161 {
162 properties::resolve_function_like_property(contexts, property_name)
163 }
164 "ExportableFunction" | "Function" | "Method"
165 if matches!(property_name.as_ref(), "export_name") =>
166 {
167 properties::resolve_exportable_function_property(contexts, property_name)
168 }
169 "Function" => properties::resolve_function_property(contexts, property_name),
170 "FunctionParameter" => {
171 properties::resolve_function_parameter_property(contexts, property_name)
172 }
173 "FunctionAbi" => properties::resolve_function_abi_property(contexts, property_name),
174 "Impl" => properties::resolve_impl_property(contexts, property_name),
175 "Attribute" => properties::resolve_attribute_property(contexts, property_name),
176 "AttributeMetaItem" => {
177 properties::resolve_attribute_meta_item_property(contexts, property_name)
178 }
179 "Trait" => properties::resolve_trait_property(
180 contexts,
181 property_name,
182 self.current_crate,
183 self.previous_crate,
184 ),
185 "ImplementedTrait" => properties::resolve_implemented_trait_property(
186 contexts,
187 property_name,
188 self.current_crate,
189 self.previous_crate,
190 ),
191 "Static" => properties::resolve_static_property(contexts, property_name),
192 "RawType" | "ResolvedPathType" if matches!(property_name.as_ref(), "name") => {
193 properties::resolve_raw_type_property(contexts, property_name)
195 }
196 "AssociatedType" => {
197 properties::resolve_associated_type_property(contexts, property_name)
198 }
199 "AssociatedConstant" => {
200 properties::resolve_associated_constant_property(contexts, property_name)
201 }
202 "Constant" => properties::resolve_constant_property(contexts, property_name),
203 "Discriminant" => {
204 properties::resolve_discriminant_property(contexts, property_name)
205 }
206 "Feature" => properties::resolve_feature_property(contexts, property_name),
207 "DeriveMacroHelperAttribute" => {
208 properties::resolve_derive_macro_helper_attribute_property(
209 contexts,
210 property_name,
211 )
212 }
213 "GenericParameter"
214 | "GenericTypeParameter"
215 | "GenericLifetimeParameter"
216 | "GenericConstParameter"
217 if matches!(property_name.as_ref(), "name" | "position") =>
218 {
219 properties::resolve_generic_parameter_property(contexts, property_name)
220 }
221 "GenericTypeParameter" => properties::resolve_generic_type_parameter_property(
222 contexts,
223 property_name,
224 self.current_crate,
225 self.previous_crate,
226 ),
227 "GenericConstParameter" => {
228 properties::resolve_generic_const_parameter_property(contexts, property_name)
229 }
230 "Receiver" => properties::resolve_receiver_property(contexts, property_name),
231 "RequiredTargetFeature" => {
232 properties::resolve_required_target_feature_property(contexts, property_name)
233 }
234 _ => unreachable!("resolve_property {type_name} {property_name}"),
235 }
236 }
237 }
238
239 fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'a>(
240 &self,
241 contexts: ContextIterator<'a, V>,
242 type_name: &Arc<str>,
243 edge_name: &Arc<str>,
244 parameters: &EdgeParameters,
245 resolve_info: &ResolveEdgeInfo,
246 ) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Self::Vertex>> {
247 match type_name.as_ref() {
248 "CrateDiff" => edges::resolve_crate_diff_edge(contexts, edge_name),
249 "Crate" => edges::resolve_crate_edge(self, contexts, edge_name, resolve_info),
250 "Importable"
251 | "ImplOwner"
252 | "Struct"
253 | "Enum"
254 | "Union"
255 | "Trait"
256 | "Function"
257 | "GlobalValue"
258 | "Constant"
259 | "Static"
260 | "Module"
261 | "Macro"
262 | "ProcMacro"
263 | "FunctionLikeProcMacro"
264 | "AttributeProcMacro"
265 | "DeriveProcMacro"
266 if matches!(edge_name.as_ref(), "importable_path" | "canonical_path") =>
267 {
268 edges::resolve_importable_edge(
269 contexts,
270 edge_name,
271 self.current_crate,
272 self.previous_crate,
273 )
274 }
275 "Item"
276 | "GenericItem"
277 | "ImplOwner"
278 | "Struct"
279 | "StructField"
280 | "Enum"
281 | "Variant"
282 | "PlainVariant"
283 | "TupleVariant"
284 | "Union"
285 | "StructVariant"
286 | "Trait"
287 | "ExportableFunction"
288 | "Function"
289 | "Method"
290 | "Impl"
291 | "GlobalValue"
292 | "Constant"
293 | "Static"
294 | "AssociatedType"
295 | "AssociatedConstant"
296 | "Module"
297 | "Macro"
298 | "ProcMacro"
299 | "FunctionLikeProcMacro"
300 | "AttributeProcMacro"
301 | "DeriveProcMacro"
302 if matches!(edge_name.as_ref(), "span" | "attribute") =>
303 {
304 edges::resolve_item_edge(contexts, edge_name)
305 }
306 "ImplOwner" | "Struct" | "Enum" | "Union"
307 if matches!(edge_name.as_ref(), "impl" | "inherent_impl") =>
308 {
309 edges::resolve_impl_owner_edge(self, contexts, edge_name, resolve_info)
310 }
311 "Function" | "Method" | "FunctionLike" | "ExportableFunction"
312 if matches!(edge_name.as_ref(), "parameter" | "abi") =>
313 {
314 edges::resolve_function_like_edge(contexts, edge_name)
315 }
316 "GenericItem" | "ImplOwner" | "Struct" | "Enum" | "Union" | "Trait" | "Function"
317 | "Method" | "Impl"
318 if matches!(edge_name.as_ref(), "generic_parameter") =>
319 {
320 edges::resolve_generic_parameter_edge(contexts, edge_name)
321 }
322 "Method" if matches!(edge_name.as_ref(), "receiver") => {
323 edges::resolve_receiver_edge(contexts, edge_name)
324 }
325 "Function" | "Method" if matches!(edge_name.as_ref(), "requires_feature") => {
326 edges::resolve_requires_target_feature_edge(
327 contexts,
328 self.current_crate,
329 self.previous_crate,
330 )
331 }
332 "Module" => edges::resolve_module_edge(
333 contexts,
334 edge_name,
335 self.current_crate,
336 self.previous_crate,
337 ),
338 "Struct" => edges::resolve_struct_edge(
339 contexts,
340 edge_name,
341 self.current_crate,
342 self.previous_crate,
343 ),
344 "Variant" | "PlainVariant" | "TupleVariant" | "StructVariant" => {
345 edges::resolve_variant_edge(
346 contexts,
347 edge_name,
348 self.current_crate,
349 self.previous_crate,
350 )
351 }
352 "Enum" => edges::resolve_enum_edge(
353 contexts,
354 edge_name,
355 self.current_crate,
356 self.previous_crate,
357 ),
358 "Union" => edges::resolve_union_edge(
359 contexts,
360 edge_name,
361 self.current_crate,
362 self.previous_crate,
363 ),
364 "StructField" => edges::resolve_struct_field_edge(contexts, edge_name),
365 "Impl" => edges::resolve_impl_edge(self, contexts, edge_name, resolve_info),
366 "Trait" => edges::resolve_trait_edge(
367 contexts,
368 edge_name,
369 self.current_crate,
370 self.previous_crate,
371 ),
372 "ImplementedTrait" => edges::resolve_implemented_trait_edge(contexts, edge_name),
373 "Attribute" => edges::resolve_attribute_edge(contexts, edge_name),
374 "AttributeMetaItem" => edges::resolve_attribute_meta_item_edge(contexts, edge_name),
375 "Feature" => edges::resolve_feature_edge(
376 contexts,
377 edge_name,
378 self.current_crate,
379 self.previous_crate,
380 ),
381 "DeriveProcMacro" => edges::resolve_derive_proc_macro_edge(contexts, edge_name),
382 "GenericTypeParameter" => edges::resolve_generic_type_parameter_edge(
383 contexts,
384 edge_name,
385 self.current_crate,
386 self.previous_crate,
387 ),
388 _ => unreachable!("resolve_neighbors {type_name} {edge_name} {parameters:?}"),
389 }
390 }
391
392 fn resolve_coercion<V: AsVertex<Self::Vertex> + 'a>(
393 &self,
394 contexts: ContextIterator<'a, V>,
395 type_name: &Arc<str>,
396 coerce_to_type: &Arc<str>,
397 _resolve_info: &ResolveInfo,
398 ) -> ContextOutcomeIterator<'a, V, bool> {
399 let coerce_to_type = coerce_to_type.clone();
400 match type_name.as_ref() {
401 "Item" | "GenericItem" | "Variant" | "FunctionLike" | "ExportableFunction"
402 | "Importable" | "ImplOwner" | "RawType" | "GlobalValue" | "ProcMacro" => {
403 resolve_coercion_with(contexts, move |vertex| {
404 let actual_type_name = vertex.typename();
405
406 match coerce_to_type.as_ref() {
407 "GenericItem" => matches!(
408 actual_type_name,
409 "Struct" | "Enum" | "Union" | "Trait" | "Function" | "Method"
410 ),
411 "Variant" => matches!(
412 actual_type_name,
413 "PlainVariant" | "TupleVariant" | "StructVariant"
414 ),
415 "ImplOwner" => matches!(actual_type_name, "Struct" | "Enum" | "Union"),
416 "GlobalValue" => matches!(actual_type_name, "Constant" | "Static",),
417 "ProcMacro" => matches!(
418 actual_type_name,
419 "FunctionLikeProcMacro" | "AttributeProcMacro" | "DeriveProcMacro"
420 ),
421 "ExportableFunction" => matches!(actual_type_name, "Function" | "Method"),
422 _ => {
423 actual_type_name == coerce_to_type.as_ref()
427 }
428 }
429 })
430 }
431 "GenericParameter" => resolve_coercion_with(contexts, move |vertex| {
432 let actual_type_name = vertex.typename();
433
434 actual_type_name == coerce_to_type.as_ref()
438 }),
439 _ => unreachable!("resolve_coercion {type_name} {coerce_to_type}"),
440 }
441 }
442}
443
444pub(crate) fn supported_item_kind(item: &Item) -> bool {
445 matches!(
446 item.inner,
447 rustdoc_types::ItemEnum::Struct(..)
448 | rustdoc_types::ItemEnum::StructField(..)
449 | rustdoc_types::ItemEnum::Enum(..)
450 | rustdoc_types::ItemEnum::Variant(..)
451 | rustdoc_types::ItemEnum::Union(..)
452 | rustdoc_types::ItemEnum::Function(..)
453 | rustdoc_types::ItemEnum::Impl(..)
454 | rustdoc_types::ItemEnum::Trait(..)
455 | rustdoc_types::ItemEnum::Constant { .. }
456 | rustdoc_types::ItemEnum::Static(..)
457 | rustdoc_types::ItemEnum::AssocType { .. }
458 | rustdoc_types::ItemEnum::Module { .. }
459 | rustdoc_types::ItemEnum::Macro { .. }
460 | rustdoc_types::ItemEnum::ProcMacro { .. }
461 )
462}