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