trustfall_rustdoc_adapter/adapter/
mod.rs

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 inherited from Item, accesssed on Item subtypes
141                    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                    // fields from "RawType"
194                    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                            // The remaining types are final (don't have any subtypes)
424                            // so we can just compare the actual type name to
425                            // the type we are attempting to coerce to.
426                            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                // The possible types are final (don't have any subtypes)
435                // so we can just compare the actual type name to
436                // the type we are attempting to coerce to.
437                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}