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