microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
alex/pythontelem

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_doc_gen/src/display.rs

769lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use qsc_ast::ast::{self, Idents};
5use qsc_frontend::resolve;
6use qsc_hir::{
7 hir::{self, PackageId},
8 ty::{self, GenericParam},
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<'a> Display for IdentTy<'a> {
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<'a> Display for NameTyId<'a> {
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<'a> Display for AstCallableDecl<'a> {
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(|p| p.name.clone())
222 .collect::<Vec<_>>()
223 .join(", ");
224 write!(f, "<{type_params}>")?;
225 }
226 let input = AstPat {
227 pat: &self.decl.input,
228 lookup: self.lookup,
229 };
230 if matches!(*self.decl.input.kind, ast::PatKind::Tuple(_)) {
231 write!(f, "{input}")?;
232 } else {
233 write!(f, "({input})")?;
234 }
235 write!(
236 f,
237 " : {}{}",
238 AstTy {
239 ty: &self.decl.output
240 },
241 functors,
242 )
243 }
244}
245
246struct HirPat<'a> {
247 pat: &'a hir::Pat,
248}
249
250impl<'a> Display for HirPat<'a> {
251 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
252 match &self.pat.kind {
253 hir::PatKind::Bind(name) => write!(f, "{} : {}", name.name, self.pat.ty.display()),
254 hir::PatKind::Discard => write!(f, "_ : {}", self.pat.ty.display()),
255 hir::PatKind::Tuple(items) => {
256 let mut elements = items.iter();
257 if let Some(elem) = elements.next() {
258 write!(f, "({}", HirPat { pat: elem })?;
259 for elem in elements {
260 write!(f, ", {}", HirPat { pat: elem })?;
261 }
262 write!(f, ")")
263 } else {
264 write!(f, "()")
265 }
266 }
267 hir::PatKind::Err => write!(f, "?"),
268 }
269 }
270}
271
272struct AstPat<'a> {
273 lookup: &'a dyn Lookup,
274 pat: &'a ast::Pat,
275}
276
277impl<'a> Display for AstPat<'a> {
278 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
279 match &*self.pat.kind {
280 ast::PatKind::Bind(ident, anno) => match anno {
281 Some(ty) => write!(f, "{}", IdentTy { ident, ty }),
282 None => write!(
283 f,
284 "{}",
285 NameTyId {
286 lookup: self.lookup,
287 name: &ident.name,
288 ty_id: self.pat.id
289 }
290 ),
291 },
292 ast::PatKind::Discard(anno) => match anno {
293 Some(ty) => write!(f, "{}", AstTy { ty }),
294 None => write!(
295 f,
296 "_ : {}",
297 TyId {
298 lookup: self.lookup,
299 ty_id: self.pat.id,
300 }
301 ),
302 },
303 ast::PatKind::Elided => write!(f, "..."),
304 ast::PatKind::Paren(item) => write!(
305 f,
306 "{}",
307 AstPat {
308 lookup: self.lookup,
309 pat: item,
310 }
311 ),
312 ast::PatKind::Tuple(items) => {
313 let mut elements = items.iter();
314 if let Some(elem) = elements.next() {
315 write!(
316 f,
317 "({}",
318 AstPat {
319 lookup: self.lookup,
320 pat: elem,
321 }
322 )?;
323 for elem in elements {
324 write!(
325 f,
326 ", {}",
327 AstPat {
328 lookup: self.lookup,
329 pat: elem,
330 }
331 )?;
332 }
333 write!(f, ")")
334 } else {
335 write!(f, "()")
336 }
337 }
338 ast::PatKind::Err => write!(f, "?"),
339 }
340 }
341}
342
343struct IdentTyDef<'a> {
344 ident: &'a ast::Ident,
345 def: &'a ast::TyDef,
346}
347
348impl Display for IdentTyDef<'_> {
349 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
350 if let Some(fields) = as_struct(self.def) {
351 write!(f, "struct {} ", self.ident.name)?;
352 fmt_brace_seq(f, &fields, |item| IdentTy {
353 ident: &item.name,
354 ty: &item.ty,
355 })
356 } else {
357 write!(
358 f,
359 "newtype {} = {}",
360 self.ident.name,
361 TyDef { def: self.def }
362 )
363 }
364 }
365}
366
367struct StructDecl<'a> {
368 decl: &'a ast::StructDecl,
369}
370
371impl Display for StructDecl<'_> {
372 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
373 write!(f, "struct {} ", self.decl.name.name)?;
374 fmt_brace_seq(f, &self.decl.fields, |item| IdentTy {
375 ident: &item.name,
376 ty: &item.ty,
377 })
378 }
379}
380
381struct HirUdt<'a> {
382 udt: &'a ty::Udt,
383 is_struct: bool,
384}
385
386impl<'a> HirUdt<'a> {
387 fn new(udt: &'a ty::Udt) -> Self {
388 HirUdt {
389 udt,
390 is_struct: udt.is_struct(),
391 }
392 }
393}
394
395impl<'a> Display for HirUdt<'a> {
396 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
397 if self.is_struct {
398 match &self.udt.definition.kind {
399 ty::UdtDefKind::Tuple(fields) => {
400 write!(f, "struct {} ", self.udt.name)?;
401 fmt_brace_seq(f, fields, UdtDef::new)?;
402 }
403 ty::UdtDefKind::Field(_) => {}
404 }
405 Ok(())
406 } else {
407 let udt_def = UdtDef::new(&self.udt.definition);
408 write!(f, "newtype {} = {}", self.udt.name, udt_def)
409 }
410 }
411}
412
413struct UdtDef<'a> {
414 name: Option<Rc<str>>,
415 kind: UdtDefKind<'a>,
416}
417
418enum UdtDefKind<'a> {
419 SingleTy(&'a ty::Ty),
420 TupleTy(Vec<UdtDef<'a>>),
421}
422
423impl<'a> UdtDef<'a> {
424 pub fn new(def: &'a ty::UdtDef) -> Self {
425 match &def.kind {
426 ty::UdtDefKind::Field(field) => UdtDef {
427 name: field.name.clone(),
428 kind: UdtDefKind::SingleTy(&field.ty),
429 },
430 ty::UdtDefKind::Tuple(defs) => UdtDef {
431 name: None,
432 kind: UdtDefKind::TupleTy(defs.iter().map(UdtDef::new).collect()),
433 },
434 }
435 }
436}
437
438impl Display for UdtDef<'_> {
439 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
440 if let Some(name) = &self.name {
441 write!(f, "{name} : ")?;
442 }
443
444 match &self.kind {
445 UdtDefKind::SingleTy(ty) => {
446 write!(f, "{}", ty.display())
447 }
448 UdtDefKind::TupleTy(defs) => fmt_tuple(f, defs, |def| def),
449 }
450 }
451}
452
453struct HirUdtField<'a> {
454 field: &'a ty::UdtField,
455}
456
457impl Display for HirUdtField<'_> {
458 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
459 if let Some(name) = &self.field.name {
460 write!(f, "{name} : ")?;
461 }
462 write!(f, "{}", self.field.ty.display())
463 }
464}
465
466struct FunctorSetValue {
467 functors: ty::FunctorSetValue,
468}
469
470impl Display for FunctorSetValue {
471 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
472 if let ty::FunctorSetValue::Empty = self.functors {
473 Ok(())
474 } else {
475 write!(f, " is {}", self.functors)
476 }
477 }
478}
479
480struct TyId<'a> {
481 lookup: &'a dyn Lookup,
482 ty_id: ast::NodeId,
483}
484
485impl<'a> Display for TyId<'a> {
486 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
487 if let Some(ty) = self.lookup.get_ty(self.ty_id) {
488 write!(f, "{}", ty.display())
489 } else {
490 write!(f, "?")
491 }
492 }
493}
494
495struct AstTy<'a> {
496 ty: &'a ast::Ty,
497}
498
499impl<'a> Display for AstTy<'a> {
500 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
501 match self.ty.kind.as_ref() {
502 ast::TyKind::Array(ty) => write!(f, "{}[]", AstTy { ty }),
503 ast::TyKind::Arrow(kind, input, output, functors) => {
504 let arrow = match kind {
505 ast::CallableKind::Function => "->",
506 ast::CallableKind::Operation => "=>",
507 };
508 write!(
509 f,
510 "({} {} {}{})",
511 AstTy { ty: input },
512 arrow,
513 AstTy { ty: output },
514 FunctorExpr { functors }
515 )
516 }
517 ast::TyKind::Hole => write!(f, "_"),
518 ast::TyKind::Paren(ty) => write!(f, "{}", AstTy { ty }),
519 ast::TyKind::Path(path) => write!(f, "{}", AstPathKind { path }),
520 ast::TyKind::Param(id) => write!(f, "{}", id.name),
521 ast::TyKind::Tuple(tys) => fmt_tuple(f, tys, |ty| AstTy { ty }),
522 ast::TyKind::Err => write!(f, "?"),
523 }
524 }
525}
526
527struct FunctorExpr<'a> {
528 functors: &'a Option<Box<ast::FunctorExpr>>,
529}
530
531impl<'a> Display for FunctorExpr<'a> {
532 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
533 match self.functors {
534 Some(functors) => {
535 let functors = eval_functor_expr(functors);
536 write!(f, "{}", FunctorSetValue { functors })
537 }
538 None => Ok(()),
539 }
540 }
541}
542
543struct AstPathKind<'a> {
544 path: &'a ast::PathKind,
545}
546
547impl<'a> Display for AstPathKind<'a> {
548 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
549 if let ast::PathKind::Ok(path) = self.path {
550 write!(f, "{}", path.full_name())
551 } else {
552 write!(f, "?")
553 }
554 }
555}
556
557struct TyDef<'a> {
558 def: &'a ast::TyDef,
559}
560
561impl<'a> Display for TyDef<'a> {
562 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
563 match self.def.kind.as_ref() {
564 ast::TyDefKind::Field(name, ty) => match name {
565 Some(name) => write!(f, "{} : {}", name.name, AstTy { ty }),
566 None => write!(f, "{}", AstTy { ty }),
567 },
568 ast::TyDefKind::Paren(def) => write!(f, "{}", TyDef { def }),
569 ast::TyDefKind::Tuple(tys) => fmt_tuple(f, tys, |def| TyDef { def }),
570 ast::TyDefKind::Err => write!(f, "?"),
571 }
572 }
573}
574
575fn fmt_tuple<'a, I, O>(
576 formatter: &mut Formatter,
577 items: &'a [I],
578 map: impl Fn(&'a I) -> O,
579) -> Result
580where
581 O: Display,
582{
583 let mut elements = items.iter();
584 if let Some(elem) = elements.next() {
585 write!(formatter, "({}", map(elem))?;
586 if elements.len() == 0 {
587 write!(formatter, ",)")?;
588 } else {
589 for elem in elements {
590 write!(formatter, ", {}", map(elem))?;
591 }
592 write!(formatter, ")")?;
593 }
594 } else {
595 write!(formatter, "Unit")?;
596 }
597 Ok(())
598}
599
600fn fmt_brace_seq<'a, I, O>(
601 formatter: &mut Formatter<'_>,
602 items: &'a [I],
603 map: impl Fn(&'a I) -> O,
604) -> Result
605where
606 O: Display,
607{
608 write!(formatter, "{{ ")?;
609 if let Some((last, most)) = items.split_last() {
610 for item in most {
611 write!(formatter, "{}, ", map(item))?;
612 }
613 write!(formatter, "{} ", map(last))?;
614 }
615 write!(formatter, "}}")
616}
617
618fn display_type_params(generics: &[GenericParam]) -> String {
619 let type_params = generics
620 .iter()
621 .filter_map(|generic| match generic {
622 GenericParam::Ty(name) => Some(name.name.clone()),
623 GenericParam::Functor(_) => None,
624 })
625 .collect::<Vec<_>>()
626 .join(", ");
627 if type_params.is_empty() {
628 type_params
629 } else {
630 format!("<{type_params}>")
631 }
632}
633
634//
635// helpers that don't manipulate any strings
636//
637
638fn ast_callable_functors(callable: &ast::CallableDecl) -> ty::FunctorSetValue {
639 let mut functors = callable
640 .functors
641 .as_ref()
642 .map_or(ty::FunctorSetValue::Empty, |f| {
643 eval_functor_expr(f.as_ref())
644 });
645
646 if let ast::CallableBody::Specs(specs) = callable.body.as_ref() {
647 for spec in specs {
648 let spec_functors = match spec.spec {
649 ast::Spec::Body => ty::FunctorSetValue::Empty,
650 ast::Spec::Adj => ty::FunctorSetValue::Adj,
651 ast::Spec::Ctl => ty::FunctorSetValue::Ctl,
652 ast::Spec::CtlAdj => ty::FunctorSetValue::CtlAdj,
653 };
654 functors = functors.union(&spec_functors);
655 }
656 }
657
658 functors
659}
660
661fn eval_functor_expr(expr: &ast::FunctorExpr) -> ty::FunctorSetValue {
662 match expr.kind.as_ref() {
663 ast::FunctorExprKind::BinOp(op, lhs, rhs) => {
664 let lhs_functors = eval_functor_expr(lhs);
665 let rhs_functors = eval_functor_expr(rhs);
666 match op {
667 ast::SetOp::Union => lhs_functors.union(&rhs_functors),
668 ast::SetOp::Intersect => lhs_functors.intersect(&rhs_functors),
669 }
670 }
671 ast::FunctorExprKind::Lit(ast::Functor::Adj) => ty::FunctorSetValue::Adj,
672 ast::FunctorExprKind::Lit(ast::Functor::Ctl) => ty::FunctorSetValue::Ctl,
673 ast::FunctorExprKind::Paren(inner) => eval_functor_expr(inner),
674 }
675}
676
677fn as_struct(ty_def: &ast::TyDef) -> Option<Vec<ast::FieldDef>> {
678 match ty_def.kind.as_ref() {
679 ast::TyDefKind::Paren(inner) => as_struct(inner),
680 ast::TyDefKind::Tuple(fields) => {
681 let mut converted_fields = Vec::new();
682 for field in fields {
683 let field = remove_parens(field);
684 match field.kind.as_ref() {
685 ast::TyDefKind::Field(Some(name), field_ty) => {
686 converted_fields.push(ast::FieldDef {
687 id: field.id,
688 span: field.span,
689 name: name.clone(),
690 ty: field_ty.clone(),
691 });
692 }
693 _ => return None,
694 }
695 }
696 Some(converted_fields)
697 }
698 ast::TyDefKind::Err | ast::TyDefKind::Field(..) => None,
699 }
700}
701
702fn remove_parens(ty_def: &ast::TyDef) -> &ast::TyDef {
703 match ty_def.kind.as_ref() {
704 ast::TyDefKind::Paren(inner) => remove_parens(inner.as_ref()),
705 _ => ty_def,
706 }
707}
708
709//
710// parsing functions for working with doc comments
711//
712
713/// Takes a doc string from Q# and increases all of the markdown header levels by one level.
714/// i.e. `# Summary` becomes `## Summary`
715#[must_use]
716pub fn increase_header_level(doc: &str) -> String {
717 let re = Regex::new(r"(?mi)^(#+)( [\s\S]+?)$").expect("Invalid regex");
718 re.replace_all(doc, "$1#$2").to_string()
719}
720
721/// Takes a doc string from Q# and returns the contents of the `# Summary` section. If no
722/// such section can be found, returns the original doc string.
723#[must_use]
724pub fn parse_doc_for_summary(doc: &str) -> String {
725 let re = Regex::new(r"(?mi)(?:^# Summary$)([\s\S]*?)(?:(^# .*)|\z)").expect("Invalid regex");
726 match re.captures(doc) {
727 Some(captures) => {
728 let capture = captures
729 .get(1)
730 .expect("Didn't find the capture for the given regex");
731 capture.as_str()
732 }
733 None => doc,
734 }
735 .trim()
736 .to_string()
737}
738
739/// Takes a doc string from a Q# callable and the name of a parameter of
740/// that callable. Returns the description of that parameter found in the
741/// doc string. If no description is found, returns the empty string.
742#[must_use]
743pub fn parse_doc_for_param(doc: &str, param: &str) -> String {
744 let re = Regex::new(r"(?mi)(?:^# Input$)([\s\S]*?)(?:(^# .*)|\z)").expect("Invalid regex");
745 let input = match re.captures(doc) {
746 Some(captures) => {
747 let capture = captures
748 .get(1)
749 .expect("Didn't find the capture for the given regex");
750 capture.as_str()
751 }
752 None => return String::new(),
753 }
754 .trim();
755
756 let re = Regex::new(format!(r"(?mi)(?:^## {param}$)([\s\S]*?)(?:(^(#|##) .*)|\z)").as_str())
757 .expect("Invalid regex");
758 match re.captures(input) {
759 Some(captures) => {
760 let capture = captures
761 .get(1)
762 .expect("Didn't find the capture for the given regex");
763 capture.as_str()
764 }
765 None => return String::new(),
766 }
767 .trim()
768 .to_string()
769}
770