1use rustdoc_types::{
2 Abi, AssocItemConstraintKind, Crate, Enum, GenericArg, GenericArgs, GenericBound,
3 GenericParamDefKind, Generics, Id, Impl, Item, ItemEnum, MacroKind, Module,
4 PreciseCapturingArg, Struct, StructKind, Term, Trait, TraitBoundModifier, Type, Union,
5 VariantKind, Visibility, WherePredicate,
6};
7
8pub fn rustdoc_json_to_markdown(data: Crate) -> String {
9 let mut output = String::new();
10
11 output.push_str("# Crate Documentation\n\n");
13
14 if let Some(version) = &data.crate_version {
15 output.push_str(&format!("**Version:** {}\n\n", version));
16 }
17
18 output.push_str(&format!("**Format Version:** {}\n\n", data.format_version));
19
20 let root_id = data.root;
22 if let Some(root_item) = data.index.get(&root_id) {
23 if let ItemEnum::Module(module) = &root_item.inner {
24 if let Some(name) = &root_item.name {
25 output.push_str(&format!("# Module `{}`\n\n", name));
26 } else if module.is_crate {
27 output.push_str("# Crate Root\n\n");
28 }
29
30 if let Some(docs) = &root_item.docs {
32 output.push_str(&format!("{}\n\n", docs));
33 }
34
35 process_items(&mut output, &module.items, &data, 2);
38 }
39 }
40
41 output
42}
43
44fn process_items(output: &mut String, item_ids: &[Id], data: &Crate, level: usize) {
45 let heading_level = std::cmp::min(level, 6);
47
48 let mut modules = Vec::new();
50 let mut types = Vec::new();
51 let mut traits = Vec::new();
52 let mut functions = Vec::new();
53 let mut constants = Vec::new();
54 let mut macros = Vec::new();
55 let mut reexports = Vec::new(); let mut other_items = Vec::new();
57
58 for &id in item_ids {
59 if let Some(item) = data.index.get(&id) {
60 match &item.inner {
61 ItemEnum::Module(_) => modules.push(id),
62 ItemEnum::Struct(_)
63 | ItemEnum::Enum(_)
64 | ItemEnum::Union(_)
65 | ItemEnum::TypeAlias(_) => types.push(id),
66 ItemEnum::Trait(_) | ItemEnum::TraitAlias(_) => traits.push(id),
67 ItemEnum::Function(_) => functions.push(id),
68 ItemEnum::Constant { .. } | ItemEnum::Static(_) => constants.push(id),
69 ItemEnum::Macro(_) | ItemEnum::ProcMacro(_) => macros.push(id),
70 ItemEnum::Use(_) => reexports.push(id), _ => other_items.push(id),
72 }
73 }
74 }
75
76 if !modules.is_empty() {
78 output.push_str(&format!("{} Modules\n\n", "#".repeat(heading_level)));
79 for id in modules {
80 process_item(output, data.index.get(&id).unwrap(), data, level + 1);
81 }
82 }
83
84 if !types.is_empty() {
85 output.push_str(&format!("{} Types\n\n", "#".repeat(heading_level)));
86 for id in types {
87 process_item(output, data.index.get(&id).unwrap(), data, level + 1);
88 }
89 }
90
91 if !traits.is_empty() {
92 output.push_str(&format!("{} Traits\n\n", "#".repeat(heading_level)));
93 for id in traits {
94 process_item(output, data.index.get(&id).unwrap(), data, level + 1);
95 }
96 }
97
98 if !functions.is_empty() {
99 output.push_str(&format!("{} Functions\n\n", "#".repeat(heading_level)));
100 for id in functions {
101 process_item(output, data.index.get(&id).unwrap(), data, level + 1);
102 }
103 }
104
105 if !constants.is_empty() {
106 output.push_str(&format!(
107 "{} Constants and Statics\n\n",
108 "#".repeat(heading_level)
109 ));
110 for id in constants {
111 process_item(output, data.index.get(&id).unwrap(), data, level + 1);
112 }
113 }
114
115 if !macros.is_empty() {
116 output.push_str(&format!("{} Macros\n\n", "#".repeat(heading_level)));
117 for id in macros {
118 process_item(output, data.index.get(&id).unwrap(), data, level + 1);
119 }
120 }
121
122 if !reexports.is_empty() {
123 output.push_str(&format!("{} Re-exports\n\n", "#".repeat(heading_level)));
124 for id in reexports {
125 process_item(output, data.index.get(&id).unwrap(), data, level + 1);
126 }
127 }
128
129 if !other_items.is_empty() {
130 output.push_str(&format!("{} Other Items\n\n", "#".repeat(heading_level)));
131 for id in other_items {
132 process_item(output, data.index.get(&id).unwrap(), data, level + 1);
133 }
134 }
135}
136
137fn process_item(output: &mut String, item: &Item, data: &Crate, level: usize) {
138 let heading_level = std::cmp::min(level, 6);
140 let heading = "#".repeat(heading_level);
141
142 match &item.inner {
144 ItemEnum::Use(use_item) => {
146 let source_name = use_item
148 .source
149 .split("::")
150 .last()
151 .unwrap_or(&use_item.source);
152
153 if use_item.is_glob {
155 output.push_str(&format!(
156 "{} Re-export `{}::*`\n\n",
157 heading, use_item.source
158 ));
159 } else if let Some(name) = &item.name {
160 if name != source_name {
161 output.push_str(&format!(
162 "{} Re-export `{}` as `{}`\n\n",
163 heading, source_name, name
164 ));
165 } else {
166 output.push_str(&format!("{} Re-export `{}`\n\n", heading, name));
167 }
168 } else {
169 output.push_str(&format!("{} Re-export `{}`\n\n", heading, source_name));
170 }
171 }
172 _ => {
173 if let Some(name) = &item.name {
175 match &item.inner {
176 ItemEnum::Module(_) => output.push_str(&format!("## Module `{}`\n\n", name)),
178 ItemEnum::Struct(_) => {
179 output.push_str(&format!("{} Struct `{}`\n\n", heading, name))
180 }
181 ItemEnum::Enum(_) => {
182 output.push_str(&format!("{} Enum `{}`\n\n", heading, name))
183 }
184 ItemEnum::Union(_) => {
185 output.push_str(&format!("{} Union `{}`\n\n", heading, name))
186 }
187 ItemEnum::Trait(_) => {
188 output.push_str(&format!("{} Trait `{}`\n\n", heading, name))
189 }
190 ItemEnum::TraitAlias(_) => {
191 output.push_str(&format!("{} Trait Alias `{}`\n\n", heading, name))
192 }
193 ItemEnum::Function(_) => {
194 output.push_str(&format!("{} Function `{}`\n\n", heading, name))
195 }
196 ItemEnum::TypeAlias(_) => {
197 output.push_str(&format!("{} Type Alias `{}`\n\n", heading, name))
198 }
199 ItemEnum::Constant { .. } => {
200 output.push_str(&format!("{} Constant `{}`\n\n", heading, name))
201 }
202 ItemEnum::Static(_) => {
203 output.push_str(&format!("{} Static `{}`\n\n", heading, name))
204 }
205 ItemEnum::Macro(_) => {
206 output.push_str(&format!("{} Macro `{}`\n\n", heading, name))
207 }
208 ItemEnum::ProcMacro(_) => {
209 output.push_str(&format!("{} Procedural Macro `{}`\n\n", heading, name))
210 }
211 ItemEnum::ExternCrate {
212 name: crate_name, ..
213 } => output.push_str(&format!("{} Extern Crate `{}`\n\n", heading, crate_name)),
214 _ => output.push_str(&format!("{} `{}`\n\n", heading, name)),
215 }
216 } else {
217 match &item.inner {
219 ItemEnum::Impl(impl_) => {
220 if let Some(trait_) = &impl_.trait_ {
221 output.push_str(&format!(
223 "{} Implementation of `{}` for `{}`\n\n",
224 heading,
225 trait_.path,
226 format_type(&impl_.for_, data)
227 ));
228 } else {
229 output.push_str(&format!(
231 "{} Implementation for `{}`\n\n",
232 heading,
233 format_type(&impl_.for_, data)
234 ));
235 }
236 }
237 _ => {
238 output.push_str(&format!("{} Unnamed Item\n\n", heading));
240 }
241 }
242 }
243 }
244 }
245
246 if !item.attrs.is_empty() {
248 output.push_str("**Attributes:**\n\n");
249 for attr in &item.attrs {
250 output.push_str(&format!("- `{:?}`\n", attr));
251 }
252 output.push('\n');
253 }
254
255 if let Some(deprecation) = &item.deprecation {
257 output.push_str("**⚠️ Deprecated");
258 if let Some(since) = &deprecation.since {
259 output.push_str(&format!(" since {}", since));
260 }
261 output.push_str("**");
262
263 if let Some(note) = &deprecation.note {
264 output.push_str(&format!(": {}", note));
265 }
266 output.push_str("\n\n");
267 }
268
269 if let Some(docs) = &item.docs {
271 output.push_str(&format!("{}\n\n", docs));
272 }
273
274 output.push_str("```rust\n");
276 format_item_signature(output, item, data);
277 output.push_str("\n```\n\n");
278
279 match &item.inner {
281 ItemEnum::Module(module) => process_module_details(output, module, data, level + 1),
282 ItemEnum::Struct(struct_) => process_struct_details(output, struct_, data, level + 1),
283 ItemEnum::Enum(enum_) => process_enum_details(output, enum_, data, level + 1),
284 ItemEnum::Union(union_) => process_union_details(output, union_, data, level + 1),
285 ItemEnum::Trait(trait_) => process_trait_details(output, trait_, data, level + 1),
286 ItemEnum::Impl(impl_) => process_impl_details(output, impl_, data, level + 1),
287 _ => {}
288 }
289}
290
291fn format_item_signature(output: &mut String, item: &Item, data: &Crate) {
292 match &item.visibility {
294 Visibility::Public => output.push_str("pub "),
295 Visibility::Crate => output.push_str("pub(crate) "),
296 Visibility::Restricted { path, .. } => output.push_str(&format!("pub(in {}) ", path)),
297 Visibility::Default => {}
298 }
299
300 match &item.inner {
302 ItemEnum::Module(_) => {
303 if let Some(name) = &item.name {
304 output.push_str(&format!("mod {} {{ /* ... */ }}", name));
305 }
306 }
307 ItemEnum::Struct(struct_) => {
308 if let Some(name) = &item.name {
309 output.push_str(&format!("struct {}", name));
310 format_generics(output, &struct_.generics, data);
311
312 match &struct_.kind {
313 StructKind::Unit => output.push(';'),
314 StructKind::Tuple(fields) => {
315 output.push('(');
316 for (i, field_opt) in fields.iter().enumerate() {
317 if let Some(field_id) = field_opt {
318 if let Some(field_item) = data.index.get(field_id) {
319 if let ItemEnum::StructField(field_type) = &field_item.inner {
320 match &field_item.visibility {
322 Visibility::Public => output.push_str("pub "),
323 Visibility::Crate => output.push_str("pub(crate) "),
324 Visibility::Restricted { path, .. } => {
325 output.push_str(&format!("pub(in {}) ", path))
326 }
327 Visibility::Default => {}
328 }
329 output.push_str(&format_type(field_type, data));
330 }
331 }
332 if i < fields.len() - 1 {
333 output.push_str(", ");
334 }
335 } else {
336 output.push_str("/* private field */");
338 if i < fields.len() - 1 {
339 output.push_str(", ");
340 }
341 }
342 }
343 output.push_str(");");
344 }
345 StructKind::Plain {
346 fields,
347 has_stripped_fields,
348 } => {
349 output.push_str(" {\n");
350 for &field_id in fields {
351 if let Some(field_item) = data.index.get(&field_id) {
352 if let Some(field_name) = &field_item.name {
353 if let ItemEnum::StructField(field_type) = &field_item.inner {
354 match &field_item.visibility {
356 Visibility::Public => output.push_str(" pub "),
357 Visibility::Crate => output.push_str(" pub(crate) "),
358 Visibility::Restricted { path, .. } => {
359 output.push_str(&format!(" pub(in {}) ", path))
360 }
361 Visibility::Default => output.push_str(" "),
362 }
363 output.push_str(&format!(
364 "{}: {},\n",
365 field_name,
366 format_type(field_type, data)
367 ));
368 }
369 }
370 }
371 }
372 if *has_stripped_fields {
373 output.push_str(" // Some fields omitted\n");
374 }
375 output.push('}');
376 }
377 }
378 }
379 }
380 ItemEnum::Enum(enum_) => {
381 if let Some(name) = &item.name {
382 output.push_str(&format!("enum {}", name));
383 format_generics(output, &enum_.generics, data);
384 output.push_str(" {\n");
385
386 for &variant_id in &enum_.variants {
387 if let Some(variant_item) = data.index.get(&variant_id) {
388 if let Some(variant_name) = &variant_item.name {
389 output.push_str(&format!(" {}", variant_name));
390
391 if let ItemEnum::Variant(variant) = &variant_item.inner {
392 match &variant.kind {
393 VariantKind::Plain => {}
394 VariantKind::Tuple(fields) => {
395 output.push('(');
396 for (i, field_opt) in fields.iter().enumerate() {
397 if let Some(field_id) = field_opt {
398 if let Some(field_item) = data.index.get(field_id) {
399 if let ItemEnum::StructField(field_type) =
400 &field_item.inner
401 {
402 output.push_str(&format_type(
403 field_type, data,
404 ));
405 }
406 }
407 if i < fields.len() - 1 {
408 output.push_str(", ");
409 }
410 } else {
411 output.push_str("/* private field */");
413 if i < fields.len() - 1 {
414 output.push_str(", ");
415 }
416 }
417 }
418 output.push(')');
419 }
420 VariantKind::Struct {
421 fields,
422 has_stripped_fields,
423 } => {
424 output.push_str(" {\n");
425 for &field_id in fields {
426 if let Some(field_item) = data.index.get(&field_id) {
427 if let Some(field_name) = &field_item.name {
428 if let ItemEnum::StructField(field_type) =
429 &field_item.inner
430 {
431 output.push_str(&format!(
432 " {}: {},\n",
433 field_name,
434 format_type(field_type, data)
435 ));
436 }
437 }
438 }
439 }
440 if *has_stripped_fields {
441 output.push_str(" // Some fields omitted\n");
442 }
443 output.push_str(" }");
444 }
445 }
446
447 if let Some(discriminant) = &variant.discriminant {
448 output.push_str(&format!(" = {}", discriminant.expr));
449 }
450 }
451
452 output.push_str(",\n");
453 }
454 }
455 }
456
457 if enum_.has_stripped_variants {
458 output.push_str(" // Some variants omitted\n");
459 }
460
461 output.push('}');
462 }
463 }
464 ItemEnum::Union(union_) => {
465 if let Some(name) = &item.name {
466 output.push_str(&format!("union {}", name));
467 format_generics(output, &union_.generics, data);
468 output.push_str(" {\n");
469
470 for &field_id in &union_.fields {
471 if let Some(field_item) = data.index.get(&field_id) {
472 if let Some(field_name) = &field_item.name {
473 if let ItemEnum::StructField(field_type) = &field_item.inner {
474 match &field_item.visibility {
475 Visibility::Public => output.push_str(" pub "),
476 Visibility::Crate => output.push_str(" pub(crate) "),
477 Visibility::Restricted { path, .. } => {
478 output.push_str(&format!(" pub(in {}) ", path))
479 }
480 Visibility::Default => output.push_str(" "),
481 }
482 output.push_str(&format!(
483 "{}: {},\n",
484 field_name,
485 format_type(field_type, data)
486 ));
487 }
488 }
489 }
490 }
491
492 if union_.has_stripped_fields {
493 output.push_str(" // Some fields omitted\n");
494 }
495
496 output.push('}');
497 }
498 }
499 ItemEnum::Function(function) => {
500 if function.header.is_const {
502 output.push_str("const ");
503 }
504 if function.header.is_unsafe {
505 output.push_str("unsafe ");
506 }
507 if function.header.is_async {
508 output.push_str("async ");
509 }
510
511 match &function.header.abi {
513 Abi::Rust => {}
514 Abi::C { unwind } => {
515 if *unwind {
516 output.push_str("extern \"C-unwind\" ");
517 } else {
518 output.push_str("extern \"C\" ");
519 }
520 }
521 Abi::Cdecl { unwind } => {
522 if *unwind {
523 output.push_str("extern \"cdecl-unwind\" ");
524 } else {
525 output.push_str("extern \"cdecl\" ");
526 }
527 }
528 Abi::Stdcall { unwind } => {
529 if *unwind {
530 output.push_str("extern \"stdcall-unwind\" ");
531 } else {
532 output.push_str("extern \"stdcall\" ");
533 }
534 }
535 Abi::Fastcall { unwind } => {
536 if *unwind {
537 output.push_str("extern \"fastcall-unwind\" ");
538 } else {
539 output.push_str("extern \"fastcall\" ");
540 }
541 }
542 Abi::Aapcs { unwind } => {
543 if *unwind {
544 output.push_str("extern \"aapcs-unwind\" ");
545 } else {
546 output.push_str("extern \"aapcs\" ");
547 }
548 }
549 Abi::Win64 { unwind } => {
550 if *unwind {
551 output.push_str("extern \"win64-unwind\" ");
552 } else {
553 output.push_str("extern \"win64\" ");
554 }
555 }
556 Abi::SysV64 { unwind } => {
557 if *unwind {
558 output.push_str("extern \"sysv64-unwind\" ");
559 } else {
560 output.push_str("extern \"sysv64\" ");
561 }
562 }
563 Abi::System { unwind } => {
564 if *unwind {
565 output.push_str("extern \"system-unwind\" ");
566 } else {
567 output.push_str("extern \"system\" ");
568 }
569 }
570 Abi::Other(abi) => {
571 output.push_str(&format!("extern \"{}\" ", abi));
572 }
573 }
574
575 if let Some(name) = &item.name {
577 output.push_str(&format!("fn {}", name));
578
579 format_generics(output, &function.generics, data);
581
582 output.push('(');
584 for (i, (param_name, param_type)) in function.sig.inputs.iter().enumerate() {
585 output.push_str(&format!(
586 "{}: {}",
587 param_name,
588 format_type(param_type, data)
589 ));
590 if i < function.sig.inputs.len() - 1 || function.sig.is_c_variadic {
591 output.push_str(", ");
592 }
593 }
594
595 if function.sig.is_c_variadic {
597 output.push_str("...");
598 }
599
600 output.push(')');
601
602 if let Some(return_type) = &function.sig.output {
604 output.push_str(&format!(" -> {}", format_type(return_type, data)));
605 }
606
607 format_where_clause(output, &function.generics.where_predicates, data);
609
610 if function.has_body {
612 output.push_str(" { /* ... */ }");
613 } else {
614 output.push(';');
615 }
616 }
617 }
618 ItemEnum::Trait(trait_) => {
619 if trait_.is_auto {
621 output.push_str("auto ");
622 }
623 if trait_.is_unsafe {
624 output.push_str("unsafe ");
625 }
626
627 if let Some(name) = &item.name {
629 output.push_str(&format!("trait {}", name));
630 format_generics(output, &trait_.generics, data);
631
632 if !trait_.bounds.is_empty() {
634 output.push_str(": ");
635 format_bounds(output, &trait_.bounds, data);
636 }
637
638 format_where_clause(output, &trait_.generics.where_predicates, data);
640
641 output.push_str(" {\n /* Associated items */\n}");
642 }
643 }
644 ItemEnum::TraitAlias(trait_alias) => {
645 if let Some(name) = &item.name {
646 output.push_str(&format!("trait {}", name));
647 format_generics(output, &trait_alias.generics, data);
648 output.push_str(" = ");
649 format_bounds(output, &trait_alias.params, data);
650 format_where_clause(output, &trait_alias.generics.where_predicates, data);
651 output.push(';');
652 }
653 }
654 ItemEnum::Impl(impl_) => {
655 if impl_.is_unsafe {
657 output.push_str("unsafe ");
658 }
659
660 output.push_str("impl");
661
662 format_generics(output, &impl_.generics, data);
664
665 if let Some(trait_) = &impl_.trait_ {
667 if impl_.is_negative {
668 output.push_str(" !");
669 } else {
670 output.push(' ');
671 }
672
673 output.push_str(&trait_.path);
674 if let Some(args) = &trait_.args {
675 let mut args_str = String::new();
676 format_generic_args(&mut args_str, args, data);
677 output.push_str(&args_str);
678 }
679
680 output.push_str(" for ");
681 }
682
683 output.push_str(&format_type(&impl_.for_, data));
685
686 format_where_clause(output, &impl_.generics.where_predicates, data);
688
689 output.push_str(" {\n /* Associated items */\n}");
690
691 if impl_.is_synthetic {
693 output.push_str("\n// Note: This impl is compiler-generated");
694 }
695 }
696 ItemEnum::TypeAlias(type_alias) => {
697 if let Some(name) = &item.name {
698 output.push_str(&format!("type {}", name));
699 format_generics(output, &type_alias.generics, data);
700 format_where_clause(output, &type_alias.generics.where_predicates, data);
701 output.push_str(&format!(" = {};", format_type(&type_alias.type_, data)));
702 }
703 }
704 ItemEnum::Constant { type_, const_ } => {
705 if let Some(name) = &item.name {
706 output.push_str(&format!(
707 "const {}: {} = {};",
708 name,
709 format_type(type_, data),
710 const_.expr
711 ));
712 }
713 }
714 ItemEnum::Static(static_) => {
715 if let Some(name) = &item.name {
716 output.push_str("static ");
717 if static_.is_mutable {
718 output.push_str("mut ");
719 }
720 if static_.is_unsafe {
721 output.push_str("/* unsafe */ ");
722 }
723 output.push_str(&format!(
724 "{}: {} = {};",
725 name,
726 format_type(&static_.type_, data),
727 static_.expr
728 ));
729 }
730 }
731 ItemEnum::Macro(macro_body) => {
732 if let Some(name) = &item.name {
733 output.push_str(&format!(
734 "macro_rules! {} {{\n /* {} */\n}}",
735 name, macro_body
736 ));
737 }
738 }
739 ItemEnum::ProcMacro(proc_macro) => {
740 if let Some(name) = &item.name {
741 output.push_str("#[proc_macro");
742 match proc_macro.kind {
743 MacroKind::Bang => output.push(']'),
744
745 MacroKind::Attr => output.push_str("_attribute]"),
746 MacroKind::Derive => {
747 output.push_str("_derive]");
748 if !proc_macro.helpers.is_empty() {
749 output.push_str("\n// Helpers: ");
750 for (i, helper) in proc_macro.helpers.iter().enumerate() {
751 output.push_str(&format!("#[{}]", helper));
752 if i < proc_macro.helpers.len() - 1 {
753 output.push_str(", ");
754 }
755 }
756 }
757 }
758 }
759 output.push_str(&format!(
760 "\npub fn {}(/* ... */) -> /* ... */ {{\n /* ... */\n}}",
761 name
762 ));
763 }
764 }
765 ItemEnum::ExternCrate { name, rename } => {
766 output.push_str(&format!("extern crate {}", name));
767 if let Some(rename_val) = rename {
768 output.push_str(&format!(" as {}", rename_val));
769 }
770 output.push(';');
771 }
772 ItemEnum::Use(use_item) => {
773 output.push_str(&format!("use {}", use_item.source));
774 if use_item.is_glob {
775 output.push_str("::*");
776 } else if use_item.name
777 != use_item
778 .source
779 .split("::")
780 .last()
781 .unwrap_or(&use_item.source)
782 {
783 output.push_str(&format!(" as {}", use_item.name));
784 }
785 output.push(';');
786 }
787 ItemEnum::StructField(field_type) => {
788 if let Some(name) = &item.name {
790 match &item.visibility {
791 Visibility::Public => output.push_str("pub "),
792 Visibility::Crate => output.push_str("pub(crate) "),
793 Visibility::Restricted { path, .. } => {
794 output.push_str(&format!("pub(in {}) ", path))
795 }
796 Visibility::Default => {}
797 }
798 output.push_str(&format!("{}: {}", name, format_type(field_type, data)));
799 } else {
800 output.push_str(&format_type(field_type, data));
801 }
802 }
803 ItemEnum::Variant(variant) => {
804 if let Some(name) = &item.name {
806 output.push_str(name);
807
808 match &variant.kind {
809 VariantKind::Plain => {}
810 VariantKind::Tuple(fields) => {
811 output.push('(');
812 for (i, field_opt) in fields.iter().enumerate() {
813 if let Some(field_id) = field_opt {
814 if let Some(field_item) = data.index.get(field_id) {
815 if let ItemEnum::StructField(field_type) = &field_item.inner {
816 output.push_str(&format_type(field_type, data));
817 }
818 }
819 if i < fields.len() - 1 {
820 output.push_str(", ");
821 }
822 } else {
823 output.push_str("/* private field */");
825 if i < fields.len() - 1 {
826 output.push_str(", ");
827 }
828 }
829 }
830 output.push(')');
831 }
832 VariantKind::Struct {
833 fields,
834 has_stripped_fields,
835 } => {
836 output.push_str(" {\n");
837 for &field_id in fields {
838 if let Some(field_item) = data.index.get(&field_id) {
839 if let Some(field_name) = &field_item.name {
840 if let ItemEnum::StructField(field_type) = &field_item.inner {
841 output.push_str(&format!(
842 " {}: {},\n",
843 field_name,
844 format_type(field_type, data)
845 ));
846 }
847 }
848 }
849 }
850 if *has_stripped_fields {
851 output.push_str(" // Some fields omitted\n");
852 }
853 output.push('}');
854 }
855 }
856
857 if let Some(discriminant) = &variant.discriminant {
858 output.push_str(&format!(" = {}", discriminant.expr));
859 }
860 }
861 }
862 ItemEnum::Primitive(primitive) => {
863 output.push_str(&format!("// Primitive type: {}", primitive.name));
864 }
865 ItemEnum::ExternType => {
866 if let Some(name) = &item.name {
867 output.push_str(&format!("extern {{ type {}; }}", name));
868 }
869 }
870 ItemEnum::AssocConst { type_, value } => {
871 if let Some(name) = &item.name {
872 output.push_str(&format!("const {}: {}", name, format_type(type_, data)));
873 if let Some(val) = value {
874 output.push_str(&format!(" = {}", val));
875 }
876 output.push(';');
877 }
878 }
879 ItemEnum::AssocType {
880 generics,
881 bounds,
882 type_,
883 } => {
884 if let Some(name) = &item.name {
885 output.push_str(&format!("type {}", name));
886 format_generics(output, generics, data);
887
888 if !bounds.is_empty() {
889 output.push_str(": ");
890 format_bounds(output, bounds, data);
891 }
892
893 if let Some(ty) = type_ {
894 output.push_str(&format!(" = {}", format_type(ty, data)));
895 }
896
897 format_where_clause(output, &generics.where_predicates, data);
898 output.push(';');
899 }
900 }
901 }
902}
903
904fn format_generics(output: &mut String, generics: &Generics, data: &Crate) {
905 if generics.params.is_empty() {
906 return;
907 }
908
909 output.push('<');
910 for (i, param) in generics.params.iter().enumerate() {
911 match ¶m.kind {
912 GenericParamDefKind::Lifetime { outlives } => {
913 output.push_str(&format!("'{}", param.name));
914 if !outlives.is_empty() {
915 output.push_str(": ");
916 for (j, lifetime) in outlives.iter().enumerate() {
917 output.push_str(&format!("'{}", lifetime));
918 if j < outlives.len() - 1 {
919 output.push_str(" + ");
920 }
921 }
922 }
923 }
924 GenericParamDefKind::Type {
925 bounds,
926 default,
927 is_synthetic,
928 } => {
929 if *is_synthetic {
931 output.push_str("/* synthetic */ ");
932 }
933
934 output.push_str(¶m.name);
935 if !bounds.is_empty() {
936 output.push_str(": ");
937 format_bounds(output, bounds, data);
938 }
939 if let Some(default_type) = default {
940 output.push_str(&format!(" = {}", format_type(default_type, data)));
941 }
942 }
943 GenericParamDefKind::Const { type_, default } => {
944 output.push_str(&format!(
945 "const {}: {}",
946 param.name,
947 format_type(type_, data)
948 ));
949 if let Some(default_value) = default {
950 output.push_str(&format!(" = {}", default_value));
951 }
952 }
953 }
954
955 if i < generics.params.len() - 1 {
956 output.push_str(", ");
957 }
958 }
959 output.push('>');
960}
961
962fn format_where_clause(output: &mut String, predicates: &[WherePredicate], data: &Crate) {
963 if predicates.is_empty() {
964 return;
965 }
966
967 output.push_str("\nwhere\n ");
968 for (i, predicate) in predicates.iter().enumerate() {
969 match predicate {
970 WherePredicate::BoundPredicate {
971 type_,
972 bounds,
973 generic_params,
974 } => {
975 if !generic_params.is_empty() {
976 output.push_str("for<");
977 for (j, param) in generic_params.iter().enumerate() {
978 match ¶m.kind {
979 GenericParamDefKind::Lifetime { .. } => {
980 output.push_str(&format!("'{}", param.name));
981 }
982 _ => output.push_str(¶m.name),
983 }
984
985 if j < generic_params.len() - 1 {
986 output.push_str(", ");
987 }
988 }
989 output.push_str("> ");
990 }
991
992 output.push_str(&format_type(type_, data));
993
994 if !bounds.is_empty() {
995 output.push_str(": ");
996 format_bounds(output, bounds, data);
997 }
998 }
999 WherePredicate::LifetimePredicate { lifetime, outlives } => {
1000 output.push_str(&format!("'{}", lifetime));
1001 if !outlives.is_empty() {
1002 output.push_str(": ");
1003 for (j, outlive) in outlives.iter().enumerate() {
1004 output.push_str(&format!("'{}", outlive));
1005 if j < outlives.len() - 1 {
1006 output.push_str(" + ");
1007 }
1008 }
1009 }
1010 }
1011 WherePredicate::EqPredicate { lhs, rhs } => {
1012 output.push_str(&format_type(lhs, data));
1013 output.push_str(" = ");
1014 match rhs {
1015 Term::Type(type_) => output.push_str(&format_type(type_, data)),
1016 Term::Constant(constant) => output.push_str(&constant.expr),
1017 }
1018 }
1019 }
1020
1021 if i < predicates.len() - 1 {
1022 output.push_str(",\n ");
1023 }
1024 }
1025}
1026
1027fn format_bounds(output: &mut String, bounds: &[GenericBound], data: &Crate) {
1028 for (i, bound) in bounds.iter().enumerate() {
1029 match bound {
1030 GenericBound::TraitBound {
1031 trait_,
1032 generic_params,
1033 modifier,
1034 } => {
1035 match modifier {
1036 TraitBoundModifier::None => {}
1037 TraitBoundModifier::Maybe => output.push('?'),
1038 TraitBoundModifier::MaybeConst => output.push_str("~const "),
1039 }
1040
1041 if !generic_params.is_empty() {
1042 output.push_str("for<");
1043 for (j, param) in generic_params.iter().enumerate() {
1044 match ¶m.kind {
1045 GenericParamDefKind::Lifetime { .. } => {
1046 output.push_str(&format!("'{}", param.name));
1047 }
1048 _ => output.push_str(¶m.name),
1049 }
1050
1051 if j < generic_params.len() - 1 {
1052 output.push_str(", ");
1053 }
1054 }
1055 output.push_str("> ");
1056 }
1057
1058 output.push_str(&trait_.path);
1059 if let Some(args) = &trait_.args {
1060 let mut args_str = String::new();
1061 format_generic_args(&mut args_str, args, data);
1062 output.push_str(&args_str);
1063 }
1064 }
1065 GenericBound::Outlives(lifetime) => {
1066 output.push_str(&format!("'{}", lifetime));
1067 }
1068 GenericBound::Use(args) => {
1069 output.push_str("use<");
1070 for (i, arg) in args.iter().enumerate() {
1071 match arg {
1072 PreciseCapturingArg::Lifetime(lifetime) => {
1073 output.push_str(&format!("'{}", lifetime))
1074 }
1075 PreciseCapturingArg::Param(param) => output.push_str(param),
1076 }
1077
1078 if i < args.len() - 1 {
1079 output.push_str(", ");
1080 }
1081 }
1082 output.push('>');
1083 }
1084 }
1085
1086 if i < bounds.len() - 1 {
1087 output.push_str(" + ");
1088 }
1089 }
1090}
1091
1092fn format_generic_args(output: &mut String, args: &GenericArgs, data: &Crate) {
1093 match args {
1094 GenericArgs::AngleBracketed { args, constraints } => {
1095 if args.is_empty() && constraints.is_empty() {
1096 return;
1097 }
1098
1099 output.push('<');
1100
1101 for (i, arg) in args.iter().enumerate() {
1103 match arg {
1104 GenericArg::Lifetime(lifetime) => output.push_str(&format!("'{}", lifetime)),
1105 GenericArg::Type(type_) => output.push_str(&format_type(type_, data)),
1106 GenericArg::Const(constant) => output.push_str(&constant.expr),
1107 GenericArg::Infer => output.push('_'),
1108 }
1109
1110 if i < args.len() - 1 || !constraints.is_empty() {
1111 output.push_str(", ");
1112 }
1113 }
1114
1115 for (i, constraint) in constraints.iter().enumerate() {
1117 output.push_str(&constraint.name.to_string());
1118
1119 if let Some(args) = &constraint.args {
1121 let mut args_str = String::new();
1122 format_generic_args(&mut args_str, &args, data);
1123 if !args_str.is_empty() && args_str != "<>" {
1124 output.push_str(&args_str);
1125 }
1126 }
1127
1128 match &constraint.binding {
1129 AssocItemConstraintKind::Equality(term) => {
1130 output.push_str(" = ");
1131 match term {
1132 Term::Type(type_) => output.push_str(&format_type(type_, data)),
1133 Term::Constant(constant) => output.push_str(&constant.expr),
1134 }
1135 }
1136 AssocItemConstraintKind::Constraint(bounds) => {
1137 output.push_str(": ");
1138 format_bounds(output, bounds, data);
1139 }
1140 }
1141
1142 if i < constraints.len() - 1 {
1143 output.push_str(", ");
1144 }
1145 }
1146
1147 output.push('>');
1148 }
1149 GenericArgs::Parenthesized {
1150 inputs,
1151 output: output_type,
1152 } => {
1153 output.push('(');
1154
1155 for (i, input) in inputs.iter().enumerate() {
1156 output.push_str(&format_type(input, data));
1157 if i < inputs.len() - 1 {
1158 output.push_str(", ");
1159 }
1160 }
1161
1162 output.push(')');
1163
1164 if let Some(output_ty) = output_type {
1165 output.push_str(&format!(" -> {}", format_type(output_ty, data)));
1166 }
1167 }
1168 GenericArgs::ReturnTypeNotation => {
1169 output.push_str("::method(..)");
1170 }
1171 }
1172}
1173
1174fn format_type(ty: &Type, data: &Crate) -> String {
1175 let mut output = String::new();
1176
1177 match ty {
1178 Type::ResolvedPath(path) => {
1179 output.push_str(&path.path);
1180 if let Some(args) = &path.args {
1181 let mut args_str = String::new();
1182 format_generic_args(&mut args_str, args, data);
1183 output.push_str(&args_str);
1184 }
1185 }
1186 Type::DynTrait(dyn_trait) => {
1187 output.push_str("dyn ");
1188
1189 for (i, trait_) in dyn_trait.traits.iter().enumerate() {
1190 if !trait_.generic_params.is_empty() {
1192 output.push_str("for<");
1193 for (j, param) in trait_.generic_params.iter().enumerate() {
1194 match ¶m.kind {
1195 GenericParamDefKind::Lifetime { .. } => {
1196 output.push_str(&format!("'{}", param.name));
1197 }
1198 _ => output.push_str(¶m.name),
1199 }
1200
1201 if j < trait_.generic_params.len() - 1 {
1202 output.push_str(", ");
1203 }
1204 }
1205 output.push_str("> ");
1206 }
1207
1208 output.push_str(&trait_.trait_.path);
1209 if let Some(args) = &trait_.trait_.args {
1210 let mut args_str = String::new();
1211 format_generic_args(&mut args_str, args, data);
1212 output.push_str(&args_str);
1213 }
1214
1215 if i < dyn_trait.traits.len() - 1 {
1216 output.push_str(" + ");
1217 }
1218 }
1219
1220 if let Some(lifetime) = &dyn_trait.lifetime {
1222 output.push_str(&format!(" + '{}", lifetime));
1223 }
1224 }
1225 Type::Generic(name) => {
1226 output.push_str(name);
1227 }
1228 Type::Primitive(name) => {
1229 output.push_str(name);
1230 }
1231 Type::FunctionPointer(fn_ptr) => {
1232 if !fn_ptr.generic_params.is_empty() {
1234 output.push_str("for<");
1235 for (j, param) in fn_ptr.generic_params.iter().enumerate() {
1236 match ¶m.kind {
1237 GenericParamDefKind::Lifetime { .. } => {
1238 output.push_str(&format!("'{}", param.name));
1239 }
1240 _ => output.push_str(¶m.name),
1241 }
1242
1243 if j < fn_ptr.generic_params.len() - 1 {
1244 output.push_str(", ");
1245 }
1246 }
1247 output.push_str("> ");
1248 }
1249
1250 if fn_ptr.header.is_const {
1252 output.push_str("const ");
1253 }
1254 if fn_ptr.header.is_unsafe {
1255 output.push_str("unsafe ");
1256 }
1257
1258 match &fn_ptr.header.abi {
1260 Abi::Rust => {}
1261 Abi::C { unwind } => {
1262 if *unwind {
1263 output.push_str("extern \"C-unwind\" ");
1264 } else {
1265 output.push_str("extern \"C\" ");
1266 }
1267 }
1268 Abi::Cdecl { unwind } => {
1269 if *unwind {
1270 output.push_str("extern \"cdecl-unwind\" ");
1271 } else {
1272 output.push_str("extern \"cdecl\" ");
1273 }
1274 }
1275 Abi::Stdcall { unwind } => {
1276 if *unwind {
1277 output.push_str("extern \"stdcall-unwind\" ");
1278 } else {
1279 output.push_str("extern \"stdcall\" ");
1280 }
1281 }
1282 Abi::Fastcall { unwind } => {
1283 if *unwind {
1284 output.push_str("extern \"fastcall-unwind\" ");
1285 } else {
1286 output.push_str("extern \"fastcall\" ");
1287 }
1288 }
1289 Abi::Aapcs { unwind } => {
1290 if *unwind {
1291 output.push_str("extern \"aapcs-unwind\" ");
1292 } else {
1293 output.push_str("extern \"aapcs\" ");
1294 }
1295 }
1296 Abi::Win64 { unwind } => {
1297 if *unwind {
1298 output.push_str("extern \"win64-unwind\" ");
1299 } else {
1300 output.push_str("extern \"win64\" ");
1301 }
1302 }
1303 Abi::SysV64 { unwind } => {
1304 if *unwind {
1305 output.push_str("extern \"sysv64-unwind\" ");
1306 } else {
1307 output.push_str("extern \"sysv64\" ");
1308 }
1309 }
1310 Abi::System { unwind } => {
1311 if *unwind {
1312 output.push_str("extern \"system-unwind\" ");
1313 } else {
1314 output.push_str("extern \"system\" ");
1315 }
1316 }
1317 Abi::Other(abi) => {
1318 output.push_str(&format!("extern \"{}\" ", abi));
1319 }
1320 }
1321
1322 output.push_str("fn(");
1323
1324 for (i, (_, param_type)) in fn_ptr.sig.inputs.iter().enumerate() {
1326 output.push_str(&format_type(param_type, data));
1327 if i < fn_ptr.sig.inputs.len() - 1 || fn_ptr.sig.is_c_variadic {
1328 output.push_str(", ");
1329 }
1330 }
1331
1332 if fn_ptr.sig.is_c_variadic {
1334 output.push_str("...");
1335 }
1336
1337 output.push(')');
1338
1339 if let Some(return_type) = &fn_ptr.sig.output {
1341 output.push_str(&format!(" -> {}", format_type(return_type, data)));
1342 }
1343 }
1344 Type::Tuple(types) => {
1345 if types.is_empty() {
1346 output.push_str("()");
1347 } else {
1348 output.push('(');
1349 for (i, ty) in types.iter().enumerate() {
1350 output.push_str(&format_type(ty, data));
1351 if i < types.len() - 1 {
1352 output.push_str(", ");
1353 }
1354 }
1355 output.push(')');
1356 }
1357 }
1358 Type::Slice(ty) => {
1359 output.push_str(&format!("[{}]", format_type(ty, data)));
1360 }
1361 Type::Array { type_, len } => {
1362 output.push_str(&format!("[{}; {}]", format_type(type_, data), len));
1363 }
1364 Type::Pat {
1365 type_,
1366 __pat_unstable_do_not_use,
1367 } => {
1368 output.push_str(&format!(
1369 "{} is {}",
1370 format_type(type_, data),
1371 __pat_unstable_do_not_use
1372 ));
1373 }
1374 Type::ImplTrait(bounds) => {
1375 output.push_str("impl ");
1376
1377 let mut bounds_str = String::new();
1378 format_bounds(&mut bounds_str, bounds, data);
1379 output.push_str(&bounds_str);
1380 }
1381 Type::Infer => {
1382 output.push('_');
1383 }
1384 Type::RawPointer { is_mutable, type_ } => {
1385 if *is_mutable {
1386 output.push_str("*mut ");
1387 } else {
1388 output.push_str("*const ");
1389 }
1390 output.push_str(&format_type(type_, data));
1391 }
1392 Type::BorrowedRef {
1393 lifetime,
1394 is_mutable,
1395 type_,
1396 } => {
1397 output.push('&');
1398 if let Some(lt) = lifetime {
1399 output.push_str(&format!("'{} ", lt));
1400 }
1401 if *is_mutable {
1402 output.push_str("mut ");
1403 }
1404 output.push_str(&format_type(type_, data));
1405 }
1406 Type::QualifiedPath {
1407 name,
1408 args,
1409 self_type,
1410 trait_,
1411 } => {
1412 output.push('<');
1413 output.push_str(&format_type(self_type, data));
1414
1415 if let Some(trait_path) = trait_ {
1416 output.push_str(&format!(" as {}", trait_path.path));
1417 if let Some(trait_args) = &trait_path.args {
1418 let mut args_str = String::new();
1419 format_generic_args(&mut args_str, trait_args, data);
1420 output.push_str(&args_str);
1421 }
1422 }
1423
1424 output.push_str(&format!(">::{}", name));
1425
1426 if let Some(args) = args {
1427 let mut args_str = String::new();
1428 format_generic_args(&mut args_str, args, data);
1429 if args_str != "<>" && !args_str.is_empty() {
1430 output.push_str(&args_str);
1431 }
1432 }
1433 }
1434 }
1435
1436 output
1437}
1438
1439fn process_module_details(output: &mut String, module: &Module, data: &Crate, _level: usize) {
1440 if module.is_stripped {
1441 output.push_str(
1442 "> **Note:** This module is marked as stripped. Some items may be omitted.\n\n",
1443 );
1444 }
1445
1446 process_items(output, &module.items, data, 3);
1449}
1450
1451fn process_struct_details(output: &mut String, struct_: &Struct, data: &Crate, level: usize) {
1452 let heading_level = std::cmp::min(level, 6);
1454 match &struct_.kind {
1456 StructKind::Unit => {
1457 }
1459 StructKind::Tuple(fields) => {
1460 output.push_str(&format!("{} Fields\n\n", "#".repeat(heading_level)));
1462 output.push_str("| Index | Type | Documentation |\n");
1463 output.push_str("|-------|------|---------------|\n");
1464
1465 for (i, field_opt) in fields.iter().enumerate() {
1466 if let Some(field_id) = field_opt {
1467 if let Some(field_item) = data.index.get(field_id) {
1468 if let ItemEnum::StructField(field_type) = &field_item.inner {
1469 let docs = field_item
1470 .docs
1471 .as_deref()
1472 .unwrap_or("")
1473 .replace("\n", "<br>");
1474 output.push_str(&format!(
1475 "| {} | `{}` | {} |\n",
1476 i,
1477 format_type(field_type, data),
1478 docs
1479 ));
1480 }
1481 }
1482 } else {
1483 output.push_str(&format!("| {} | `private` | *Private field* |\n", i));
1484 }
1485 }
1486 output.push('\n');
1487 }
1488 StructKind::Plain {
1489 fields,
1490 has_stripped_fields,
1491 } => {
1492 output.push_str(&format!("{} Fields\n\n", "#".repeat(heading_level)));
1494 output.push_str("| Name | Type | Documentation |\n");
1495 output.push_str("|------|------|---------------|\n");
1496
1497 for &field_id in fields {
1498 if let Some(field_item) = data.index.get(&field_id) {
1499 if let Some(field_name) = &field_item.name {
1500 if let ItemEnum::StructField(field_type) = &field_item.inner {
1501 let docs = field_item
1502 .docs
1503 .as_deref()
1504 .unwrap_or("")
1505 .replace("\n", "<br>");
1506 output.push_str(&format!(
1507 "| `{}` | `{}` | {} |\n",
1508 field_name,
1509 format_type(field_type, data),
1510 docs
1511 ));
1512 }
1513 }
1514 }
1515 }
1516
1517 if *has_stripped_fields {
1518 output.push_str("| *private fields* | ... | *Some fields have been omitted* |\n");
1519 }
1520
1521 output.push('\n');
1522 }
1523 }
1524
1525 if !struct_.impls.is_empty() {
1527 output.push_str(&format!(
1529 "{} Implementations\n\n",
1530 "#".repeat(heading_level)
1531 ));
1532
1533 let mut trait_impls: std::collections::HashMap<String, Vec<Id>> =
1535 std::collections::HashMap::new();
1536 let mut inherent_impls: Vec<Id> = Vec::new();
1537
1538 for &impl_id in &struct_.impls {
1539 if let Some(impl_item) = data.index.get(&impl_id) {
1540 if let ItemEnum::Impl(impl_) = &impl_item.inner {
1541 if let Some(trait_) = &impl_.trait_ {
1542 let trait_name = trait_.path.clone();
1543 trait_impls.entry(trait_name).or_default().push(impl_id);
1544 } else {
1545 inherent_impls.push(impl_id);
1547 }
1548 }
1549 }
1550 }
1551
1552 if !inherent_impls.is_empty() {
1554 output.push_str(&format!(
1556 "{} Methods\n\n",
1557 "#".repeat(std::cmp::min(heading_level + 1, 6))
1558 ));
1559 for &impl_id in &inherent_impls {
1560 if let Some(impl_item) = data.index.get(&impl_id) {
1561 if let ItemEnum::Impl(impl_) = &impl_item.inner {
1562 for &item_id in &impl_.items {
1563 if let Some(method_item) = data.index.get(&item_id) {
1564 if let ItemEnum::Function(_) = &method_item.inner {
1565 let mut method_signature = String::new();
1567 format_item_signature(&mut method_signature, method_item, data);
1568
1569 output.push_str("- ```rust\n ");
1571 output.push_str(&method_signature.trim());
1572 output.push_str("\n ```");
1573
1574 if let Some(docs) = &method_item.docs {
1576 if let Some(first_line) = docs.lines().next() {
1577 if !first_line.trim().is_empty() {
1578 output.push_str(&format!("\n {}", first_line));
1579 }
1580 }
1581 }
1582 output.push_str("\n\n");
1583 }
1584 }
1585 }
1586 }
1587 }
1588 }
1589 }
1590
1591 if !trait_impls.is_empty() {
1593 output.push_str(&format!(
1595 "{} Trait Implementations\n\n",
1596 "#".repeat(std::cmp::min(heading_level + 1, 6))
1597 ));
1598 let mut sorted_trait_impls: Vec<_> = trait_impls.into_iter().collect();
1600 sorted_trait_impls.sort_by(|a, b| a.0.cmp(&b.0));
1601 for (trait_name, impls) in sorted_trait_impls {
1602 output.push_str(&format!("- **{}**\n", trait_name));
1603 for &impl_id in &impls {
1604 if let Some(impl_item) = data.index.get(&impl_id) {
1605 if let ItemEnum::Impl(impl_) = &impl_item.inner {
1606 for &item_id in &impl_.items {
1607 if let Some(method_item) = data.index.get(&item_id) {
1608 if let ItemEnum::Function(_) = &method_item.inner {
1609 let mut method_signature = String::new();
1611 format_item_signature(
1612 &mut method_signature,
1613 method_item,
1614 data,
1615 );
1616
1617 output.push_str(" - ```rust\n ");
1619 output.push_str(&method_signature.trim());
1620 output.push_str("\n ```");
1621
1622 if let Some(docs) = &method_item.docs {
1624 if let Some(first_line) = docs.lines().next() {
1625 if !first_line.trim().is_empty() {
1626 output
1627 .push_str(&format!("\n {}", first_line));
1628 }
1629 }
1630 }
1631 output.push_str("\n\n");
1632 }
1633 }
1634 }
1635 }
1636 }
1637 }
1638 }
1639 }
1640 }
1641}
1642
1643fn process_enum_details(output: &mut String, enum_: &Enum, data: &Crate, level: usize) {
1644 let heading_level = std::cmp::min(level, 6);
1646 output.push_str(&format!("{} Variants\n\n", "#".repeat(heading_level)));
1648
1649 for &variant_id in &enum_.variants {
1650 if let Some(variant_item) = data.index.get(&variant_id) {
1651 if let Some(variant_name) = &variant_item.name {
1652 let variant_heading_level = std::cmp::min(heading_level + 1, 6);
1654 output.push_str(&format!(
1655 "{} `{}`\n\n",
1656 "#".repeat(variant_heading_level),
1657 variant_name
1658 ));
1659
1660 if let Some(docs) = &variant_item.docs {
1662 output.push_str(&format!("{}\n\n", docs));
1663 }
1664
1665 if let ItemEnum::Variant(variant) = &variant_item.inner {
1666 match &variant.kind {
1667 VariantKind::Plain => {
1668 if let Some(discriminant) = &variant.discriminant {
1670 output.push_str(&format!(
1671 "Discriminant: `{}`\n\n",
1672 discriminant.expr
1673 ));
1674 }
1675 }
1676 VariantKind::Tuple(fields) => {
1677 output.push_str("Fields:\n\n");
1678 output.push_str("| Index | Type | Documentation |\n");
1679 output.push_str("|-------|------|---------------|\n");
1680
1681 for (i, field_opt) in fields.iter().enumerate() {
1682 if let Some(field_id) = field_opt {
1683 if let Some(field_item) = data.index.get(field_id) {
1684 if let ItemEnum::StructField(field_type) = &field_item.inner
1685 {
1686 let docs = field_item
1687 .docs
1688 .as_deref()
1689 .unwrap_or("")
1690 .replace("\n", "<br>");
1691 output.push_str(&format!(
1692 "| {} | `{}` | {} |\n",
1693 i,
1694 format_type(field_type, data),
1695 docs
1696 ));
1697 }
1698 }
1699 } else {
1700 output.push_str(&format!(
1701 "| {} | `private` | *Private field* |\n",
1702 i
1703 ));
1704 }
1705 }
1706 output.push('\n');
1707 }
1708 VariantKind::Struct {
1709 fields,
1710 has_stripped_fields,
1711 } => {
1712 output.push_str("Fields:\n\n");
1713 output.push_str("| Name | Type | Documentation |\n");
1714 output.push_str("|------|------|---------------|\n");
1715
1716 for &field_id in fields {
1717 if let Some(field_item) = data.index.get(&field_id) {
1718 if let Some(field_name) = &field_item.name {
1719 if let ItemEnum::StructField(field_type) = &field_item.inner
1720 {
1721 let docs = field_item
1722 .docs
1723 .as_deref()
1724 .unwrap_or("")
1725 .replace("\n", "<br>");
1726 output.push_str(&format!(
1727 "| `{}` | `{}` | {} |\n",
1728 field_name,
1729 format_type(field_type, data),
1730 docs
1731 ));
1732 }
1733 }
1734 }
1735 }
1736
1737 if *has_stripped_fields {
1738 output.push_str("| *private fields* | ... | *Some fields have been omitted* |\n");
1739 }
1740
1741 output.push('\n');
1742 }
1743 }
1744
1745 if let Some(discriminant) = &variant.discriminant {
1746 output
1747 .push_str(&format!("Discriminant value: `{}`\n\n", discriminant.value));
1748 }
1749 }
1750 }
1751 }
1752 }
1753
1754 if enum_.has_stripped_variants {
1755 output.push_str(
1756 "*Note: Some variants have been omitted because they are private or hidden.*\n\n",
1757 );
1758 }
1759
1760 if !enum_.impls.is_empty() {
1762 output.push_str(&format!(
1763 "{} Implementations\n\n",
1764 "#".repeat(heading_level)
1765 ));
1766
1767 let mut trait_impls: std::collections::HashMap<String, Vec<Id>> =
1769 std::collections::HashMap::new();
1770 let mut inherent_impls: Vec<Id> = Vec::new();
1771
1772 for &impl_id in &enum_.impls {
1773 if let Some(impl_item) = data.index.get(&impl_id) {
1774 if let ItemEnum::Impl(impl_) = &impl_item.inner {
1775 if let Some(trait_) = &impl_.trait_ {
1776 let trait_name = trait_.path.clone();
1777 trait_impls.entry(trait_name).or_default().push(impl_id);
1778 } else {
1779 inherent_impls.push(impl_id);
1781 }
1782 }
1783 }
1784 }
1785
1786 if !inherent_impls.is_empty() {
1788 let methods_level = std::cmp::min(heading_level + 1, 6);
1789 output.push_str(&format!("{} Methods\n\n", "#".repeat(methods_level)));
1790 for &impl_id in &inherent_impls {
1791 if let Some(impl_item) = data.index.get(&impl_id) {
1792 if let ItemEnum::Impl(impl_) = &impl_item.inner {
1793 for &item_id in &impl_.items {
1794 if let Some(method_item) = data.index.get(&item_id) {
1795 if let ItemEnum::Function(_) = &method_item.inner {
1796 let mut method_signature = String::new();
1798 format_item_signature(&mut method_signature, method_item, data);
1799
1800 output.push_str("- ```rust\n ");
1802 output.push_str(&method_signature.trim());
1803 output.push_str("\n ```");
1804
1805 if let Some(docs) = &method_item.docs {
1807 if let Some(first_line) = docs.lines().next() {
1808 if !first_line.trim().is_empty() {
1809 output.push_str(&format!("\n {}", first_line));
1810 }
1811 }
1812 }
1813 output.push_str("\n\n");
1814 }
1815 }
1816 }
1817 }
1818 }
1819 }
1820 }
1821
1822 if !trait_impls.is_empty() {
1824 let trait_impl_level = std::cmp::min(heading_level + 1, 6);
1825 output.push_str(&format!(
1826 "{} Trait Implementations\n\n",
1827 "#".repeat(trait_impl_level)
1828 ));
1829 let mut sorted_trait_impls: Vec<_> = trait_impls.into_iter().collect();
1831 sorted_trait_impls.sort_by(|a, b| a.0.cmp(&b.0));
1832 for (trait_name, impls) in sorted_trait_impls {
1833 output.push_str(&format!("- **{}**\n", trait_name));
1834 for &impl_id in &impls {
1835 if let Some(impl_item) = data.index.get(&impl_id) {
1836 if let ItemEnum::Impl(impl_) = &impl_item.inner {
1837 for &item_id in &impl_.items {
1838 if let Some(method_item) = data.index.get(&item_id) {
1839 if let ItemEnum::Function(_) = &method_item.inner {
1840 let mut method_signature = String::new();
1842 format_item_signature(
1843 &mut method_signature,
1844 method_item,
1845 data,
1846 );
1847
1848 output.push_str(" - ```rust\n ");
1850 output.push_str(&method_signature.trim());
1851 output.push_str("\n ```");
1852
1853 if let Some(docs) = &method_item.docs {
1855 if let Some(first_line) = docs.lines().next() {
1856 if !first_line.trim().is_empty() {
1857 output
1858 .push_str(&format!("\n {}", first_line));
1859 }
1860 }
1861 }
1862 output.push_str("\n\n");
1863 }
1864 }
1865 }
1866 }
1867 }
1868 }
1869 }
1870 }
1871 }
1872}
1873
1874fn process_union_details(output: &mut String, union_: &Union, data: &Crate, level: usize) {
1875 let heading_level = std::cmp::min(level, 6);
1877 output.push_str(&format!("{} Fields\n\n", "#".repeat(heading_level)));
1879 output.push_str("| Name | Type | Documentation |\n");
1880 output.push_str("|------|------|---------------|\n");
1881
1882 for &field_id in &union_.fields {
1883 if let Some(field_item) = data.index.get(&field_id) {
1884 if let Some(field_name) = &field_item.name {
1885 if let ItemEnum::StructField(field_type) = &field_item.inner {
1886 let docs = field_item
1887 .docs
1888 .as_deref()
1889 .unwrap_or("")
1890 .replace("\n", "<br>");
1891 output.push_str(&format!(
1892 "| `{}` | `{}` | {} |\n",
1893 field_name,
1894 format_type(field_type, data),
1895 docs
1896 ));
1897 }
1898 }
1899 }
1900 }
1901
1902 if union_.has_stripped_fields {
1903 output.push_str("| *private fields* | ... | *Some fields have been omitted* |\n");
1904 }
1905
1906 output.push('\n');
1907
1908 if !union_.impls.is_empty() {
1910 output.push_str(&format!(
1911 "{} Implementations\n\n",
1912 "#".repeat(heading_level)
1913 ));
1914
1915 let mut trait_impls: std::collections::HashMap<String, Vec<Id>> =
1917 std::collections::HashMap::new();
1918 let mut inherent_impls: Vec<Id> = Vec::new();
1919
1920 for &impl_id in &union_.impls {
1921 if let Some(impl_item) = data.index.get(&impl_id) {
1922 if let ItemEnum::Impl(impl_) = &impl_item.inner {
1923 if let Some(trait_) = &impl_.trait_ {
1924 let trait_name = trait_.path.clone();
1925 trait_impls.entry(trait_name).or_default().push(impl_id);
1926 } else {
1927 inherent_impls.push(impl_id);
1929 }
1930 }
1931 }
1932 }
1933
1934 if !inherent_impls.is_empty() {
1936 let methods_level = std::cmp::min(heading_level + 1, 6);
1937 output.push_str(&format!("{} Methods\n\n", "#".repeat(methods_level)));
1938 for &impl_id in &inherent_impls {
1939 if let Some(impl_item) = data.index.get(&impl_id) {
1940 if let ItemEnum::Impl(impl_) = &impl_item.inner {
1941 for &item_id in &impl_.items {
1942 if let Some(method_item) = data.index.get(&item_id) {
1943 if let ItemEnum::Function(_) = &method_item.inner {
1944 if let Some(name) = &method_item.name {
1945 output.push_str(&format!("- `{}`: ", name));
1946 if let Some(docs) = &method_item.docs {
1947 let first_line = docs.lines().next().unwrap_or("");
1948 output.push_str(first_line);
1949 }
1950 output.push('\n');
1951 }
1952 }
1953 }
1954 }
1955 }
1956 }
1957 }
1958 output.push('\n');
1959 }
1960
1961 if !trait_impls.is_empty() {
1963 let trait_impl_level = std::cmp::min(heading_level + 1, 6);
1964 output.push_str(&format!(
1965 "{} Trait Implementations\n\n",
1966 "#".repeat(trait_impl_level)
1967 ));
1968 let mut sorted_trait_impls: Vec<_> = trait_impls.into_iter().collect();
1970 sorted_trait_impls.sort_by(|a, b| a.0.cmp(&b.0));
1971 for (trait_name, impls) in sorted_trait_impls {
1972 output.push_str(&format!("- **{}**\n", trait_name));
1973 for &impl_id in &impls {
1974 if let Some(impl_item) = data.index.get(&impl_id) {
1975 if let ItemEnum::Impl(impl_) = &impl_item.inner {
1976 for &item_id in &impl_.items {
1977 if let Some(method_item) = data.index.get(&item_id) {
1978 if let Some(name) = &method_item.name {
1979 output.push_str(&format!(" - `{}`: ", name));
1980 if let Some(docs) = &method_item.docs {
1981 let first_line = docs.lines().next().unwrap_or("");
1982 output.push_str(first_line);
1983 }
1984 output.push('\n');
1985 }
1986 }
1987 }
1988 }
1989 }
1990 }
1991 }
1992 output.push('\n');
1993 }
1994 }
1995}
1996
1997fn process_trait_details(output: &mut String, trait_: &Trait, data: &Crate, level: usize) {
1998 let heading_level = std::cmp::min(level, 6);
2000 if trait_.is_auto {
2002 output.push_str("> This is an auto trait.\n\n");
2003 }
2004 if trait_.is_unsafe {
2005 output.push_str("> This trait is unsafe to implement.\n\n");
2006 }
2007 if !trait_.is_dyn_compatible {
2008 output.push_str(
2009 "> This trait is not object-safe and cannot be used in dynamic trait objects.\n\n",
2010 );
2011 }
2012
2013 if !trait_.items.is_empty() {
2015 let mut required_methods = Vec::new();
2017 let mut provided_methods = Vec::new();
2018 let mut assoc_types = Vec::new();
2019 let mut assoc_consts = Vec::new();
2020
2021 for &item_id in &trait_.items {
2022 if let Some(item) = data.index.get(&item_id) {
2023 match &item.inner {
2024 ItemEnum::Function(function) => {
2025 if function.has_body {
2026 provided_methods.push(item_id);
2027 } else {
2028 required_methods.push(item_id);
2029 }
2030 }
2031 ItemEnum::AssocType { .. } => assoc_types.push(item_id),
2032 ItemEnum::AssocConst { value, .. } => {
2033 if value.is_some() {
2034 provided_methods.push(item_id);
2036 } else {
2037 assoc_consts.push(item_id);
2038 }
2039 }
2040 _ => {}
2041 }
2042 }
2043 }
2044
2045 if !required_methods.is_empty() || !assoc_types.is_empty() || !assoc_consts.is_empty() {
2047 output.push_str(&format!("{} Required Items\n\n", "#".repeat(heading_level)));
2048
2049 if !assoc_types.is_empty() {
2050 output.push_str(&format!(
2051 "{} Associated Types\n\n",
2052 "#".repeat(heading_level + 1)
2053 ));
2054 for &type_id in &assoc_types {
2055 if let Some(type_item) = data.index.get(&type_id) {
2056 if let Some(name) = &type_item.name {
2057 output.push_str(&format!("- `{}`", name));
2058 if let Some(docs) = &type_item.docs {
2059 if let Some(first_line) = docs.lines().next() {
2060 if !first_line.trim().is_empty() {
2061 output.push_str(&format!(": {}", first_line));
2062 }
2063 }
2064 }
2065 output.push('\n');
2066 }
2067 }
2068 }
2069 output.push('\n');
2070 }
2071
2072 if !assoc_consts.is_empty() {
2073 output.push_str(&format!(
2074 "{} Associated Constants\n\n",
2075 "#".repeat(heading_level + 1)
2076 ));
2077 for &const_id in &assoc_consts {
2078 if let Some(const_item) = data.index.get(&const_id) {
2079 if let Some(name) = &const_item.name {
2080 output.push_str(&format!("- `{}`", name));
2081 if let Some(docs) = &const_item.docs {
2082 if let Some(first_line) = docs.lines().next() {
2083 if !first_line.trim().is_empty() {
2084 output.push_str(&format!(": {}", first_line));
2085 }
2086 }
2087 }
2088 output.push('\n');
2089 }
2090 }
2091 }
2092 output.push('\n');
2093 }
2094
2095 if !required_methods.is_empty() {
2096 output.push_str(&format!(
2097 "{} Required Methods\n\n",
2098 "#".repeat(heading_level + 1)
2099 ));
2100 for &method_id in &required_methods {
2101 if let Some(method_item) = data.index.get(&method_id) {
2102 if let Some(name) = &method_item.name {
2103 output.push_str(&format!("- `{}`", name));
2104 if let Some(docs) = &method_item.docs {
2105 if let Some(first_line) = docs.lines().next() {
2106 if !first_line.trim().is_empty() {
2107 output.push_str(&format!(": {}", first_line));
2108 }
2109 }
2110 }
2111 output.push('\n');
2112 }
2113 }
2114 }
2115 output.push('\n');
2116 }
2117 }
2118
2119 if !provided_methods.is_empty() {
2121 output.push_str(&format!(
2122 "{} Provided Methods\n\n",
2123 "#".repeat(heading_level)
2124 ));
2125 for &method_id in &provided_methods {
2126 if let Some(method_item) = data.index.get(&method_id) {
2127 if let ItemEnum::Function(_) = &method_item.inner {
2128 let mut method_signature = String::new();
2130 format_item_signature(&mut method_signature, method_item, data);
2131
2132 output.push_str("- ```rust\n ");
2134 output.push_str(&method_signature.trim());
2135 output.push_str("\n ```");
2136
2137 if let Some(docs) = &method_item.docs {
2139 if let Some(first_line) = docs.lines().next() {
2140 if !first_line.trim().is_empty() {
2141 output.push_str(&format!("\n {}", first_line));
2142 }
2143 }
2144 }
2145 output.push_str("\n\n");
2146 }
2147 }
2148 }
2149 }
2150 }
2151
2152 if !trait_.implementations.is_empty() {
2154 output.push_str(&format!(
2155 "{} Implementations\n\n",
2156 "#".repeat(heading_level)
2157 ));
2158 output.push_str("This trait is implemented for the following types:\n\n");
2159
2160 for &impl_id in &trait_.implementations {
2161 if let Some(impl_item) = data.index.get(&impl_id) {
2162 if let ItemEnum::Impl(impl_) = &impl_item.inner {
2163 output.push_str(&format!("- `{}`", format_type(&impl_.for_, data)));
2164 if !impl_.generics.params.is_empty() {
2166 let mut generics_str = String::new();
2167 format_generics(&mut generics_str, &impl_.generics, data);
2168 if generics_str != "<>" {
2169 output.push_str(" with ");
2170 output.push_str(&generics_str);
2171 }
2172 }
2173 output.push('\n');
2174 }
2175 }
2176 }
2177 output.push('\n');
2178 }
2179}
2180
2181fn process_impl_details(output: &mut String, impl_: &Impl, data: &Crate, level: usize) {
2182 let heading_level = std::cmp::min(level, 6);
2184 if !impl_.items.is_empty() {
2186 output.push_str(&format!(
2187 "{} Associated Items\n\n",
2188 "#".repeat(heading_level)
2189 ));
2190
2191 let mut methods = Vec::new();
2193 let mut assoc_types = Vec::new();
2194 let mut assoc_consts = Vec::new();
2195
2196 for &item_id in &impl_.items {
2197 if let Some(item) = data.index.get(&item_id) {
2198 match &item.inner {
2199 ItemEnum::Function(_) => methods.push(item_id),
2200 ItemEnum::AssocType { .. } => assoc_types.push(item_id),
2201 ItemEnum::AssocConst { .. } => assoc_consts.push(item_id),
2202 _ => {}
2203 }
2204 }
2205 }
2206
2207 if !assoc_types.is_empty() {
2208 output.push_str(&format!(
2209 "{} Associated Types\n\n",
2210 "#".repeat(heading_level + 1)
2211 ));
2212 for &type_id in &assoc_types {
2213 process_item(output, data.index.get(&type_id).unwrap(), data, level + 1);
2214 }
2215 }
2216
2217 if !assoc_consts.is_empty() {
2218 output.push_str(&format!(
2219 "{} Associated Constants\n\n",
2220 "#".repeat(heading_level + 1)
2221 ));
2222 for &const_id in &assoc_consts {
2223 process_item(output, data.index.get(&const_id).unwrap(), data, level + 1);
2224 }
2225 }
2226
2227 if !methods.is_empty() {
2228 output.push_str(&format!("{} Methods\n\n", "#".repeat(heading_level + 1)));
2229 for &method_id in &methods {
2230 process_item(output, data.index.get(&method_id).unwrap(), data, level + 1);
2231 }
2232 }
2233 }
2234
2235 if impl_.trait_.is_some() && !impl_.provided_trait_methods.is_empty() {
2237 output.push_str(&format!(
2238 "{} Provided Trait Methods\n\n",
2239 "#".repeat(heading_level)
2240 ));
2241 output.push_str("The following methods are available through the trait but not explicitly implemented:\n\n");
2242
2243 for provided_method in &impl_.provided_trait_methods {
2244 output.push_str(&format!("- `{}`\n", provided_method));
2245 }
2246
2247 output.push('\n');
2248 }
2249
2250 if let Some(blanket_type) = &impl_.blanket_impl {
2252 output.push_str(&format!(
2253 "This is a blanket implementation for all types that match: `{}`\n\n",
2254 format_type(blanket_type, data)
2255 ));
2256 }
2257}