1use crate::ast::*;
36use std::fmt::Write;
37
38pub fn serialize(agent: &AgentFile) -> String {
50 let mut w = Writer::new();
51 w.write_agent_file(agent);
52 w.finish()
53}
54
55struct Writer {
57 output: String,
58 indent: usize,
59}
60
61impl Writer {
62 fn new() -> Self {
63 Self {
64 output: String::new(),
65 indent: 0,
66 }
67 }
68
69 fn finish(self) -> String {
70 self.output
71 }
72
73 fn write_indent(&mut self) {
75 for _ in 0..self.indent {
76 self.output.push_str(" ");
77 }
78 }
79
80 fn indent(&mut self) {
82 self.indent += 1;
83 }
84
85 fn dedent(&mut self) {
87 if self.indent > 0 {
88 self.indent -= 1;
89 }
90 }
91
92 fn writeln(&mut self, text: &str) {
94 self.write_indent();
95 writeln!(self.output, "{}", text).unwrap();
96 }
97
98 #[allow(dead_code)]
100 fn write(&mut self, text: &str) {
101 write!(self.output, "{}", text).unwrap();
102 }
103
104 fn newline(&mut self) {
106 self.output.push('\n');
107 }
108
109 fn write_agent_file(&mut self, agent: &AgentFile) {
114 if let Some(config) = &agent.config {
116 self.write_config_block(&config.node);
117 self.newline();
118 }
119
120 if let Some(variables) = &agent.variables {
121 self.write_variables_block(&variables.node);
122 self.newline();
123 }
124
125 if let Some(system) = &agent.system {
126 self.write_system_block(&system.node);
127 self.newline();
128 }
129
130 for connection in &agent.connections {
131 self.write_connection_block(&connection.node);
132 self.newline();
133 }
134
135 if let Some(knowledge) = &agent.knowledge {
136 self.write_knowledge_block(&knowledge.node);
137 self.newline();
138 }
139
140 if let Some(language) = &agent.language {
141 self.write_language_block(&language.node);
142 self.newline();
143 }
144
145 if let Some(start_agent) = &agent.start_agent {
146 self.write_start_agent_block(&start_agent.node);
147 self.newline();
148 }
149
150 for topic in &agent.topics {
151 self.write_topic_block(&topic.node);
152 self.newline();
153 }
154 }
155
156 fn write_config_block(&mut self, config: &ConfigBlock) {
161 self.writeln("config:");
162 self.indent();
163
164 self.write_indent();
165 write!(self.output, "agent_name: \"{}\"", config.agent_name.node).unwrap();
166 self.newline();
167
168 if let Some(label) = &config.agent_label {
169 self.write_indent();
170 write!(self.output, "agent_label: \"{}\"", label.node).unwrap();
171 self.newline();
172 }
173
174 if let Some(desc) = &config.description {
175 self.write_indent();
176 write!(self.output, "description: \"{}\"", escape_string(&desc.node)).unwrap();
177 self.newline();
178 }
179
180 if let Some(agent_type) = &config.agent_type {
181 self.write_indent();
182 write!(self.output, "agent_type: \"{}\"", agent_type.node).unwrap();
183 self.newline();
184 }
185
186 if let Some(user) = &config.default_agent_user {
187 self.write_indent();
188 write!(self.output, "default_agent_user: \"{}\"", user.node).unwrap();
189 self.newline();
190 }
191
192 self.dedent();
193 }
194
195 fn write_variables_block(&mut self, vars: &VariablesBlock) {
200 self.writeln("variables:");
201 self.indent();
202
203 for var in &vars.variables {
204 self.write_variable_decl(&var.node);
205 }
206
207 self.dedent();
208 }
209
210 fn write_variable_decl(&mut self, var: &VariableDecl) {
211 self.write_indent();
212 write!(self.output, "{}: ", var.name.node).unwrap();
213
214 match var.kind {
216 VariableKind::Mutable => {
217 write!(self.output, "mutable {}", self.type_to_string(&var.ty.node)).unwrap();
218 if let Some(default) = &var.default {
219 write!(self.output, " = {}", self.expr_to_string(&default.node)).unwrap();
220 }
221 }
222 VariableKind::Linked => {
223 write!(self.output, "linked {}", self.type_to_string(&var.ty.node)).unwrap();
224 }
225 }
226 self.newline();
227
228 self.indent();
230 if let Some(desc) = &var.description {
231 self.write_indent();
232 write!(self.output, "description: \"{}\"", escape_string(&desc.node)).unwrap();
233 self.newline();
234 }
235
236 if let Some(source) = &var.source {
237 self.write_indent();
238 write!(self.output, "source: {}", self.reference_to_string(&source.node)).unwrap();
239 self.newline();
240 }
241 self.dedent();
242 }
243
244 fn write_system_block(&mut self, system: &SystemBlock) {
249 self.writeln("system:");
250 self.indent();
251
252 if let Some(instructions) = &system.instructions {
253 self.write_indent();
254 write!(self.output, "instructions:").unwrap();
255 self.write_instructions(&instructions.node);
256 }
257
258 if let Some(messages) = &system.messages {
259 self.writeln("messages:");
260 self.indent();
261
262 if let Some(welcome) = &messages.node.welcome {
263 self.write_indent();
264 write!(self.output, "welcome: \"{}\"", escape_string(&welcome.node)).unwrap();
265 self.newline();
266 }
267
268 if let Some(error) = &messages.node.error {
269 self.write_indent();
270 write!(self.output, "error: \"{}\"", escape_string(&error.node)).unwrap();
271 self.newline();
272 }
273
274 self.dedent();
275 }
276
277 self.dedent();
278 }
279
280 fn write_connection_block(&mut self, connection: &ConnectionBlock) {
285 self.write_indent();
287 write!(self.output, "connection {}:", connection.name.node).unwrap();
288 self.newline();
289
290 self.indent();
291 for entry in &connection.entries {
292 self.write_indent();
293 write!(
294 self.output,
295 "{}: \"{}\"",
296 entry.node.name.node,
297 escape_string(&entry.node.value.node)
298 )
299 .unwrap();
300 self.newline();
301 }
302 self.dedent();
303 }
304
305 fn write_knowledge_block(&mut self, knowledge: &KnowledgeBlock) {
310 self.writeln("knowledge:");
311 self.indent();
312
313 for entry in &knowledge.entries {
314 self.write_indent();
315 write!(
316 self.output,
317 "{}: {}",
318 entry.node.name.node,
319 self.expr_to_string(&entry.node.value.node)
320 )
321 .unwrap();
322 self.newline();
323 }
324
325 self.dedent();
326 }
327
328 fn write_language_block(&mut self, language: &LanguageBlock) {
333 self.writeln("language:");
334 self.indent();
335
336 for entry in &language.entries {
337 self.write_indent();
338 write!(
339 self.output,
340 "{}: {}",
341 entry.node.name.node,
342 self.expr_to_string(&entry.node.value.node)
343 )
344 .unwrap();
345 self.newline();
346 }
347
348 self.dedent();
349 }
350
351 fn write_start_agent_block(&mut self, start_agent: &StartAgentBlock) {
356 self.write_indent();
357 write!(self.output, "start_agent {}:", start_agent.name.node).unwrap();
358 self.newline();
359
360 self.indent();
361
362 if let Some(desc) = &start_agent.description {
363 self.write_indent();
364 write!(self.output, "description: \"{}\"", escape_string(&desc.node)).unwrap();
365 self.newline();
366 }
367
368 if let Some(system) = &start_agent.system {
369 self.write_topic_system_override(&system.node);
370 }
371
372 if let Some(actions) = &start_agent.actions {
373 self.write_actions_block(&actions.node);
374 }
375
376 if let Some(before) = &start_agent.before_reasoning {
377 self.writeln("before_reasoning:");
378 self.indent();
379 self.write_directive_block(&before.node);
380 self.dedent();
381 }
382
383 if let Some(reasoning) = &start_agent.reasoning {
384 self.write_reasoning_block(&reasoning.node);
385 }
386
387 if let Some(after) = &start_agent.after_reasoning {
388 self.writeln("after_reasoning:");
389 self.indent();
390 self.write_directive_block(&after.node);
391 self.dedent();
392 }
393
394 self.dedent();
395 }
396
397 fn write_topic_block(&mut self, topic: &TopicBlock) {
402 self.write_indent();
403 write!(self.output, "topic {}:", topic.name.node).unwrap();
404 self.newline();
405
406 self.indent();
407
408 if let Some(desc) = &topic.description {
409 self.write_indent();
410 write!(self.output, "description: \"{}\"", escape_string(&desc.node)).unwrap();
411 self.newline();
412 }
413
414 if let Some(system) = &topic.system {
415 self.write_topic_system_override(&system.node);
416 }
417
418 if let Some(actions) = &topic.actions {
419 self.write_actions_block(&actions.node);
420 }
421
422 if let Some(before) = &topic.before_reasoning {
423 self.writeln("before_reasoning:");
424 self.indent();
425 self.write_directive_block(&before.node);
426 self.dedent();
427 }
428
429 if let Some(reasoning) = &topic.reasoning {
430 self.write_reasoning_block(&reasoning.node);
431 }
432
433 if let Some(after) = &topic.after_reasoning {
434 self.writeln("after_reasoning:");
435 self.indent();
436 self.write_directive_block(&after.node);
437 self.dedent();
438 }
439
440 self.dedent();
441 }
442
443 fn write_topic_system_override(&mut self, system: &TopicSystemOverride) {
444 if let Some(instructions) = &system.instructions {
445 self.writeln("system:");
446 self.indent();
447 self.write_indent();
448 write!(self.output, "instructions:").unwrap();
449 self.write_instructions(&instructions.node);
450 self.dedent();
451 }
452 }
453
454 fn write_actions_block(&mut self, actions: &ActionsBlock) {
459 self.writeln("actions:");
460 self.indent();
461
462 for action in &actions.actions {
463 self.write_action_def(&action.node);
464 }
465
466 self.dedent();
467 }
468
469 fn write_action_def(&mut self, action: &ActionDef) {
470 self.write_indent();
471 write!(self.output, "{}:", action.name.node).unwrap();
472 self.newline();
473
474 self.indent();
475
476 if let Some(desc) = &action.description {
477 self.write_indent();
478 write!(self.output, "description: \"{}\"", escape_string(&desc.node)).unwrap();
479 self.newline();
480 }
481
482 if let Some(label) = &action.label {
483 self.write_indent();
484 write!(self.output, "label: \"{}\"", escape_string(&label.node)).unwrap();
485 self.newline();
486 }
487
488 if let Some(target) = &action.target {
489 self.write_indent();
490 write!(self.output, "target: \"{}\"", escape_string(&target.node)).unwrap();
491 self.newline();
492 }
493
494 if let Some(confirm) = &action.require_user_confirmation {
495 self.write_indent();
496 write!(
497 self.output,
498 "require_user_confirmation: {}",
499 if confirm.node { "True" } else { "False" }
500 )
501 .unwrap();
502 self.newline();
503 }
504
505 if let Some(progress) = &action.include_in_progress_indicator {
506 self.write_indent();
507 write!(
508 self.output,
509 "include_in_progress_indicator: {}",
510 if progress.node { "True" } else { "False" }
511 )
512 .unwrap();
513 self.newline();
514 }
515
516 if let Some(msg) = &action.progress_indicator_message {
517 self.write_indent();
518 write!(self.output, "progress_indicator_message: \"{}\"", escape_string(&msg.node))
519 .unwrap();
520 self.newline();
521 }
522
523 if let Some(inputs) = &action.inputs {
524 self.writeln("inputs:");
525 self.indent();
526 for param in &inputs.node {
527 self.write_param_def(¶m.node);
528 }
529 self.dedent();
530 }
531
532 if let Some(outputs) = &action.outputs {
533 self.writeln("outputs:");
534 self.indent();
535 for param in &outputs.node {
536 self.write_param_def(¶m.node);
537 }
538 self.dedent();
539 }
540
541 self.dedent();
542 }
543
544 fn write_param_def(&mut self, param: &ParamDef) {
545 self.write_indent();
546 write!(self.output, "{}: {}", param.name.node, self.type_to_string(¶m.ty.node))
547 .unwrap();
548 self.newline();
549
550 self.indent();
551
552 if let Some(desc) = ¶m.description {
553 self.write_indent();
554 write!(self.output, "description: \"{}\"", escape_string(&desc.node)).unwrap();
555 self.newline();
556 }
557
558 if let Some(label) = ¶m.label {
559 self.write_indent();
560 write!(self.output, "label: \"{}\"", escape_string(&label.node)).unwrap();
561 self.newline();
562 }
563
564 if let Some(required) = ¶m.is_required {
565 self.write_indent();
566 write!(self.output, "is_required: {}", if required.node { "True" } else { "False" })
567 .unwrap();
568 self.newline();
569 }
570
571 if let Some(filter) = ¶m.filter_from_agent {
572 self.write_indent();
573 write!(
574 self.output,
575 "filter_from_agent: {}",
576 if filter.node { "True" } else { "False" }
577 )
578 .unwrap();
579 self.newline();
580 }
581
582 if let Some(displayable) = ¶m.is_displayable {
583 self.write_indent();
584 write!(
585 self.output,
586 "is_displayable: {}",
587 if displayable.node { "True" } else { "False" }
588 )
589 .unwrap();
590 self.newline();
591 }
592
593 if let Some(complex) = ¶m.complex_data_type_name {
594 self.write_indent();
595 write!(self.output, "complex_data_type_name: \"{}\"", escape_string(&complex.node))
596 .unwrap();
597 self.newline();
598 }
599
600 self.dedent();
601 }
602
603 fn write_directive_block(&mut self, block: &DirectiveBlock) {
608 for stmt in &block.statements {
609 self.write_statement(&stmt.node, false);
610 }
611 }
612
613 fn write_statement(&mut self, stmt: &Stmt, _in_reasoning: bool) {
614 match stmt {
615 Stmt::Set { target, value } => {
616 self.write_indent();
617 write!(
618 self.output,
619 "set {} = {}",
620 self.reference_to_string(&target.node),
621 self.expr_to_string(&value.node)
622 )
623 .unwrap();
624 self.newline();
625 }
626 Stmt::Run {
627 action,
628 with_clauses,
629 set_clauses,
630 } => {
631 self.write_indent();
632 write!(self.output, "run {}", self.reference_to_string(&action.node)).unwrap();
633 self.newline();
634
635 self.indent();
636 for with_clause in with_clauses {
637 self.write_with_clause(&with_clause.node);
638 }
639 for set_clause in set_clauses {
640 self.write_set_clause(&set_clause.node);
641 }
642 self.dedent();
643 }
644 Stmt::If {
645 condition,
646 then_block,
647 else_block,
648 } => {
649 self.write_indent();
650 write!(self.output, "if {}:", self.expr_to_string(&condition.node)).unwrap();
651 self.newline();
652
653 self.indent();
654 for then_stmt in then_block {
655 self.write_statement(&then_stmt.node, _in_reasoning);
656 }
657 self.dedent();
658
659 if let Some(else_stmts) = else_block {
660 self.writeln("else:");
661 self.indent();
662 for else_stmt in else_stmts {
663 self.write_statement(&else_stmt.node, _in_reasoning);
664 }
665 self.dedent();
666 }
667 }
668 Stmt::Transition { target } => {
669 self.write_indent();
670 write!(self.output, "transition to {}", self.reference_to_string(&target.node))
671 .unwrap();
672 self.newline();
673 }
674 }
675 }
676
677 fn write_reasoning_block(&mut self, reasoning: &ReasoningBlock) {
682 self.writeln("reasoning:");
683 self.indent();
684
685 if let Some(instructions) = &reasoning.instructions {
686 self.write_indent();
687 write!(self.output, "instructions:").unwrap();
688 self.write_instructions(&instructions.node);
689 }
690
691 if let Some(actions) = &reasoning.actions {
692 self.writeln("actions:");
693 self.indent();
694 for action in &actions.node {
695 self.write_reasoning_action(&action.node);
696 }
697 self.dedent();
698 }
699
700 self.dedent();
701 }
702
703 fn write_reasoning_action(&mut self, action: &ReasoningAction) {
704 self.write_indent();
705 write!(
706 self.output,
707 "{}: {}",
708 action.name.node,
709 self.reasoning_action_target_to_string(&action.target.node)
710 )
711 .unwrap();
712 self.newline();
713
714 self.indent();
715
716 if let Some(desc) = &action.description {
717 self.write_indent();
718 write!(self.output, "description: \"{}\"", escape_string(&desc.node)).unwrap();
719 self.newline();
720 }
721
722 if let Some(available) = &action.available_when {
723 self.write_indent();
724 write!(self.output, "available when {}", self.expr_to_string(&available.node)).unwrap();
725 self.newline();
726 }
727
728 for with_clause in &action.with_clauses {
729 self.write_with_clause(&with_clause.node);
730 }
731
732 for set_clause in &action.set_clauses {
733 self.write_set_clause(&set_clause.node);
734 }
735
736 for run_clause in &action.run_clauses {
737 self.write_run_clause(&run_clause.node);
738 }
739
740 for if_clause in &action.if_clauses {
741 self.write_if_clause(&if_clause.node);
742 }
743
744 if let Some(transition) = &action.transition {
745 self.write_indent();
746 write!(self.output, "transition to {}", self.reference_to_string(&transition.node))
747 .unwrap();
748 self.newline();
749 }
750
751 self.dedent();
752 }
753
754 fn write_with_clause(&mut self, with: &WithClause) {
755 self.write_indent();
756 write!(self.output, "with {} = ", with.param.node).unwrap();
757 match &with.value.node {
758 WithValue::Expr(expr) => {
759 write!(self.output, "{}", self.expr_to_string(expr)).unwrap();
760 }
761 }
762 self.newline();
763 }
764
765 fn write_set_clause(&mut self, set: &SetClause) {
766 self.write_indent();
767 write!(
768 self.output,
769 "set {} = {}",
770 self.reference_to_string(&set.target.node),
771 self.expr_to_string(&set.source.node)
772 )
773 .unwrap();
774 self.newline();
775 }
776
777 fn write_run_clause(&mut self, run: &RunClause) {
778 self.write_indent();
779 write!(self.output, "run {}", self.reference_to_string(&run.action.node)).unwrap();
780 self.newline();
781
782 self.indent();
783 for with_clause in &run.with_clauses {
784 self.write_with_clause(&with_clause.node);
785 }
786 for set_clause in &run.set_clauses {
787 self.write_set_clause(&set_clause.node);
788 }
789 self.dedent();
790 }
791
792 fn write_if_clause(&mut self, if_clause: &IfClause) {
793 self.write_indent();
794 write!(self.output, "if {}:", self.expr_to_string(&if_clause.condition.node)).unwrap();
795 self.newline();
796
797 if let Some(transition) = &if_clause.transition {
798 self.indent();
799 self.write_indent();
800 write!(self.output, "transition to {}", self.reference_to_string(&transition.node))
801 .unwrap();
802 self.newline();
803 self.dedent();
804 }
805 }
806
807 fn write_instructions(&mut self, instructions: &Instructions) {
812 match instructions {
813 Instructions::Simple(text) => {
814 write!(self.output, " \"{}\"", escape_string(text)).unwrap();
816 self.newline();
817 }
818 Instructions::Static(lines) => {
819 write!(self.output, "|").unwrap();
821 self.newline();
822 self.indent();
823 for line in lines {
824 self.write_indent();
825 write!(self.output, "{}", line.node).unwrap();
826 self.newline();
827 }
828 self.dedent();
829 }
830 Instructions::Dynamic(parts) => {
831 write!(self.output, "->").unwrap();
833 self.newline();
834 self.indent();
835 for part in parts {
836 self.write_instruction_part(&part.node);
837 }
838 self.dedent();
839 }
840 }
841 }
842
843 fn write_instruction_part(&mut self, part: &InstructionPart) {
844 match part {
845 InstructionPart::Text(text) => {
846 let lines: Vec<&str> = text.split('\n').collect();
848 for (i, line) in lines.iter().enumerate() {
849 self.write_indent();
850 if i == 0 {
851 write!(self.output, "| {}", line).unwrap();
852 } else {
853 write!(self.output, " {}", line).unwrap();
855 }
856 self.newline();
857 }
858 }
859 InstructionPart::Interpolation(expr) => {
860 self.write_indent();
861 write!(self.output, "{{!{}}}", self.expr_to_string(expr)).unwrap();
862 self.newline();
863 }
864 InstructionPart::Conditional {
865 condition,
866 then_parts,
867 else_parts,
868 } => {
869 self.write_indent();
870 write!(self.output, "if {}:", self.expr_to_string(&condition.node)).unwrap();
871 self.newline();
872
873 self.indent();
874 for then_part in then_parts {
875 self.write_instruction_part(&then_part.node);
876 }
877 self.dedent();
878
879 if let Some(else_ps) = else_parts {
880 self.writeln("else:");
881 self.indent();
882 for else_part in else_ps {
883 self.write_instruction_part(&else_part.node);
884 }
885 self.dedent();
886 }
887 }
888 }
889 }
890
891 #[allow(clippy::only_used_in_recursion)]
896 fn type_to_string(&self, ty: &Type) -> String {
897 match ty {
898 Type::String => "string".to_string(),
899 Type::Number => "number".to_string(),
900 Type::Boolean => "boolean".to_string(),
901 Type::Object => "object".to_string(),
902 Type::Date => "date".to_string(),
903 Type::Timestamp => "timestamp".to_string(),
904 Type::Currency => "currency".to_string(),
905 Type::Id => "id".to_string(),
906 Type::Datetime => "datetime".to_string(),
907 Type::Time => "time".to_string(),
908 Type::Integer => "integer".to_string(),
909 Type::Long => "long".to_string(),
910 Type::List(inner) => format!("list[{}]", self.type_to_string(inner)),
911 }
912 }
913
914 fn reference_to_string(&self, reference: &Reference) -> String {
915 reference.full_path()
916 }
917
918 fn reasoning_action_target_to_string(&self, target: &ReasoningActionTarget) -> String {
919 match target {
920 ReasoningActionTarget::Action(r) => self.reference_to_string(r),
921 ReasoningActionTarget::TransitionTo(r) => {
922 format!("@utils.transition to {}", self.reference_to_string(r))
923 }
924 ReasoningActionTarget::Escalate => "@utils.escalate".to_string(),
925 ReasoningActionTarget::SetVariables => "@utils.setVariables".to_string(),
926 ReasoningActionTarget::TopicDelegate(r) => self.reference_to_string(r),
927 }
928 }
929
930 fn expr_to_string(&self, expr: &Expr) -> String {
931 match expr {
932 Expr::Reference(r) => self.reference_to_string(r),
933 Expr::String(s) => format!("\"{}\"", escape_string(s)),
934 Expr::Number(n) => {
935 if n.fract() == 0.0 && n.is_finite() {
937 format!("{:.0}", n)
938 } else {
939 format!("{}", n)
940 }
941 }
942 Expr::Bool(b) => {
943 if *b {
944 "True".to_string()
945 } else {
946 "False".to_string()
947 }
948 }
949 Expr::None => "None".to_string(),
950 Expr::SlotFill => "...".to_string(),
951 Expr::List(items) => {
952 let items_str: Vec<_> =
953 items.iter().map(|i| self.expr_to_string(&i.node)).collect();
954 format!("[{}]", items_str.join(", "))
955 }
956 Expr::Object(map) => {
957 let pairs: Vec<_> = map
958 .iter()
959 .map(|(k, v)| format!("{}: {}", k, self.expr_to_string(&v.node)))
960 .collect();
961 format!("{{{}}}", pairs.join(", "))
962 }
963 Expr::BinOp { left, op, right } => {
964 format!(
965 "{} {} {}",
966 self.expr_to_string(&left.node),
967 self.binop_to_string(op),
968 self.expr_to_string(&right.node)
969 )
970 }
971 Expr::UnaryOp { op, operand } => {
972 format!("{} {}", self.unaryop_to_string(op), self.expr_to_string(&operand.node))
973 }
974 Expr::Ternary {
975 condition,
976 then_expr,
977 else_expr,
978 } => {
979 format!(
980 "{} if {} else {}",
981 self.expr_to_string(&then_expr.node),
982 self.expr_to_string(&condition.node),
983 self.expr_to_string(&else_expr.node)
984 )
985 }
986 Expr::Property { object, field } => {
987 format!("{}.{}", self.expr_to_string(&object.node), field.node)
988 }
989 Expr::Index { object, index } => {
990 format!(
991 "{}[{}]",
992 self.expr_to_string(&object.node),
993 self.expr_to_string(&index.node)
994 )
995 }
996 }
997 }
998
999 fn binop_to_string(&self, op: &BinOp) -> &'static str {
1000 match op {
1001 BinOp::Eq => "==",
1002 BinOp::Ne => "!=",
1003 BinOp::Lt => "<",
1004 BinOp::Gt => ">",
1005 BinOp::Le => "<=",
1006 BinOp::Ge => ">=",
1007 BinOp::Is => "is",
1008 BinOp::IsNot => "is not",
1009 BinOp::And => "and",
1010 BinOp::Or => "or",
1011 BinOp::Add => "+",
1012 BinOp::Sub => "-",
1013 }
1014 }
1015
1016 fn unaryop_to_string(&self, op: &UnaryOp) -> &'static str {
1017 match op {
1018 UnaryOp::Not => "not",
1019 UnaryOp::Neg => "-",
1020 }
1021 }
1022}
1023
1024fn escape_string(s: &str) -> String {
1026 s.replace('\\', "\\\\")
1027 .replace('"', "\\\"")
1028 .replace('\n', "\\n")
1029 .replace('\r', "\\r")
1030 .replace('\t', "\\t")
1031}
1032
1033#[cfg(test)]
1034mod tests {
1035 use super::*;
1036
1037 #[test]
1038 fn test_serialize_minimal_config() {
1039 let mut agent = AgentFile::new();
1040 agent.config = Some(Spanned::new(
1041 ConfigBlock {
1042 agent_name: Spanned::new("TestAgent".to_string(), 0..10),
1043 agent_label: None,
1044 description: None,
1045 agent_type: None,
1046 default_agent_user: None,
1047 },
1048 0..10,
1049 ));
1050
1051 let output = serialize(&agent);
1052 assert!(output.contains("config:"));
1053 assert!(output.contains("agent_name: \"TestAgent\""));
1054 }
1055
1056 #[test]
1057 fn test_serialize_variable() {
1058 let mut agent = AgentFile::new();
1059 agent.variables = Some(Spanned::new(
1060 VariablesBlock {
1061 variables: vec![Spanned::new(
1062 VariableDecl {
1063 name: Spanned::new("test_var".to_string(), 0..8),
1064 kind: VariableKind::Mutable,
1065 ty: Spanned::new(Type::String, 0..6),
1066 default: Some(Spanned::new(Expr::String("default".to_string()), 0..7)),
1067 description: Some(Spanned::new("Test variable".to_string(), 0..13)),
1068 source: None,
1069 },
1070 0..50,
1071 )],
1072 },
1073 0..50,
1074 ));
1075
1076 let output = serialize(&agent);
1077 assert!(output.contains("variables:"));
1078 assert!(output.contains("test_var: mutable string"));
1079 assert!(output.contains("description: \"Test variable\""));
1080 }
1081
1082 #[test]
1083 fn test_serialize_topic() {
1084 let mut agent = AgentFile::new();
1085 agent.topics = vec![Spanned::new(
1086 TopicBlock {
1087 name: Spanned::new("main".to_string(), 0..4),
1088 description: Some(Spanned::new("Main topic".to_string(), 0..10)),
1089 system: None,
1090 actions: None,
1091 before_reasoning: None,
1092 reasoning: None,
1093 after_reasoning: None,
1094 },
1095 0..50,
1096 )];
1097
1098 let output = serialize(&agent);
1099 assert!(output.contains("topic main:"));
1100 assert!(output.contains("description: \"Main topic\""));
1101 }
1102
1103 #[test]
1104 fn test_escape_string() {
1105 assert_eq!(escape_string("hello"), "hello");
1106 assert_eq!(escape_string("hello\"world"), "hello\\\"world");
1107 assert_eq!(escape_string("line1\nline2"), "line1\\nline2");
1108 assert_eq!(escape_string("tab\there"), "tab\\there");
1109 }
1110
1111 #[test]
1112 fn test_type_to_string() {
1113 let w = Writer::new();
1114 assert_eq!(w.type_to_string(&Type::String), "string");
1115 assert_eq!(w.type_to_string(&Type::Number), "number");
1116 assert_eq!(w.type_to_string(&Type::Boolean), "boolean");
1117 assert_eq!(w.type_to_string(&Type::List(Box::new(Type::String))), "list[string]");
1118 }
1119
1120 #[test]
1121 fn test_expr_to_string() {
1122 let w = Writer::new();
1123 assert_eq!(w.expr_to_string(&Expr::String("test".to_string())), "\"test\"");
1124 assert_eq!(w.expr_to_string(&Expr::Number(42.0)), "42");
1125 assert_eq!(w.expr_to_string(&Expr::Number(3.15)), "3.15");
1126 assert_eq!(w.expr_to_string(&Expr::Bool(true)), "True");
1127 assert_eq!(w.expr_to_string(&Expr::Bool(false)), "False");
1128 assert_eq!(w.expr_to_string(&Expr::None), "None");
1129 }
1130}