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" | "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 | "GenericItem"
282 | "ImplOwner"
283 | "Struct"
284 | "StructField"
285 | "Enum"
286 | "Variant"
287 | "PlainVariant"
288 | "TupleVariant"
289 | "Union"
290 | "StructVariant"
291 | "Trait"
292 | "ExportableFunction"
293 | "Function"
294 | "Method"
295 | "Impl"
296 | "GlobalValue"
297 | "Constant"
298 | "Static"
299 | "AssociatedType"
300 | "AssociatedConstant"
301 | "Module"
302 | "Macro"
303 | "ProcMacro"
304 | "FunctionLikeProcMacro"
305 | "AttributeProcMacro"
306 | "DeriveProcMacro"
307 if matches!(edge_name.as_ref(), "span" | "attribute") =>
308 {
309 edges::resolve_item_edge(contexts, edge_name)
310 }
311 "ImplOwner" | "Struct" | "Enum" | "Union"
312 if matches!(edge_name.as_ref(), "impl" | "inherent_impl") =>
313 {
314 edges::resolve_impl_owner_edge(self, contexts, edge_name, resolve_info)
315 }
316 "Function" | "Method" | "FunctionLike" | "ExportableFunction"
317 if matches!(edge_name.as_ref(), "parameter" | "abi" | "return_value") =>
318 {
319 edges::resolve_function_like_edge(contexts, edge_name)
320 }
321 "GenericItem" | "ImplOwner" | "Struct" | "Enum" | "Union" | "Trait" | "Function"
322 | "Method" | "Impl"
323 if matches!(edge_name.as_ref(), "generic_parameter") =>
324 {
325 edges::resolve_generic_parameter_edge(contexts, edge_name)
326 }
327 "Method" if matches!(edge_name.as_ref(), "receiver") => {
328 edges::resolve_receiver_edge(contexts, edge_name)
329 }
330 "Function" | "Method" if matches!(edge_name.as_ref(), "requires_feature") => {
331 edges::resolve_requires_target_feature_edge(contexts, self)
332 }
333 "Module" => edges::resolve_module_edge(contexts, edge_name, self),
334 "Struct" => edges::resolve_struct_edge(contexts, edge_name, self),
335 "Variant" | "PlainVariant" | "TupleVariant" | "StructVariant" => {
336 edges::resolve_variant_edge(contexts, edge_name, self)
337 }
338 "Enum" => edges::resolve_enum_edge(contexts, edge_name, self, resolve_info),
339 "Union" => edges::resolve_union_edge(contexts, edge_name, self),
340 "StructField" => edges::resolve_struct_field_edge(contexts, edge_name),
341 "Impl" => edges::resolve_impl_edge(self, contexts, edge_name, resolve_info),
342 "Trait" => edges::resolve_trait_edge(contexts, edge_name, self),
343 "ImplementedTrait" => edges::resolve_implemented_trait_edge(contexts, edge_name),
344 "Attribute" => edges::resolve_attribute_edge(contexts, edge_name),
345 "AttributeMetaItem" => edges::resolve_attribute_meta_item_edge(contexts, edge_name),
346 "Feature" => edges::resolve_feature_edge(contexts, edge_name, self),
347 "DeriveProcMacro" => edges::resolve_derive_proc_macro_edge(contexts, edge_name),
348 "GenericTypeParameter" => {
349 edges::resolve_generic_type_parameter_edge(contexts, edge_name, self)
350 }
351 _ => unreachable!("resolve_neighbors {type_name} {edge_name} {parameters:?}"),
352 }
353 }
354
355 fn resolve_coercion<V: AsVertex<Self::Vertex> + 'a>(
356 &self,
357 contexts: ContextIterator<'a, V>,
358 type_name: &Arc<str>,
359 coerce_to_type: &Arc<str>,
360 _resolve_info: &ResolveInfo,
361 ) -> ContextOutcomeIterator<'a, V, bool> {
362 let coerce_to_type = coerce_to_type.clone();
363 match type_name.as_ref() {
364 "Item" | "GenericItem" | "Variant" | "FunctionLike" | "ExportableFunction"
365 | "Importable" | "ImplOwner" | "RawType" | "GlobalValue" | "ProcMacro" => {
366 resolve_coercion_with(contexts, move |vertex| {
367 let actual_type_name = vertex.typename();
368
369 match coerce_to_type.as_ref() {
370 "GenericItem" => matches!(
371 actual_type_name,
372 "Struct" | "Enum" | "Union" | "Trait" | "Function" | "Method"
373 ),
374 "Variant" => matches!(
375 actual_type_name,
376 "PlainVariant" | "TupleVariant" | "StructVariant"
377 ),
378 "ImplOwner" => matches!(actual_type_name, "Struct" | "Enum" | "Union"),
379 "GlobalValue" => matches!(actual_type_name, "Constant" | "Static",),
380 "ProcMacro" => matches!(
381 actual_type_name,
382 "FunctionLikeProcMacro" | "AttributeProcMacro" | "DeriveProcMacro"
383 ),
384 "ExportableFunction" => matches!(actual_type_name, "Function" | "Method"),
385 _ => {
386 actual_type_name == coerce_to_type.as_ref()
390 }
391 }
392 })
393 }
394 "GenericParameter" => resolve_coercion_with(contexts, move |vertex| {
395 let actual_type_name = vertex.typename();
396
397 actual_type_name == coerce_to_type.as_ref()
401 }),
402 _ => unreachable!("resolve_coercion {type_name} {coerce_to_type}"),
403 }
404 }
405}
406
407pub(crate) fn supported_item_kind(item: &Item) -> bool {
408 matches!(
409 item.inner,
410 rustdoc_types::ItemEnum::Struct(..)
411 | rustdoc_types::ItemEnum::StructField(..)
412 | rustdoc_types::ItemEnum::Enum(..)
413 | rustdoc_types::ItemEnum::Variant(..)
414 | rustdoc_types::ItemEnum::Union(..)
415 | rustdoc_types::ItemEnum::Function(..)
416 | rustdoc_types::ItemEnum::Impl(..)
417 | rustdoc_types::ItemEnum::Trait(..)
418 | rustdoc_types::ItemEnum::Constant { .. }
419 | rustdoc_types::ItemEnum::Static(..)
420 | rustdoc_types::ItemEnum::AssocType { .. }
421 | rustdoc_types::ItemEnum::Module { .. }
422 | rustdoc_types::ItemEnum::Macro { .. }
423 | rustdoc_types::ItemEnum::ProcMacro { .. }
424 )
425}