1use crate::writer::{Writer, comment_lines, escape_char, escape_string, verbatim_lines};
9use alloc::format;
10use alloc::string::{String, ToString};
11use alloc::vec::Vec;
12use zerodds_lang_cst::common::{Doc, Ident, Literal, Path, Visibility};
13use zerodds_lang_cst::rust::{
14 Attr, AttrArg, Block, Const, Derive, Enum, Expr, Field, File, Function, GenericParam, Impl,
15 Item, MatchArm, Module, Param, Pattern, Receiver, Statement, Struct, StructBody, Trait, Type,
16 TypeAlias, Use, Variant, VariantBody,
17};
18
19#[must_use]
21pub fn file(file: &File) -> String {
22 let mut w = Writer::spaces4();
23 for attr in &file.inner_attrs {
24 w.line(&attr_text(attr, true));
25 }
26 if !file.inner_attrs.is_empty() {
27 w.blank_line();
28 }
29 doc(&mut w, &file.doc);
30 for use_ in &file.uses {
31 w.line(&use_text(use_));
32 }
33 if !file.uses.is_empty() {
34 w.blank_line();
35 }
36 for (index, item) in file.items.iter().enumerate() {
37 if index > 0 {
38 w.blank_line();
39 }
40 render_item(&mut w, item);
41 }
42 w.finish()
43}
44
45#[must_use]
47pub fn item(node: &Item) -> String {
48 let mut w = Writer::spaces4();
49 render_item(&mut w, node);
50 w.finish()
51}
52
53#[must_use]
55pub fn type_(node: &Type) -> String {
56 type_text(node)
57}
58
59#[must_use]
61pub fn expr(node: &Expr) -> String {
62 expr_text(node)
63}
64
65fn render_item(w: &mut Writer, node: &Item) {
69 match node {
70 Item::Module(module) => render_module(w, module),
71 Item::Struct(s) => render_struct(w, s),
72 Item::Enum(e) => render_enum(w, e),
73 Item::TypeAlias(alias) => render_alias(w, alias),
74 Item::Const(constant) => render_const(w, constant),
75 Item::Trait(t) => render_trait(w, t),
76 Item::Impl(i) => render_impl(w, i),
77 Item::Function(f) => render_function(w, f),
78 Item::Verbatim(text) => verbatim_lines(w, text),
79 }
80}
81
82fn render_module(w: &mut Writer, node: &Module) {
86 doc(w, &node.doc);
87 attrs(w, &node.attrs);
88 let mut head = String::new();
89 head.push_str(&vis_text(node.visibility));
90 head.push_str("mod ");
91 head.push_str(node.name.as_str());
92 head.push_str(" {");
93 w.line(&head);
94 w.indent();
95 for use_ in &node.uses {
96 w.line(&use_text(use_));
97 }
98 if !node.uses.is_empty() && !node.items.is_empty() {
99 w.blank_line();
100 }
101 for (index, item) in node.items.iter().enumerate() {
102 if index > 0 {
103 w.blank_line();
104 }
105 render_item(w, item);
106 }
107 w.dedent();
108 w.line("}");
109}
110
111fn render_struct(w: &mut Writer, node: &Struct) {
112 doc(w, &node.doc);
113 attrs(w, &node.attrs);
114 derives(w, &node.derives);
115 let mut head = String::new();
116 head.push_str(&vis_text(node.visibility));
117 head.push_str("struct ");
118 head.push_str(node.name.as_str());
119 head.push_str(&generics_text(&node.generics));
120
121 let text = &node.body_text;
122 let has_text = !text.begin.is_empty() || !text.end.is_empty();
123 match &node.body {
124 StructBody::Unit if has_text => {
125 head.push_str(" {");
126 w.line(&head);
127 w.indent();
128 for line in text.begin.iter().chain(&text.end) {
129 verbatim_lines(w, line);
130 }
131 w.dedent();
132 w.line("}");
133 }
134 StructBody::Unit => {
135 head.push(';');
136 w.line(&head);
137 }
138 StructBody::Tuple(types) => {
139 head.push('(');
140 let parts: Vec<String> = types.iter().map(type_text).collect();
141 head.push_str(&parts.join(", "));
142 head.push_str(");");
143 w.line(&head);
144 }
145 StructBody::Named(fields) => {
146 head.push_str(" {");
147 w.line(&head);
148 w.indent();
149 for line in &text.begin {
150 verbatim_lines(w, line);
151 }
152 for field in fields {
153 render_field(w, field);
154 }
155 for line in &text.end {
156 verbatim_lines(w, line);
157 }
158 w.dedent();
159 w.line("}");
160 }
161 }
162}
163
164fn render_field(w: &mut Writer, node: &Field) {
165 doc(w, &node.doc);
166 attrs(w, &node.attrs);
167 let mut line = String::new();
168 line.push_str(&vis_text(node.visibility));
169 line.push_str(node.name.as_str());
170 line.push_str(": ");
171 line.push_str(&type_text(&node.type_));
172 line.push(',');
173 w.line(&line);
174}
175
176fn render_enum(w: &mut Writer, node: &Enum) {
177 doc(w, &node.doc);
178 attrs(w, &node.attrs);
179 derives(w, &node.derives);
180 let mut head = String::new();
181 head.push_str(&vis_text(node.visibility));
182 head.push_str("enum ");
183 head.push_str(node.name.as_str());
184 head.push_str(&generics_text(&node.generics));
185 head.push_str(" {");
186 w.line(&head);
187 w.indent();
188 for variant in &node.variants {
189 render_variant(w, variant);
190 }
191 w.dedent();
192 w.line("}");
193}
194
195fn render_variant(w: &mut Writer, node: &Variant) {
196 doc(w, &node.doc);
197 attrs(w, &node.attrs);
198 let mut line = String::from(node.name.as_str());
199 match &node.body {
200 VariantBody::Unit => {}
201 VariantBody::Tuple(types) => {
202 line.push('(');
203 let parts: Vec<String> = types.iter().map(type_text).collect();
204 line.push_str(&parts.join(", "));
205 line.push(')');
206 }
207 VariantBody::Named(fields) => {
208 line.push_str(" {");
209 w.line(&line);
210 w.indent();
211 for field in fields {
212 render_field(w, field);
213 }
214 w.dedent();
215 w.line("},");
216 return;
217 }
218 }
219 if let Some(value) = &node.discriminant {
220 line.push_str(" = ");
221 line.push_str(&expr_text(value));
222 }
223 line.push(',');
224 w.line(&line);
225}
226
227fn render_alias(w: &mut Writer, node: &TypeAlias) {
228 doc(w, &node.doc);
229 attrs(w, &node.attrs);
230 let mut line = String::new();
231 line.push_str(&vis_text(node.visibility));
232 line.push_str("type ");
233 line.push_str(node.name.as_str());
234 line.push_str(&generics_text(&node.generics));
235 line.push_str(" = ");
236 line.push_str(&type_text(&node.type_));
237 line.push(';');
238 w.line(&line);
239}
240
241fn render_const(w: &mut Writer, node: &Const) {
242 doc(w, &node.doc);
243 attrs(w, &node.attrs);
244 let mut line = String::new();
245 line.push_str(&vis_text(node.visibility));
246 line.push_str("const ");
247 line.push_str(node.name.as_str());
248 line.push_str(": ");
249 line.push_str(&type_text(&node.type_));
250 line.push_str(" = ");
251 line.push_str(&expr_text(&node.value));
252 line.push(';');
253 w.line(&line);
254}
255
256fn render_trait(w: &mut Writer, node: &Trait) {
260 doc(w, &node.doc);
261 attrs(w, &node.attrs);
262 let mut head = String::new();
263 head.push_str(&vis_text(node.visibility));
264 head.push_str("trait ");
265 head.push_str(node.name.as_str());
266 head.push_str(&generics_text(&node.generics));
267 if !node.supertraits.is_empty() {
268 head.push_str(": ");
269 let parts: Vec<String> = node.supertraits.iter().map(type_text).collect();
270 head.push_str(&parts.join(" + "));
271 }
272 head.push_str(" {");
273 w.line(&head);
274 w.indent();
275 for (index, item) in node.items.iter().enumerate() {
276 if index > 0 {
277 w.blank_line();
278 }
279 render_item(w, item);
280 }
281 w.dedent();
282 w.line("}");
283}
284
285fn render_impl(w: &mut Writer, node: &Impl) {
289 doc(w, &node.doc);
290 attrs(w, &node.attrs);
291 let mut head = String::from("impl");
292 head.push_str(&generics_text(&node.generics));
293 head.push(' ');
294 if let Some(trait_) = &node.trait_ {
295 head.push_str(&type_text(trait_));
296 head.push_str(" for ");
297 }
298 head.push_str(&type_text(&node.self_type));
299 head.push_str(" {");
300 w.line(&head);
301 w.indent();
302 for (index, item) in node.items.iter().enumerate() {
303 if index > 0 {
304 w.blank_line();
305 }
306 render_item(w, item);
307 }
308 w.dedent();
309 w.line("}");
310}
311
312fn render_function(w: &mut Writer, node: &Function) {
313 doc(w, &node.doc);
314 attrs(w, &node.attrs);
315 let mut head = String::new();
316 head.push_str(&vis_text(node.visibility));
317 head.push_str("fn ");
318 head.push_str(node.name.as_str());
319 head.push_str(&generics_text(&node.generics));
320 head.push('(');
321 let parts: Vec<String> = node.params.iter().map(param_text).collect();
322 head.push_str(&parts.join(", "));
323 head.push(')');
324 if let Some(returns) = &node.returns {
325 head.push_str(" -> ");
326 head.push_str(&type_text(returns));
327 }
328 match &node.body {
329 None => {
330 head.push(';');
331 w.line(&head);
332 }
333 Some(block) => {
334 head.push_str(" {");
335 w.line(&head);
336 w.indent();
337 render_block(w, block);
338 w.dedent();
339 w.line("}");
340 }
341 }
342}
343
344fn sole_if(block: &Block) -> Option<&Expr> {
346 match block.statements.as_slice() {
347 [Statement::Semi(inner @ Expr::If { .. }) | Statement::Tail(inner @ Expr::If { .. })] => {
348 Some(inner)
349 }
350 _ => None,
351 }
352}
353
354fn closure_body_text(body: &Expr) -> String {
362 let Expr::Block(block) = body else {
363 return expr_text(body);
364 };
365 let mut parts = Vec::with_capacity(block.statements.len());
366 for statement in &block.statements {
367 let text = match statement {
368 Statement::Semi(value) => format!("{};", expr_text(value)),
369 Statement::Tail(value) => expr_text(value),
370 Statement::Let { .. } | Statement::Item(_) => return expr_text(body),
371 };
372 if text.contains('\n') {
373 return expr_text(body);
374 }
375 parts.push(text);
376 }
377 format!("{{ {} }}", parts.join(" "))
378}
379
380fn render_block(w: &mut Writer, block: &Block) {
381 for statement in &block.statements {
382 render_statement(w, statement);
383 }
384}
385
386fn render_statement(w: &mut Writer, node: &Statement) {
387 match node {
388 Statement::Let {
389 name,
390 type_,
391 value,
392 mutable,
393 } => {
394 let mut line = String::from("let ");
395 if *mutable {
396 line.push_str("mut ");
397 }
398 line.push_str(name.as_str());
399 if let Some(type_) = type_ {
400 line.push_str(": ");
401 line.push_str(&type_text(type_));
402 }
403 if let Some(value) = value {
404 line.push_str(" = ");
405 line.push_str(&expr_text(value));
406 }
407 line.push(';');
408 w.line(&line);
409 }
410 Statement::Semi(value) => {
411 let mut line = expr_text(value);
412 line.push(';');
413 w.line(&line);
414 }
415 Statement::Tail(value) => w.line(&expr_text(value)),
416 Statement::Item(item) => render_item(w, item),
417 }
418}
419
420fn param_text(node: &Param) -> String {
421 if let Some(receiver) = node.receiver {
422 return match receiver {
423 Receiver::Value => "self".to_string(),
424 Receiver::Ref => "&self".to_string(),
425 Receiver::RefMut => "&mut self".to_string(),
426 };
427 }
428 let mut out = String::new();
429 if let Some(name) = &node.name {
430 out.push_str(name.as_str());
431 out.push_str(": ");
432 }
433 out.push_str(&type_text(&node.type_));
434 out
435}
436
437fn generics_text(params: &[GenericParam]) -> String {
438 if params.is_empty() {
439 return String::new();
440 }
441 let mut out = String::from("<");
442 let parts: Vec<String> = params
443 .iter()
444 .map(|param| {
445 let mut text = String::new();
446 if param.lifetime {
447 text.push('\'');
448 }
449 text.push_str(param.name.as_str());
450 if !param.bounds.is_empty() {
451 text.push_str(": ");
452 let bounds: Vec<String> = param.bounds.iter().map(type_text).collect();
453 text.push_str(&bounds.join(" + "));
454 }
455 text
456 })
457 .collect();
458 out.push_str(&parts.join(", "));
459 out.push('>');
460 out
461}
462
463fn vis_text(visibility: Visibility) -> String {
464 match visibility {
465 Visibility::Public => "pub ".to_string(),
466 Visibility::Internal => "pub(crate) ".to_string(),
467 Visibility::Private => String::new(),
468 }
469}
470
471fn doc(w: &mut Writer, doc: &Doc) {
472 if doc.is_empty() {
473 return;
474 }
475 comment_lines(w, "/// ", doc.as_lines());
476}
477
478fn attrs(w: &mut Writer, attrs: &[Attr]) {
479 for attr in attrs {
480 w.line(&attr_text(attr, false));
481 }
482}
483
484fn derives(w: &mut Writer, derives: &[Derive]) {
486 if derives.is_empty() {
487 return;
488 }
489 let names: Vec<String> = derives.iter().map(|d| path_text(&d.path)).collect();
490 let mut line = String::from("#[derive(");
491 line.push_str(&names.join(", "));
492 line.push_str(")]");
493 w.line(&line);
494}
495
496fn attr_text(attr: &Attr, inner: bool) -> String {
497 let mut out = String::from(if inner { "#![" } else { "#[" });
498 out.push_str(&path_text(&attr.path));
499 if !attr.args.is_empty() {
500 out.push('(');
501 let parts: Vec<String> = attr.args.iter().map(attr_arg_text).collect();
502 out.push_str(&parts.join(", "));
503 out.push(')');
504 }
505 out.push(']');
506 out
507}
508
509fn attr_arg_text(arg: &AttrArg) -> String {
510 match arg {
511 AttrArg::Word(name) => name.as_str().to_string(),
512 AttrArg::Pair { name, value } => {
513 let mut out = name.as_str().to_string();
514 out.push_str(" = ");
515 out.push_str(&literal_text(value));
516 out
517 }
518 AttrArg::Nested { name, args } => {
519 let mut out = name.as_str().to_string();
520 out.push('(');
521 let parts: Vec<String> = args.iter().map(attr_arg_text).collect();
522 out.push_str(&parts.join(", "));
523 out.push(')');
524 out
525 }
526 }
527}
528
529fn use_text(node: &Use) -> String {
530 let mut out = vis_text(node.visibility);
531 out.push_str("use ");
532 out.push_str(&path_text(&node.path));
533 match node.names.len() {
534 0 => {}
535 1 => {
536 out.push_str("::");
537 out.push_str(node.names[0].as_str());
538 }
539 _ => {
540 out.push_str("::{");
541 let parts: Vec<&str> = node.names.iter().map(Ident::as_str).collect();
542 out.push_str(&parts.join(", "));
543 out.push('}');
544 }
545 }
546 out.push(';');
547 out
548}
549
550fn path_text(path: &Path) -> String {
551 let parts: Vec<&str> = path.segments().iter().map(Ident::as_str).collect();
552 let joined = parts.join("::");
553 if path.is_rooted() {
554 let mut out = String::from("crate::");
555 out.push_str(&joined);
556 return out;
557 }
558 joined
559}
560
561fn type_text(node: &Type) -> String {
569 match node {
570 Type::Named { path, args } => {
571 let mut out = path_text(path);
572 if !args.is_empty() {
573 out.push('<');
574 let parts: Vec<String> = args.iter().map(type_text).collect();
575 out.push_str(&parts.join(", "));
576 out.push('>');
577 }
578 out
579 }
580 Type::Ref {
581 target,
582 mutable,
583 lifetime,
584 } => {
585 let mut out = String::from("&");
586 if let Some(lifetime) = lifetime {
587 out.push('\'');
588 out.push_str(lifetime.as_str());
589 out.push(' ');
590 }
591 if *mutable {
592 out.push_str("mut ");
593 }
594 out.push_str(&type_text(target));
595 out
596 }
597 Type::Array { element, length } => {
598 let mut out = String::from("[");
599 out.push_str(&type_text(element));
600 out.push_str("; ");
601 out.push_str(&expr_text(length));
602 out.push(']');
603 out
604 }
605 Type::Slice(element) => {
606 let mut out = String::from("[");
607 out.push_str(&type_text(element));
608 out.push(']');
609 out
610 }
611 Type::Tuple(elements) => {
612 let mut out = String::from("(");
613 let parts: Vec<String> = elements.iter().map(type_text).collect();
614 out.push_str(&parts.join(", "));
615 if elements.len() == 1 {
617 out.push(',');
618 }
619 out.push(')');
620 out
621 }
622 Type::Primitive(primitive) => primitive.as_str().to_string(),
623 Type::ImplTrait(bounds) => {
624 let mut out = String::from("impl ");
625 let parts: Vec<String> = bounds.iter().map(type_text).collect();
626 out.push_str(&parts.join(" + "));
627 out
628 }
629 Type::DynTrait(bounds) => {
630 let mut out = String::from("dyn ");
631 let parts: Vec<String> = bounds.iter().map(type_text).collect();
632 out.push_str(&parts.join(" + "));
633 out
634 }
635 }
636}
637
638fn literal_text(literal: &Literal) -> String {
639 match literal {
640 Literal::Int(text) | Literal::Float(text) => text.clone(),
641 Literal::Bool(value) => {
642 if *value {
643 "true".to_string()
644 } else {
645 "false".to_string()
646 }
647 }
648 Literal::Char(ch) => {
649 let mut out = String::from("'");
650 out.push_str(&escape_char(*ch));
651 out.push('\'');
652 out
653 }
654 Literal::Str(text) => {
655 let mut out = String::from("\"");
656 out.push_str(&escape_string(text));
657 out.push('"');
658 out
659 }
660 Literal::Bytes(bytes) => {
661 let mut out = String::from("&[");
662 let parts: Vec<String> = bytes.iter().map(|b| alloc::format!("{b}")).collect();
663 out.push_str(&parts.join(", "));
664 out.push(']');
665 out
666 }
667 Literal::Null => "None".to_string(),
668 }
669}
670
671fn parenthesised(node: &Expr) -> String {
685 match node {
686 Expr::Binary { .. }
687 | Expr::Cast { .. }
688 | Expr::Closure { .. }
689 | Expr::If { .. }
690 | Expr::Match { .. }
691 | Expr::Assign { .. }
692 | Expr::Return(_) => {
693 let mut out = String::from("(");
694 out.push_str(&expr_text(node));
695 out.push(')');
696 out
697 }
698 _ => expr_text(node),
699 }
700}
701
702#[allow(clippy::too_many_lines)]
709fn expr_text(node: &Expr) -> String {
710 match node {
711 Expr::Literal(literal) => literal_text(literal),
712 Expr::Path(path) => path_text(path),
713 Expr::MethodCall {
714 receiver,
715 method,
716 args,
717 } => {
718 let mut out = expr_text(receiver);
719 out.push('.');
720 out.push_str(method.as_str());
721 out.push('(');
722 let parts: Vec<String> = args.iter().map(expr_text).collect();
723 out.push_str(&parts.join(", "));
724 out.push(')');
725 out
726 }
727 Expr::Call { callee, args } => {
728 let mut out = expr_text(callee);
729 out.push('(');
730 let parts: Vec<String> = args.iter().map(expr_text).collect();
731 out.push_str(&parts.join(", "));
732 out.push(')');
733 out
734 }
735 Expr::StructLit { path, fields, base } => {
736 let mut out = path_text(path);
737 out.push_str(" { ");
738 let parts: Vec<String> = fields
739 .iter()
740 .map(|(name, value)| {
741 if let Expr::Path(value_path) = value {
748 if !value_path.is_rooted()
749 && value_path.segments().len() == 1
750 && value_path.last().map(Ident::as_str) == Some(name.as_str())
751 {
752 return name.as_str().to_string();
753 }
754 }
755 let mut text = name.as_str().to_string();
756 text.push_str(": ");
757 text.push_str(&expr_text(value));
758 text
759 })
760 .collect();
761 out.push_str(&parts.join(", "));
762 if let Some(base) = base {
763 if !fields.is_empty() {
764 out.push_str(", ");
765 }
766 out.push_str("..");
767 out.push_str(&expr_text(base));
768 }
769 out.push_str(" }");
770 out
771 }
772 Expr::Field { receiver, name } => {
773 let mut out = expr_text(receiver);
774 out.push('.');
775 out.push_str(name.as_str());
776 out
777 }
778 Expr::Ref { target, mutable } => {
779 let mut out = String::from("&");
780 if *mutable {
781 out.push_str("mut ");
782 }
783 out.push_str(&expr_text(target));
784 out
785 }
786 Expr::Try(inner) => {
787 let mut out = expr_text(inner);
788 out.push('?');
789 out
790 }
791 Expr::Closure { params, body } => {
792 let mut out = String::from("|");
793 for (index, param) in params.iter().enumerate() {
794 if index > 0 {
795 out.push_str(", ");
796 }
797 out.push_str(param.as_str());
798 }
799 out.push_str("| ");
800 out.push_str(&closure_body_text(body));
801 out
802 }
803 Expr::Match { scrutinee, arms } => {
804 let mut w = Writer::spaces4();
807 let mut head = String::from("match ");
808 head.push_str(&expr_text(scrutinee));
809 head.push_str(" {");
810 w.line(&head);
811 w.indent();
812 for arm in arms {
813 w.line(&arm_text(arm));
814 }
815 w.dedent();
816 w.push("}");
817 w.finish().trim_end().to_string()
818 }
819 Expr::If {
820 condition,
821 then,
822 otherwise,
823 } => {
824 let mut w = Writer::spaces4();
825 let mut head = String::from("if ");
826 head.push_str(&expr_text(condition));
827 head.push_str(" {");
828 w.line(&head);
829 w.indent();
830 render_block(&mut w, then);
831 w.dedent();
832 match otherwise {
833 Some(block) => match sole_if(block) {
839 Some(inner) => {
840 let mut tail = String::from("} else ");
841 tail.push_str(&expr_text(inner));
842 w.push(&tail);
843 }
844 None => {
845 w.line("} else {");
846 w.indent();
847 render_block(&mut w, block);
848 w.dedent();
849 w.push("}");
850 }
851 },
852 None => w.push("}"),
853 }
854 w.finish().trim_end().to_string()
855 }
856 Expr::For {
857 binding,
858 iterator,
859 body,
860 } => {
861 let mut w = Writer::spaces4();
862 let mut head = String::from("for ");
863 head.push_str(binding.as_str());
864 head.push_str(" in ");
865 head.push_str(&expr_text(iterator));
866 head.push_str(" {");
867 w.line(&head);
868 w.indent();
869 render_block(&mut w, body);
870 w.dedent();
871 w.push("}");
872 w.finish().trim_end().to_string()
873 }
874 Expr::While { condition, body } => {
875 let mut w = Writer::spaces4();
876 let mut head = String::from("while ");
877 head.push_str(&expr_text(condition));
878 head.push_str(" {");
879 w.line(&head);
880 w.indent();
881 render_block(&mut w, body);
882 w.dedent();
883 w.push("}");
884 w.finish().trim_end().to_string()
885 }
886 Expr::Assign { target, value } => {
887 let mut out = expr_text(target);
888 out.push_str(" = ");
889 out.push_str(&expr_text(value));
890 out
891 }
892 Expr::Return(value) => match value {
893 Some(value) => {
894 let mut out = String::from("return ");
895 out.push_str(&expr_text(value));
896 out
897 }
898 None => "return".to_string(),
899 },
900 Expr::Array(elements) => {
901 let mut out = String::from("[");
902 let parts: Vec<String> = elements.iter().map(expr_text).collect();
903 out.push_str(&parts.join(", "));
904 out.push(']');
905 out
906 }
907 Expr::Repeat { value, count } => {
908 let mut out = String::from("[");
909 out.push_str(&expr_text(value));
910 out.push_str("; ");
911 out.push_str(&expr_text(count));
912 out.push(']');
913 out
914 }
915 Expr::Tuple(elements) => {
916 let mut out = String::from("(");
917 let parts: Vec<String> = elements.iter().map(expr_text).collect();
918 out.push_str(&parts.join(", "));
919 if elements.len() == 1 {
920 out.push(',');
921 }
922 out.push(')');
923 out
924 }
925 Expr::Binary { op, lhs, rhs } => {
926 let mut out = parenthesised(lhs);
927 out.push(' ');
928 out.push_str(op.as_str());
929 out.push(' ');
930 out.push_str(&parenthesised(rhs));
931 out
932 }
933 Expr::Not(value) => {
934 let mut out = String::from("!");
935 out.push_str(&expr_text(value));
936 out
937 }
938 Expr::Deref(value) => {
939 let mut out = String::from("*");
940 out.push_str(&expr_text(value));
941 out
942 }
943 Expr::Cast { value, type_ } => {
944 let mut out = parenthesised(value);
945 out.push_str(" as ");
946 out.push_str(&type_text(type_));
947 out
948 }
949 Expr::Block(block) => {
950 let mut w = Writer::spaces4();
951 w.line("{");
952 w.indent();
953 render_block(&mut w, block);
954 w.dedent();
955 w.push("}");
956 w.finish().trim_end().to_string()
957 }
958 }
959}
960
961fn arm_text(arm: &MatchArm) -> String {
968 let mut out = pattern_text(&arm.pattern);
969 if let Some(guard) = &arm.guard {
970 out.push_str(" if ");
971 out.push_str(&expr_text(guard));
972 }
973 out.push_str(" => ");
974 out.push_str(&expr_text(&arm.body));
975 out.push(',');
976 out
977}
978
979fn pattern_text(pattern: &Pattern) -> String {
986 match pattern {
987 Pattern::Wildcard => "_".to_string(),
988 Pattern::Binding(name) => name.as_str().to_string(),
989 Pattern::Literal(literal) => literal_text(literal),
990 Pattern::Path(path) => path_text(path),
991 Pattern::TupleStruct { path, elements } => {
992 let mut out = path_text(path);
993 out.push('(');
994 let parts: Vec<String> = elements.iter().map(pattern_text).collect();
995 out.push_str(&parts.join(", "));
996 out.push(')');
997 out
998 }
999 Pattern::Struct { path, fields, rest } => {
1000 let mut out = path_text(path);
1001 out.push_str(" { ");
1002 let parts: Vec<String> = fields
1003 .iter()
1004 .map(|(name, pattern)| {
1005 let mut text = name.as_str().to_string();
1006 text.push_str(": ");
1007 text.push_str(&pattern_text(pattern));
1008 text
1009 })
1010 .collect();
1011 out.push_str(&parts.join(", "));
1012 if *rest {
1013 if !fields.is_empty() {
1014 out.push_str(", ");
1015 }
1016 out.push_str("..");
1017 }
1018 out.push_str(" }");
1019 out
1020 }
1021 }
1022}