microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.22.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/compiler/qsc_doc_gen/src/display.rs

815lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use qsc_ast::ast::{self, Idents, TypeParameter as AstTypeParameter};
5use qsc_frontend::resolve;
6use qsc_hir::{
7 hir::{self, PackageId},
8 ty::{self, TypeParameter as HirTypeParameter},
9};
10use regex_lite::Regex;
11use std::{
12 fmt::{Display, Formatter, Result},
13 rc::Rc,
14};
15
16/// Trait describing a struct capable of resolving various ids found in the AST and HIR.
17pub trait Lookup {
18 /// Looks up the type of a node in user code
19 fn get_ty(&self, expr_id: ast::NodeId) -> Option<&ty::Ty>;
20
21 /// Looks up the resolution of a node in user code
22 fn get_res(&self, id: ast::NodeId) -> Option<&resolve::Res>;
23
24 /// Returns the hir `Item` node referred to by `item_id`,
25 /// along with the `Package` and `PackageId` for the package
26 /// that it was found in.
27 fn resolve_item_relative_to_user_package(
28 &self,
29 item_id: &hir::ItemId,
30 ) -> (&hir::Item, &hir::Package, hir::ItemId);
31
32 /// Returns the hir `Item` node referred to by `res`.
33 /// `Res`s can resolve to external packages, and the references
34 /// are relative, so here we also need the
35 /// local `PackageId` that the `res` itself came from.
36 fn resolve_item_res(
37 &self,
38 local_package_id: PackageId,
39 res: &hir::Res,
40 ) -> (&hir::Item, hir::ItemId);
41
42 /// Returns the hir `Item` node referred to by `item_id`.
43 /// `ItemId`s can refer to external packages, and the references
44 /// are relative, so here we also need the local `PackageId`
45 /// that the `ItemId` originates from.
46 fn resolve_item(
47 &self,
48 local_package_id: PackageId,
49 item_id: &hir::ItemId,
50 ) -> (&hir::Item, &hir::Package, hir::ItemId);
51}
52
53pub struct CodeDisplay<'a> {
54 pub compilation: &'a dyn Lookup,
55}
56
57#[allow(clippy::unused_self)]
58impl<'a> CodeDisplay<'a> {
59 #[must_use]
60 pub fn hir_callable_decl(&self, decl: &'a hir::CallableDecl) -> impl Display + '_ {
61 HirCallableDecl { decl }
62 }
63
64 #[must_use]
65 pub fn ast_callable_decl(&self, decl: &'a ast::CallableDecl) -> impl Display + '_ {
66 AstCallableDecl {
67 lookup: self.compilation,
68 decl,
69 }
70 }
71
72 #[must_use]
73 pub fn name_ty_id(&self, name: &'a str, ty_id: ast::NodeId) -> impl Display + '_ {
74 NameTyId {
75 lookup: self.compilation,
76 name,
77 ty_id,
78 }
79 }
80
81 #[must_use]
82 pub fn ident_ty(&self, ident: &'a ast::Ident, ty: &'a ast::Ty) -> impl Display + '_ {
83 IdentTy { ident, ty }
84 }
85
86 #[must_use]
87 pub fn ident_ty_def(&self, ident: &'a ast::Ident, def: &'a ast::TyDef) -> impl Display + 'a {
88 IdentTyDef { ident, def }
89 }
90
91 #[must_use]
92 pub fn struct_decl(&self, decl: &'a ast::StructDecl) -> impl Display + 'a {
93 StructDecl { decl }
94 }
95
96 #[must_use]
97 pub fn hir_udt_field(&self, field: &'a ty::UdtField) -> impl Display + '_ {
98 HirUdtField { field }
99 }
100
101 #[must_use]
102 pub fn hir_udt(&self, udt: &'a ty::Udt) -> impl Display + '_ {
103 HirUdt::new(udt)
104 }
105
106 #[must_use]
107 pub fn hir_pat(&self, pat: &'a hir::Pat) -> impl Display + '_ {
108 HirPat { pat }
109 }
110
111 #[must_use]
112 pub fn get_param_offset(&self, decl: &hir::CallableDecl) -> u32 {
113 HirCallableDecl { decl }.get_param_offset()
114 }
115
116 // The rest of the display implementations are not made public b/c they're not used,
117 // but there's no reason they couldn't be
118}
119
120// Display impls for each syntax/hir element we may encounter
121
122struct IdentTy<'a> {
123 ident: &'a ast::Ident,
124 ty: &'a ast::Ty,
125}
126
127impl Display for IdentTy<'_> {
128 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
129 write!(f, "{} : {}", self.ident.name, AstTy { ty: self.ty },)
130 }
131}
132
133struct NameTyId<'a> {
134 lookup: &'a dyn Lookup,
135 name: &'a str,
136 ty_id: ast::NodeId,
137}
138
139impl Display for NameTyId<'_> {
140 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
141 write!(
142 f,
143 "{} : {}",
144 self.name,
145 TyId {
146 lookup: self.lookup,
147 ty_id: self.ty_id,
148 },
149 )
150 }
151}
152
153struct HirCallableDecl<'a> {
154 decl: &'a hir::CallableDecl,
155}
156
157impl HirCallableDecl<'_> {
158 fn get_param_offset(&self) -> u32 {
159 let offset = match self.decl.kind {
160 hir::CallableKind::Function => "function".len(),
161 hir::CallableKind::Operation => "operation".len(),
162 } + 1 // this is for the space between keyword and name
163 + self.decl.name.name.len()
164 + display_type_params(&self.decl.generics).len();
165
166 u32::try_from(offset)
167 .expect("failed to cast usize to u32 while calculating parameter offset")
168 }
169}
170
171impl Display for HirCallableDecl<'_> {
172 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
173 let kind = match self.decl.kind {
174 hir::CallableKind::Function => "function",
175 hir::CallableKind::Operation => "operation",
176 };
177
178 write!(f, "{} {}", kind, self.decl.name.name)?;
179 let type_params = display_type_params(&self.decl.generics);
180 write!(f, "{type_params}")?;
181 let input = HirPat {
182 pat: &self.decl.input,
183 };
184 if matches!(self.decl.input.kind, hir::PatKind::Tuple(_)) {
185 write!(f, "{input}")?;
186 } else {
187 write!(f, "({input})")?;
188 }
189 write!(
190 f,
191 " : {}{}",
192 self.decl.output.display(),
193 FunctorSetValue {
194 functors: self.decl.functors,
195 },
196 )
197 }
198}
199
200struct AstCallableDecl<'a> {
201 lookup: &'a dyn Lookup,
202 decl: &'a ast::CallableDecl,
203}
204
205impl Display for AstCallableDecl<'_> {
206 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
207 let kind = match self.decl.kind {
208 ast::CallableKind::Function => "function",
209 ast::CallableKind::Operation => "operation",
210 };
211
212 let functors = ast_callable_functors(self.decl);
213 let functors = FunctorSetValue { functors };
214
215 write!(f, "{} {}", kind, self.decl.name.name)?;
216 if !self.decl.generics.is_empty() {
217 let type_params = self
218 .decl
219 .generics
220 .iter()
221 .map(
222 |AstTypeParameter {
223 ty, constraints, ..
224 }| {
225 format!(
226 "{}{}",
227 ty.name,
228 if constraints.0.is_empty() {
229 Default::default()
230 } else {
231 format!(
232 ": {}",
233 constraints
234 .0
235 .iter()
236 .map(|bound| {
237 let constraint_parameters = bound
238 .parameters
239 .iter()
240 .map(|x| format!("{}", AstTy { ty: &x.ty }))
241 .collect::<Vec<_>>()
242 .join(", ");
243 format!(
244 "{}{}",
245 bound.name.name,
246 if constraint_parameters.is_empty() {
247 Default::default()
248 } else {
249 format!("[{constraint_parameters}]")
250 }
251 )
252 })
253 .collect::<Vec<_>>()
254 .join(" + ")
255 )
256 }
257 )
258 },
259 )
260 .collect::<Vec<_>>()
261 .join(", ");
262 write!(f, "<{type_params}>")?;
263 }
264 let input = AstPat {
265 pat: &self.decl.input,
266 lookup: self.lookup,
267 };
268 if matches!(*self.decl.input.kind, ast::PatKind::Tuple(_)) {
269 write!(f, "{input}")?;
270 } else {
271 write!(f, "({input})")?;
272 }
273 write!(
274 f,
275 " : {}{}",
276 AstTy {
277 ty: &self.decl.output
278 },
279 functors,
280 )
281 }
282}
283
284struct HirPat<'a> {
285 pat: &'a hir::Pat,
286}
287
288impl Display for HirPat<'_> {
289 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
290 match &self.pat.kind {
291 hir::PatKind::Bind(name) => write!(f, "{} : {}", name.name, self.pat.ty.display()),
292 hir::PatKind::Discard => write!(f, "_ : {}", self.pat.ty.display()),
293 hir::PatKind::Tuple(items) => {
294 let mut elements = items.iter();
295 if let Some(elem) = elements.next() {
296 write!(f, "({}", HirPat { pat: elem })?;
297 for elem in elements {
298 write!(f, ", {}", HirPat { pat: elem })?;
299 }
300 write!(f, ")")
301 } else {
302 write!(f, "()")
303 }
304 }
305 hir::PatKind::Err => write!(f, "?"),
306 }
307 }
308}
309
310struct AstPat<'a> {
311 lookup: &'a dyn Lookup,
312 pat: &'a ast::Pat,
313}
314
315impl Display for AstPat<'_> {
316 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
317 match &*self.pat.kind {
318 ast::PatKind::Bind(ident, anno) => match anno {
319 Some(ty) => write!(f, "{}", IdentTy { ident, ty }),
320 None => write!(
321 f,
322 "{}",
323 NameTyId {
324 lookup: self.lookup,
325 name: &ident.name,
326 ty_id: self.pat.id
327 }
328 ),
329 },
330 ast::PatKind::Discard(anno) => match anno {
331 Some(ty) => write!(f, "{}", AstTy { ty }),
332 None => write!(
333 f,
334 "_ : {}",
335 TyId {
336 lookup: self.lookup,
337 ty_id: self.pat.id,
338 }
339 ),
340 },
341 ast::PatKind::Elided => write!(f, "..."),
342 ast::PatKind::Paren(item) => write!(
343 f,
344 "{}",
345 AstPat {
346 lookup: self.lookup,
347 pat: item,
348 }
349 ),
350 ast::PatKind::Tuple(items) => {
351 let mut elements = items.iter();
352 if let Some(elem) = elements.next() {
353 write!(
354 f,
355 "({}",
356 AstPat {
357 lookup: self.lookup,
358 pat: elem,
359 }
360 )?;
361 for elem in elements {
362 write!(
363 f,
364 ", {}",
365 AstPat {
366 lookup: self.lookup,
367 pat: elem,
368 }
369 )?;
370 }
371 write!(f, ")")
372 } else {
373 write!(f, "()")
374 }
375 }
376 ast::PatKind::Err => write!(f, "?"),
377 }
378 }
379}
380
381struct IdentTyDef<'a> {
382 ident: &'a ast::Ident,
383 def: &'a ast::TyDef,
384}
385
386impl Display for IdentTyDef<'_> {
387 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
388 if let Some(fields) = as_struct(self.def) {
389 write!(f, "struct {} ", self.ident.name)?;
390 fmt_brace_seq(f, &fields, |item| IdentTy {
391 ident: &item.name,
392 ty: &item.ty,
393 })
394 } else {
395 write!(
396 f,
397 "newtype {} = {}",
398 self.ident.name,
399 TyDef { def: self.def }
400 )
401 }
402 }
403}
404
405struct StructDecl<'a> {
406 decl: &'a ast::StructDecl,
407}
408
409impl Display for StructDecl<'_> {
410 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
411 write!(f, "struct {} ", self.decl.name.name)?;
412 fmt_brace_seq(f, &self.decl.fields, |item| IdentTy {
413 ident: &item.name,
414 ty: &item.ty,
415 })
416 }
417}
418
419struct HirUdt<'a> {
420 udt: &'a ty::Udt,
421 is_struct: bool,
422}
423
424impl<'a> HirUdt<'a> {
425 fn new(udt: &'a ty::Udt) -> Self {
426 HirUdt {
427 udt,
428 is_struct: udt.is_struct(),
429 }
430 }
431}
432
433impl Display for HirUdt<'_> {
434 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
435 if self.is_struct {
436 match &self.udt.definition.kind {
437 ty::UdtDefKind::Tuple(fields) => {
438 write!(f, "struct {} ", self.udt.name)?;
439 fmt_brace_seq(f, fields, UdtDef::new)?;
440 }
441 ty::UdtDefKind::Field(_) => {}
442 }
443 Ok(())
444 } else {
445 let udt_def = UdtDef::new(&self.udt.definition);
446 write!(f, "newtype {} = {}", self.udt.name, udt_def)
447 }
448 }
449}
450
451struct UdtDef<'a> {
452 name: Option<Rc<str>>,
453 kind: UdtDefKind<'a>,
454}
455
456enum UdtDefKind<'a> {
457 SingleTy(&'a ty::Ty),
458 TupleTy(Vec<UdtDef<'a>>),
459}
460
461impl<'a> UdtDef<'a> {
462 pub fn new(def: &'a ty::UdtDef) -> Self {
463 match &def.kind {
464 ty::UdtDefKind::Field(field) => UdtDef {
465 name: field.name.clone(),
466 kind: UdtDefKind::SingleTy(&field.ty),
467 },
468 ty::UdtDefKind::Tuple(defs) => UdtDef {
469 name: None,
470 kind: UdtDefKind::TupleTy(defs.iter().map(UdtDef::new).collect()),
471 },
472 }
473 }
474}
475
476impl Display for UdtDef<'_> {
477 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
478 if let Some(name) = &self.name {
479 write!(f, "{name} : ")?;
480 }
481
482 match &self.kind {
483 UdtDefKind::SingleTy(ty) => {
484 write!(f, "{}", ty.display())
485 }
486 UdtDefKind::TupleTy(defs) => fmt_tuple(f, defs, |def| def),
487 }
488 }
489}
490
491struct HirUdtField<'a> {
492 field: &'a ty::UdtField,
493}
494
495impl Display for HirUdtField<'_> {
496 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
497 if let Some(name) = &self.field.name {
498 write!(f, "{name} : ")?;
499 }
500 write!(f, "{}", self.field.ty.display())
501 }
502}
503
504struct FunctorSetValue {
505 functors: ty::FunctorSetValue,
506}
507
508impl Display for FunctorSetValue {
509 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
510 if let ty::FunctorSetValue::Empty = self.functors {
511 Ok(())
512 } else {
513 write!(f, " is {}", self.functors)
514 }
515 }
516}
517
518struct TyId<'a> {
519 lookup: &'a dyn Lookup,
520 ty_id: ast::NodeId,
521}
522
523impl Display for TyId<'_> {
524 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
525 if let Some(ty) = self.lookup.get_ty(self.ty_id) {
526 write!(f, "{}", ty.display())
527 } else {
528 write!(f, "?")
529 }
530 }
531}
532
533struct AstTy<'a> {
534 ty: &'a ast::Ty,
535}
536
537impl Display for AstTy<'_> {
538 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
539 match self.ty.kind.as_ref() {
540 ast::TyKind::Array(ty) => write!(f, "{}[]", AstTy { ty }),
541 ast::TyKind::Arrow(kind, input, output, functors) => {
542 let arrow = match kind {
543 ast::CallableKind::Function => "->",
544 ast::CallableKind::Operation => "=>",
545 };
546 write!(
547 f,
548 "({} {} {}{})",
549 AstTy { ty: input },
550 arrow,
551 AstTy { ty: output },
552 FunctorExpr { functors }
553 )
554 }
555 ast::TyKind::Hole => write!(f, "_"),
556 ast::TyKind::Paren(ty) => write!(f, "{}", AstTy { ty }),
557 ast::TyKind::Path(path) => write!(f, "{}", AstPathKind { path }),
558 ast::TyKind::Param(AstTypeParameter { ty, .. }) => write!(f, "{}", ty.name),
559 ast::TyKind::Tuple(tys) => fmt_tuple(f, tys, |ty| AstTy { ty }),
560 ast::TyKind::Err => write!(f, "?"),
561 }
562 }
563}
564
565struct FunctorExpr<'a> {
566 functors: &'a Option<Box<ast::FunctorExpr>>,
567}
568
569impl Display for FunctorExpr<'_> {
570 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
571 match self.functors {
572 Some(functors) => {
573 let functors = eval_functor_expr(functors);
574 write!(f, "{}", FunctorSetValue { functors })
575 }
576 None => Ok(()),
577 }
578 }
579}
580
581struct AstPathKind<'a> {
582 path: &'a ast::PathKind,
583}
584
585impl Display for AstPathKind<'_> {
586 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
587 if let ast::PathKind::Ok(path) = self.path {
588 write!(f, "{}", path.full_name())
589 } else {
590 write!(f, "?")
591 }
592 }
593}
594
595struct TyDef<'a> {
596 def: &'a ast::TyDef,
597}
598
599impl Display for TyDef<'_> {
600 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
601 match self.def.kind.as_ref() {
602 ast::TyDefKind::Field(name, ty) => match name {
603 Some(name) => write!(f, "{} : {}", name.name, AstTy { ty }),
604 None => write!(f, "{}", AstTy { ty }),
605 },
606 ast::TyDefKind::Paren(def) => write!(f, "{}", TyDef { def }),
607 ast::TyDefKind::Tuple(tys) => fmt_tuple(f, tys, |def| TyDef { def }),
608 ast::TyDefKind::Err => write!(f, "?"),
609 }
610 }
611}
612
613fn fmt_tuple<'a, I, O>(
614 formatter: &mut Formatter,
615 items: &'a [I],
616 map: impl Fn(&'a I) -> O,
617) -> Result
618where
619 O: Display,
620{
621 let mut elements = items.iter();
622 if let Some(elem) = elements.next() {
623 write!(formatter, "({}", map(elem))?;
624 if elements.len() == 0 {
625 write!(formatter, ",)")?;
626 } else {
627 for elem in elements {
628 write!(formatter, ", {}", map(elem))?;
629 }
630 write!(formatter, ")")?;
631 }
632 } else {
633 write!(formatter, "Unit")?;
634 }
635 Ok(())
636}
637
638fn fmt_brace_seq<'a, I, O>(
639 formatter: &mut Formatter<'_>,
640 items: &'a [I],
641 map: impl Fn(&'a I) -> O,
642) -> Result
643where
644 O: Display,
645{
646 write!(formatter, "{{ ")?;
647 if let Some((last, most)) = items.split_last() {
648 for item in most {
649 write!(formatter, "{}, ", map(item))?;
650 }
651 write!(formatter, "{} ", map(last))?;
652 }
653 write!(formatter, "}}")
654}
655
656fn display_type_params(generics: &[HirTypeParameter]) -> String {
657 let type_params = generics
658 .iter()
659 .filter_map(|generic| match generic {
660 HirTypeParameter::Ty { name, bounds } => Some(format!(
661 "{}{}",
662 name,
663 if bounds.is_empty() {
664 Default::default()
665 } else {
666 format!(": {bounds}")
667 }
668 )),
669 HirTypeParameter::Functor(_) => None,
670 })
671 .collect::<Vec<_>>()
672 .join(", ");
673 if type_params.is_empty() {
674 type_params
675 } else {
676 format!("<{type_params}>")
677 }
678}
679
680//
681// helpers that don't manipulate any strings
682//
683
684fn ast_callable_functors(callable: &ast::CallableDecl) -> ty::FunctorSetValue {
685 let mut functors = callable
686 .functors
687 .as_ref()
688 .map_or(ty::FunctorSetValue::Empty, |f| {
689 eval_functor_expr(f.as_ref())
690 });
691
692 if let ast::CallableBody::Specs(specs) = callable.body.as_ref() {
693 for spec in specs {
694 let spec_functors = match spec.spec {
695 ast::Spec::Body => ty::FunctorSetValue::Empty,
696 ast::Spec::Adj => ty::FunctorSetValue::Adj,
697 ast::Spec::Ctl => ty::FunctorSetValue::Ctl,
698 ast::Spec::CtlAdj => ty::FunctorSetValue::CtlAdj,
699 };
700 functors = functors.union(&spec_functors);
701 }
702 }
703
704 functors
705}
706
707fn eval_functor_expr(expr: &ast::FunctorExpr) -> ty::FunctorSetValue {
708 match expr.kind.as_ref() {
709 ast::FunctorExprKind::BinOp(op, lhs, rhs) => {
710 let lhs_functors = eval_functor_expr(lhs);
711 let rhs_functors = eval_functor_expr(rhs);
712 match op {
713 ast::SetOp::Union => lhs_functors.union(&rhs_functors),
714 ast::SetOp::Intersect => lhs_functors.intersect(&rhs_functors),
715 }
716 }
717 ast::FunctorExprKind::Lit(ast::Functor::Adj) => ty::FunctorSetValue::Adj,
718 ast::FunctorExprKind::Lit(ast::Functor::Ctl) => ty::FunctorSetValue::Ctl,
719 ast::FunctorExprKind::Paren(inner) => eval_functor_expr(inner),
720 }
721}
722
723fn as_struct(ty_def: &ast::TyDef) -> Option<Vec<ast::FieldDef>> {
724 match ty_def.kind.as_ref() {
725 ast::TyDefKind::Paren(inner) => as_struct(inner),
726 ast::TyDefKind::Tuple(fields) => {
727 let mut converted_fields = Vec::new();
728 for field in fields {
729 let field = remove_parens(field);
730 match field.kind.as_ref() {
731 ast::TyDefKind::Field(Some(name), field_ty) => {
732 converted_fields.push(ast::FieldDef {
733 id: field.id,
734 span: field.span,
735 name: name.clone(),
736 ty: field_ty.clone(),
737 });
738 }
739 _ => return None,
740 }
741 }
742 Some(converted_fields)
743 }
744 ast::TyDefKind::Err | ast::TyDefKind::Field(..) => None,
745 }
746}
747
748fn remove_parens(ty_def: &ast::TyDef) -> &ast::TyDef {
749 match ty_def.kind.as_ref() {
750 ast::TyDefKind::Paren(inner) => remove_parens(inner.as_ref()),
751 _ => ty_def,
752 }
753}
754
755//
756// parsing functions for working with doc comments
757//
758
759/// Takes a doc string from Q# and increases all of the markdown header levels by one level.
760/// i.e. `# Summary` becomes `## Summary`
761#[must_use]
762pub fn increase_header_level(doc: &str) -> String {
763 let re = Regex::new(r"(?mi)^(#+)( [\s\S]+?)$").expect("Invalid regex");
764 re.replace_all(doc, "$1#$2").to_string()
765}
766
767/// Takes a doc string from Q# and returns the contents of the `# Summary` section. If no
768/// such section can be found, returns the original doc string.
769#[must_use]
770pub fn parse_doc_for_summary(doc: &str) -> String {
771 let re = Regex::new(r"(?mi)(?:^# Summary$)([\s\S]*?)(?:(^# .*)|\z)").expect("Invalid regex");
772 match re.captures(doc) {
773 Some(captures) => {
774 let capture = captures
775 .get(1)
776 .expect("Didn't find the capture for the given regex");
777 capture.as_str()
778 }
779 None => doc,
780 }
781 .trim()
782 .to_string()
783}
784
785/// Takes a doc string from a Q# callable and the name of a parameter of
786/// that callable. Returns the description of that parameter found in the
787/// doc string. If no description is found, returns the empty string.
788#[must_use]
789pub fn parse_doc_for_param(doc: &str, param: &str) -> String {
790 let re = Regex::new(r"(?mi)(?:^# Input$)([\s\S]*?)(?:(^# .*)|\z)").expect("Invalid regex");
791 let input = match re.captures(doc) {
792 Some(captures) => {
793 let capture = captures
794 .get(1)
795 .expect("Didn't find the capture for the given regex");
796 capture.as_str()
797 }
798 None => return String::new(),
799 }
800 .trim();
801
802 let re = Regex::new(format!(r"(?mi)(?:^## {param}$)([\s\S]*?)(?:(^(#|##) .*)|\z)").as_str())
803 .expect("Invalid regex");
804 match re.captures(input) {
805 Some(captures) => {
806 let capture = captures
807 .get(1)
808 .expect("Didn't find the capture for the given regex");
809 capture.as_str()
810 }
811 None => return String::new(),
812 }
813 .trim()
814 .to_string()
815}
816