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 "FunctionAbi" => properties::resolve_function_abi_property(contexts, property_name),
191 "Impl" => properties::resolve_impl_property(contexts, property_name),
192 "Attribute" => properties::resolve_attribute_property(contexts, property_name),
193 "AttributeMetaItem" => {
194 properties::resolve_attribute_meta_item_property(contexts, property_name)
195 }
196 "Trait" => properties::resolve_trait_property(contexts, property_name, self),
197 "ImplementedTrait" => {
198 properties::resolve_implemented_trait_property(contexts, property_name, self)
199 }
200 "Static" => properties::resolve_static_property(contexts, property_name),
201 "RawType" | "ResolvedPathType" if matches!(property_name.as_ref(), "name") => {
202 properties::resolve_raw_type_property(contexts, property_name)
204 }
205 "AssociatedType" => {
206 properties::resolve_associated_type_property(contexts, property_name)
207 }
208 "AssociatedConstant" => {
209 properties::resolve_associated_constant_property(contexts, property_name)
210 }
211 "Constant" => properties::resolve_constant_property(contexts, property_name),
212 "Discriminant" => {
213 properties::resolve_discriminant_property(contexts, property_name)
214 }
215 "Feature" => properties::resolve_feature_property(contexts, property_name),
216 "DeriveMacroHelperAttribute" => {
217 properties::resolve_derive_macro_helper_attribute_property(
218 contexts,
219 property_name,
220 )
221 }
222 "GenericParameter"
223 | "GenericTypeParameter"
224 | "GenericLifetimeParameter"
225 | "GenericConstParameter"
226 if matches!(property_name.as_ref(), "name" | "position") =>
227 {
228 properties::resolve_generic_parameter_property(contexts, property_name)
229 }
230 "GenericTypeParameter" => properties::resolve_generic_type_parameter_property(
231 contexts,
232 property_name,
233 self,
234 ),
235 "GenericConstParameter" => {
236 properties::resolve_generic_const_parameter_property(contexts, property_name)
237 }
238 "Receiver" => properties::resolve_receiver_property(contexts, property_name),
239 "RequiredTargetFeature" => {
240 properties::resolve_required_target_feature_property(contexts, property_name)
241 }
242 _ => unreachable!("resolve_property {type_name} {property_name}"),
243 }
244 }
245 }
246
247 fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'a>(
248 &self,
249 contexts: ContextIterator<'a, V>,
250 type_name: &Arc<str>,
251 edge_name: &Arc<str>,
252 parameters: &EdgeParameters,
253 resolve_info: &ResolveEdgeInfo,
254 ) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Self::Vertex>> {
255 match type_name.as_ref() {
256 "CrateDiff" => edges::resolve_crate_diff_edge(contexts, edge_name),
257 "Crate" => edges::resolve_crate_edge(self, contexts, edge_name, resolve_info),
258 "Importable"
259 | "ImplOwner"
260 | "Struct"
261 | "Enum"
262 | "Union"
263 | "Trait"
264 | "Function"
265 | "GlobalValue"
266 | "Constant"
267 | "Static"
268 | "Module"
269 | "Macro"
270 | "ProcMacro"
271 | "FunctionLikeProcMacro"
272 | "AttributeProcMacro"
273 | "DeriveProcMacro"
274 if matches!(edge_name.as_ref(), "importable_path" | "canonical_path") =>
275 {
276 edges::resolve_importable_edge(contexts, edge_name, self)
277 }
278 "Item"
279 | "GenericItem"
280 | "ImplOwner"
281 | "Struct"
282 | "StructField"
283 | "Enum"
284 | "Variant"
285 | "PlainVariant"
286 | "TupleVariant"
287 | "Union"
288 | "StructVariant"
289 | "Trait"
290 | "ExportableFunction"
291 | "Function"
292 | "Method"
293 | "Impl"
294 | "GlobalValue"
295 | "Constant"
296 | "Static"
297 | "AssociatedType"
298 | "AssociatedConstant"
299 | "Module"
300 | "Macro"
301 | "ProcMacro"
302 | "FunctionLikeProcMacro"
303 | "AttributeProcMacro"
304 | "DeriveProcMacro"
305 if matches!(edge_name.as_ref(), "span" | "attribute") =>
306 {
307 edges::resolve_item_edge(contexts, edge_name)
308 }
309 "ImplOwner" | "Struct" | "Enum" | "Union"
310 if matches!(edge_name.as_ref(), "impl" | "inherent_impl") =>
311 {
312 edges::resolve_impl_owner_edge(self, contexts, edge_name, resolve_info)
313 }
314 "Function" | "Method" | "FunctionLike" | "ExportableFunction"
315 if matches!(edge_name.as_ref(), "parameter" | "abi") =>
316 {
317 edges::resolve_function_like_edge(contexts, edge_name)
318 }
319 "GenericItem" | "ImplOwner" | "Struct" | "Enum" | "Union" | "Trait" | "Function"
320 | "Method" | "Impl"
321 if matches!(edge_name.as_ref(), "generic_parameter") =>
322 {
323 edges::resolve_generic_parameter_edge(contexts, edge_name)
324 }
325 "Method" if matches!(edge_name.as_ref(), "receiver") => {
326 edges::resolve_receiver_edge(contexts, edge_name)
327 }
328 "Function" | "Method" if matches!(edge_name.as_ref(), "requires_feature") => {
329 edges::resolve_requires_target_feature_edge(contexts, self)
330 }
331 "Module" => edges::resolve_module_edge(contexts, edge_name, self),
332 "Struct" => edges::resolve_struct_edge(contexts, edge_name, self),
333 "Variant" | "PlainVariant" | "TupleVariant" | "StructVariant" => {
334 edges::resolve_variant_edge(contexts, edge_name, self)
335 }
336 "Enum" => edges::resolve_enum_edge(contexts, edge_name, self, resolve_info),
337 "Union" => edges::resolve_union_edge(contexts, edge_name, self),
338 "StructField" => edges::resolve_struct_field_edge(contexts, edge_name),
339 "Impl" => edges::resolve_impl_edge(self, contexts, edge_name, resolve_info),
340 "Trait" => edges::resolve_trait_edge(contexts, edge_name, self),
341 "ImplementedTrait" => edges::resolve_implemented_trait_edge(contexts, edge_name),
342 "Attribute" => edges::resolve_attribute_edge(contexts, edge_name),
343 "AttributeMetaItem" => edges::resolve_attribute_meta_item_edge(contexts, edge_name),
344 "Feature" => edges::resolve_feature_edge(contexts, edge_name, self),
345 "DeriveProcMacro" => edges::resolve_derive_proc_macro_edge(contexts, edge_name),
346 "GenericTypeParameter" => {
347 edges::resolve_generic_type_parameter_edge(contexts, edge_name, self)
348 }
349 _ => unreachable!("resolve_neighbors {type_name} {edge_name} {parameters:?}"),
350 }
351 }
352
353 fn resolve_coercion<V: AsVertex<Self::Vertex> + 'a>(
354 &self,
355 contexts: ContextIterator<'a, V>,
356 type_name: &Arc<str>,
357 coerce_to_type: &Arc<str>,
358 _resolve_info: &ResolveInfo,
359 ) -> ContextOutcomeIterator<'a, V, bool> {
360 let coerce_to_type = coerce_to_type.clone();
361 match type_name.as_ref() {
362 "Item" | "GenericItem" | "Variant" | "FunctionLike" | "ExportableFunction"
363 | "Importable" | "ImplOwner" | "RawType" | "GlobalValue" | "ProcMacro" => {
364 resolve_coercion_with(contexts, move |vertex| {
365 let actual_type_name = vertex.typename();
366
367 match coerce_to_type.as_ref() {
368 "GenericItem" => matches!(
369 actual_type_name,
370 "Struct" | "Enum" | "Union" | "Trait" | "Function" | "Method"
371 ),
372 "Variant" => matches!(
373 actual_type_name,
374 "PlainVariant" | "TupleVariant" | "StructVariant"
375 ),
376 "ImplOwner" => matches!(actual_type_name, "Struct" | "Enum" | "Union"),
377 "GlobalValue" => matches!(actual_type_name, "Constant" | "Static",),
378 "ProcMacro" => matches!(
379 actual_type_name,
380 "FunctionLikeProcMacro" | "AttributeProcMacro" | "DeriveProcMacro"
381 ),
382 "ExportableFunction" => matches!(actual_type_name, "Function" | "Method"),
383 _ => {
384 actual_type_name == coerce_to_type.as_ref()
388 }
389 }
390 })
391 }
392 "GenericParameter" => resolve_coercion_with(contexts, move |vertex| {
393 let actual_type_name = vertex.typename();
394
395 actual_type_name == coerce_to_type.as_ref()
399 }),
400 _ => unreachable!("resolve_coercion {type_name} {coerce_to_type}"),
401 }
402 }
403}
404
405pub(crate) fn supported_item_kind(item: &Item) -> bool {
406 matches!(
407 item.inner,
408 rustdoc_types::ItemEnum::Struct(..)
409 | rustdoc_types::ItemEnum::StructField(..)
410 | rustdoc_types::ItemEnum::Enum(..)
411 | rustdoc_types::ItemEnum::Variant(..)
412 | rustdoc_types::ItemEnum::Union(..)
413 | rustdoc_types::ItemEnum::Function(..)
414 | rustdoc_types::ItemEnum::Impl(..)
415 | rustdoc_types::ItemEnum::Trait(..)
416 | rustdoc_types::ItemEnum::Constant { .. }
417 | rustdoc_types::ItemEnum::Static(..)
418 | rustdoc_types::ItemEnum::AssocType { .. }
419 | rustdoc_types::ItemEnum::Module { .. }
420 | rustdoc_types::ItemEnum::Macro { .. }
421 | rustdoc_types::ItemEnum::ProcMacro { .. }
422 )
423}