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