1use std::borrow::Cow;
11use std::fmt::Write;
12
13use rustdoc_types::{Crate, Id, Impl, Item, ItemEnum, StructKind, VariantKind, Visibility};
14
15use crate::generator::context::RenderContext;
16use crate::types::TypeRenderer;
17
18pub fn render_struct_definition(
31 md: &mut String,
32 name: &str,
33 s: &rustdoc_types::Struct,
34 krate: &Crate,
35 type_renderer: &TypeRenderer,
36) {
37 let generics = type_renderer.render_generics(&s.generics.params);
38 let where_clause = type_renderer.render_where_clause(&s.generics.where_predicates);
39
40 _ = write!(md, "### `{name}{generics}`\n\n");
41
42 md.push_str("```rust\n");
43 match &s.kind {
44 StructKind::Unit => {
45 _ = writeln!(md, "struct {name}{generics}{where_clause};");
46 },
47
48 StructKind::Tuple(fields) => {
49 let field_types: Vec<Cow<str>> = fields
50 .iter()
51 .filter_map(|id| id.as_ref())
52 .filter_map(|id| krate.index.get(id))
53 .filter_map(|item| {
54 if let ItemEnum::StructField(ty) = &item.inner {
55 Some(type_renderer.render_type(ty))
56 } else {
57 None
58 }
59 })
60 .collect();
61 _ = writeln!(
62 md,
63 "struct {}{}({}){};",
64 name,
65 generics,
66 field_types.join(", "),
67 where_clause
68 );
69 },
70
71 StructKind::Plain {
72 fields,
73 has_stripped_fields,
74 } => {
75 _ = writeln!(md, "struct {name}{generics}{where_clause} {{");
76
77 for field_id in fields {
78 if let Some(field) = krate.index.get(field_id) {
79 let field_name = field.name.as_deref().unwrap_or("_");
80 if let ItemEnum::StructField(ty) = &field.inner {
81 let vis = match &field.visibility {
82 Visibility::Public => "pub ",
83 _ => "",
84 };
85 _ = writeln!(
86 md,
87 " {}{}: {},",
88 vis,
89 field_name,
90 type_renderer.render_type(ty)
91 );
92 }
93 }
94 }
95
96 if *has_stripped_fields {
97 _ = writeln!(md, " // [REDACTED: Private Fields]");
98 }
99
100 md.push_str("}\n");
101 },
102 }
103 md.push_str("```\n\n");
104}
105
106pub fn render_struct_fields<F>(
119 md: &mut String,
120 fields: &[Id],
121 krate: &Crate,
122 type_renderer: &TypeRenderer,
123 process_docs: F,
124) where
125 F: Fn(&Item) -> Option<String>,
126{
127 let documented_fields: Vec<_> = fields
128 .iter()
129 .filter_map(|id| krate.index.get(id))
130 .filter(|f| f.docs.is_some())
131 .collect();
132
133 if !documented_fields.is_empty() {
134 md.push_str("#### Fields\n\n");
135 for field in documented_fields {
136 let field_name = field.name.as_deref().unwrap_or("_");
137 if let ItemEnum::StructField(ty) = &field.inner {
138 _ = write!(
139 md,
140 "- **`{}`**: `{}`",
141 field_name,
142 type_renderer.render_type(ty)
143 );
144
145 if let Some(docs) = process_docs(field) {
146 _ = write!(md, "\n\n {}", docs.replace('\n', "\n "));
147 }
148
149 md.push_str("\n\n");
150 }
151 }
152 }
153}
154
155pub fn render_enum_definition(
168 md: &mut String,
169 name: &str,
170 e: &rustdoc_types::Enum,
171 krate: &Crate,
172 type_renderer: &TypeRenderer,
173) {
174 let generics = type_renderer.render_generics(&e.generics.params);
175 let where_clause = type_renderer.render_where_clause(&e.generics.where_predicates);
176
177 _ = write!(md, "### `{name}{generics}`\n\n");
178
179 md.push_str("```rust\n");
180 _ = writeln!(md, "enum {name}{generics}{where_clause} {{");
181
182 for variant_id in &e.variants {
183 if let Some(variant) = krate.index.get(variant_id) {
184 render_enum_variant(md, variant, krate, type_renderer);
185 }
186 }
187 md.push_str("}\n");
188 md.push_str("```\n\n");
189}
190
191pub fn render_enum_variant(
195 md: &mut String,
196 variant: &Item,
197 krate: &Crate,
198 type_renderer: &TypeRenderer,
199) {
200 let variant_name = variant.name.as_deref().unwrap_or("_");
201
202 if let ItemEnum::Variant(v) = &variant.inner {
203 match &v.kind {
204 VariantKind::Plain => {
205 _ = writeln!(md, " {variant_name},");
206 },
207
208 VariantKind::Tuple(fields) => {
209 let field_types: Vec<Cow<str>> = fields
210 .iter()
211 .filter_map(|id| id.as_ref())
212 .filter_map(|id| krate.index.get(id))
213 .filter_map(|item| {
214 if let ItemEnum::StructField(ty) = &item.inner {
215 Some(type_renderer.render_type(ty))
216 } else {
217 None
218 }
219 })
220 .collect();
221
222 _ = writeln!(md, " {}({}),", variant_name, field_types.join(", "));
223 },
224
225 VariantKind::Struct { fields, .. } => {
226 _ = writeln!(md, " {variant_name} {{");
227
228 for field_id in fields {
229 if let Some(field) = krate.index.get(field_id) {
230 let field_name = field.name.as_deref().unwrap_or("_");
231 if let ItemEnum::StructField(ty) = &field.inner {
232 _ = writeln!(
233 md,
234 " {}: {},",
235 field_name,
236 type_renderer.render_type(ty)
237 );
238 }
239 }
240 }
241 md.push_str(" },\n");
242 },
243 }
244 }
245}
246
247pub fn render_enum_variants_docs<F>(
258 md: &mut String,
259 variants: &[Id],
260 krate: &Crate,
261 process_docs: F,
262) where
263 F: Fn(&Item) -> Option<String>,
264{
265 let documented_variants: Vec<_> = variants
266 .iter()
267 .filter_map(|id| krate.index.get(id))
268 .filter(|v| v.docs.is_some())
269 .collect();
270
271 if !documented_variants.is_empty() {
272 md.push_str("#### Variants\n\n");
273 for variant in documented_variants {
274 let variant_name = variant.name.as_deref().unwrap_or("_");
275 _ = write!(md, "- **`{variant_name}`**");
276
277 if let Some(docs) = process_docs(variant) {
278 _ = write!(md, "\n\n {}", docs.replace('\n', "\n "));
279 }
280
281 md.push_str("\n\n");
282 }
283 }
284}
285
286pub fn render_trait_definition(
298 md: &mut String,
299 name: &str,
300 t: &rustdoc_types::Trait,
301 type_renderer: &TypeRenderer,
302) {
303 let generics = type_renderer.render_generics(&t.generics.params);
304 let where_clause = type_renderer.render_where_clause(&t.generics.where_predicates);
305
306 _ = write!(md, "### `{name}{generics}`\n\n");
307
308 md.push_str("```rust\n");
309
310 let bounds = if t.bounds.is_empty() {
311 String::new()
312 } else {
313 let bound_strs: Vec<Cow<str>> = t
314 .bounds
315 .iter()
316 .map(|b| type_renderer.render_generic_bound(b))
317 .collect();
318 format!(": {}", bound_strs.join(" + "))
319 };
320
321 _ = writeln!(md, "trait {name}{generics}{bounds}{where_clause} {{ ... }}");
322 md.push_str("```\n\n");
323}
324
325pub fn render_trait_item<F>(
337 md: &mut String,
338 item: &Item,
339 type_renderer: &TypeRenderer,
340 process_docs: F,
341) where
342 F: Fn(&Item) -> Option<String>,
343{
344 let name = item.name.as_deref().unwrap_or("_");
345
346 match &item.inner {
347 ItemEnum::Function(f) => {
348 let generics = type_renderer.render_generics(&f.generics.params);
349
350 let params: Vec<String> = f
351 .sig
352 .inputs
353 .iter()
354 .map(|(param_name, ty)| format!("{param_name}: {}", type_renderer.render_type(ty)))
355 .collect();
356
357 let ret = f
358 .sig
359 .output
360 .as_ref()
361 .map(|ty| format!(" -> {}", type_renderer.render_type(ty)))
362 .unwrap_or_default();
363
364 _ = write!(
365 md,
366 "- `fn {}{}({}){}`",
367 name,
368 generics,
369 params.join(", "),
370 ret
371 );
372
373 if let Some(docs) = process_docs(item)
374 && let Some(first_line) = docs.lines().next()
375 {
376 _ = write!(md, "\n\n {first_line}");
377 }
378
379 md.push_str("\n\n");
380 },
381
382 ItemEnum::AssocType { bounds, type_, .. } => {
383 let bounds_str = if bounds.is_empty() {
384 String::new()
385 } else {
386 format!(": {}", bounds.len())
387 };
388 let default_str = type_
389 .as_ref()
390 .map(|ty| format!(" = {}", type_renderer.render_type(ty)))
391 .unwrap_or_default();
392
393 _ = write!(md, "- `type {name}{bounds_str}{default_str}`\n\n");
394 },
395
396 ItemEnum::AssocConst { type_, .. } => {
397 _ = write!(
398 md,
399 "- `const {name}: {}`\n\n",
400 type_renderer.render_type(type_)
401 );
402 },
403
404 _ => {
405 _ = write!(md, "- `{name}`\n\n");
406 },
407 }
408}
409
410pub fn render_function_definition(
422 md: &mut String,
423 name: &str,
424 f: &rustdoc_types::Function,
425 type_renderer: &TypeRenderer,
426) {
427 let generics = type_renderer.render_generics(&f.generics.params);
428 let where_clause = type_renderer.render_where_clause(&f.generics.where_predicates);
429
430 let params: Vec<String> = f
431 .sig
432 .inputs
433 .iter()
434 .map(|(param_name, ty)| format!("{param_name}: {}", type_renderer.render_type(ty)))
435 .collect();
436
437 let ret = f
438 .sig
439 .output
440 .as_ref()
441 .map(|ty| format!(" -> {}", type_renderer.render_type(ty)))
442 .unwrap_or_default();
443
444 let is_async = if f.header.is_async { "async " } else { "" };
445 let is_const = if f.header.is_const { "const " } else { "" };
446 let is_unsafe = if f.header.is_unsafe { "unsafe " } else { "" };
447
448 _ = write!(md, "### `{name}`\n\n");
449 md.push_str("```rust\n");
450
451 _ = writeln!(
452 md,
453 "{}{}{}fn {}{}({}){}{}",
454 is_const,
455 is_async,
456 is_unsafe,
457 name,
458 generics,
459 params.join(", "),
460 ret,
461 where_clause
462 );
463
464 md.push_str("```\n\n");
465}
466
467pub fn render_constant_definition(
480 md: &mut String,
481 name: &str,
482 type_: &rustdoc_types::Type,
483 const_: &rustdoc_types::Constant,
484 type_renderer: &TypeRenderer,
485) {
486 _ = write!(md, "### `{name}`\n\n");
487
488 md.push_str("```rust\n");
489
490 let value = const_
491 .value
492 .as_ref()
493 .map(|v| format!(" = {v}"))
494 .unwrap_or_default();
495
496 _ = writeln!(
497 md,
498 "const {name}: {}{value};",
499 type_renderer.render_type(type_)
500 );
501
502 md.push_str("```\n\n");
503}
504
505pub fn render_type_alias_definition(
517 md: &mut String,
518 name: &str,
519 ta: &rustdoc_types::TypeAlias,
520 type_renderer: &TypeRenderer,
521) {
522 let generics = type_renderer.render_generics(&ta.generics.params);
523 let where_clause = type_renderer.render_where_clause(&ta.generics.where_predicates);
524
525 _ = write!(md, "### `{name}{generics}`\n\n");
526 md.push_str("```rust\n");
527
528 _ = writeln!(
529 md,
530 "type {name}{generics}{where_clause} = {};",
531 type_renderer.render_type(&ta.type_)
532 );
533 md.push_str("```\n\n");
534}
535
536pub fn render_macro_heading(md: &mut String, name: &str) {
546 _ = write!(md, "### `{name}!`\n\n");
547}
548
549pub fn render_impl_items<F, L>(
563 md: &mut String,
564 impl_block: &Impl,
565 krate: &Crate,
566 type_renderer: &TypeRenderer,
567 process_docs: &Option<F>,
568 create_type_link: &Option<L>,
569) where
570 F: Fn(&Item) -> Option<String>,
571 L: Fn(rustdoc_types::Id) -> Option<String>,
572{
573 for item_id in &impl_block.items {
574 if let Some(item) = krate.index.get(item_id) {
575 let name = item.name.as_deref().unwrap_or("_");
576
577 match &item.inner {
578 ItemEnum::Function(f) => {
579 render_impl_function(md, name, f, *type_renderer);
580
581 if let Some(link_creator) = create_type_link {
583 render_function_type_links_inline(md, f, *type_renderer, link_creator);
584 }
585
586 if let Some(pf) = process_docs
588 && let Some(docs) = pf(item)
589 && let Some(first_line) = docs.lines().next()
590 {
591 _ = write!(md, "\n\n {first_line}");
592 }
593 md.push_str("\n\n");
594 },
595
596 ItemEnum::AssocConst { type_, .. } => {
597 _ = writeln!(
598 md,
599 "- `const {name}: {}`\n",
600 type_renderer.render_type(type_)
601 );
602 },
603
604 ItemEnum::AssocType { type_, .. } => {
605 if let Some(ty) = type_ {
606 _ = writeln!(md, "- `type {name} = {}`\n", type_renderer.render_type(ty));
607 } else {
608 _ = writeln!(md, "- `type {name}`\n");
609 }
610 },
611
612 _ => {},
613 }
614 }
615 }
616}
617
618fn render_function_type_links_inline<L>(
623 md: &mut String,
624 f: &rustdoc_types::Function,
625 type_renderer: TypeRenderer,
626 create_link: &L,
627) where
628 L: Fn(rustdoc_types::Id) -> Option<String>,
629{
630 use std::collections::HashSet;
631
632 let mut all_types = Vec::new();
633
634 for (_, ty) in &f.sig.inputs {
636 all_types.extend(type_renderer.collect_linkable_types(ty));
637 }
638
639 if let Some(output) = &f.sig.output {
641 all_types.extend(type_renderer.collect_linkable_types(output));
642 }
643
644 let mut seen = HashSet::new();
646 let unique_types: Vec<_> = all_types
647 .into_iter()
648 .filter(|(name, _)| seen.insert(name.clone()))
649 .collect();
650
651 let links: Vec<String> = unique_types
653 .iter()
654 .filter_map(|(_, id)| create_link(*id))
655 .collect();
656
657 if !links.is_empty() {
659 _ = write!(md, " — {}", links.join(", "));
660 }
661}
662
663fn render_impl_function(
667 md: &mut String,
668 name: &str,
669 f: &rustdoc_types::Function,
670 type_renderer: TypeRenderer,
671) {
672 let generics = type_renderer.render_generics(&f.generics.params);
673
674 let params: Vec<String> = f
675 .sig
676 .inputs
677 .iter()
678 .map(|(param_name, ty)| format!("{param_name}: {}", type_renderer.render_type(ty)))
679 .collect();
680
681 let ret = f
682 .sig
683 .output
684 .as_ref()
685 .map(|ty| format!(" -> {}", type_renderer.render_type(ty)))
686 .unwrap_or_default();
687
688 let is_async = if f.header.is_async { "async " } else { "" };
689 let is_const = if f.header.is_const { "const " } else { "" };
690 let is_unsafe = if f.header.is_unsafe { "unsafe " } else { "" };
691
692 _ = write!(
693 md,
694 "- `{}{}{}fn {}{}({}){}`",
695 is_const,
696 is_async,
697 is_unsafe,
698 name,
699 generics,
700 params.join(", "),
701 ret
702 );
703}
704
705pub fn append_docs(md: &mut String, docs: Option<String>) {
709 if let Some(docs) = docs {
710 md.push_str(&docs);
711 md.push_str("\n\n");
712 }
713}
714
715#[must_use]
719pub fn impl_sort_key(impl_block: &Impl, type_renderer: &TypeRenderer) -> String {
720 let trait_name = impl_block
721 .trait_
722 .as_ref()
723 .map(|t| t.path.clone())
724 .unwrap_or_default();
725 let for_type = type_renderer.render_type(&impl_block.for_);
726 let generics = type_renderer.render_generics(&impl_block.generics.params);
727 format!("{trait_name}{generics}::{for_type}")
728}
729
730pub trait DocsProcessor {
734 fn process_item_docs(&self, item: &Item) -> Option<String>;
736}
737
738impl<T: RenderContext + ?Sized> DocsProcessor for (&T, &str) {
739 fn process_item_docs(&self, item: &Item) -> Option<String> {
740 self.0.process_docs(item, self.1)
741 }
742}