@@ -529,3 +529,132 @@ let to_markdown_string t =
529529 render_section " 🔴 Removed resources" removes;
530530 render_section " ⚪ Existing resources" existing;
531531 ])
532+
533+ let to_atlantis_comment_string t =
534+ let fmt v = Printf. sprintf " $%.2f" (CCFloat. abs v) in
535+ let fmt_with_sign v = Printf. sprintf " %s$%.2f" (if v > = 0.0 then " " else " -" ) (CCFloat. abs v) in
536+
537+ (* Initial box header with emojis *)
538+ let lines =
539+ [
540+ " \n " ;
541+ (* Add an empty line for spacing *)
542+ " ╔══════════════════════════════════════════════════════════╗\n " ;
543+ " ║ 💸 OpenInfraQuote Monthly Cost Estimate ║\n " ;
544+ " ╚══════════════════════════════════════════════════════════╝\n " ;
545+ ]
546+ in
547+
548+ (* Price difference section *)
549+ let delta_min = t.price_diff.Oiq_range. min in
550+ let delta_max = t.price_diff.Oiq_range. max in
551+ let lines =
552+ if delta_min = 0.0 && delta_max = 0.0 then lines @ [ " \n " ; " No change in monthly cost\n " ]
553+ else if delta_max < 0.0 then
554+ lines
555+ @ [
556+ " \n " ;
557+ Printf. sprintf
558+ " Decrease: %s → %s/month\n "
559+ (fmt (CCFloat. abs delta_min))
560+ (fmt (CCFloat. abs delta_max));
561+ ]
562+ else
563+ lines @ [ " \n " ; Printf. sprintf " Increase: %s → %s/month\n " (fmt delta_min) (fmt delta_max) ]
564+ in
565+
566+ (* Before/After summary *)
567+ let lines =
568+ lines
569+ @ [
570+ " \n " ;
571+ Printf. sprintf
572+ " Before: %s - %s\n "
573+ (fmt_with_sign t.prev_price.Oiq_range. min)
574+ (fmt_with_sign t.prev_price.Oiq_range. max);
575+ Printf. sprintf
576+ " After: %s - %s\n "
577+ (fmt_with_sign t.price.Oiq_range. min)
578+ (fmt_with_sign t.price.Oiq_range. max);
579+ ]
580+ in
581+
582+ (* Group resources by change type *)
583+ let adds, removes, existing =
584+ CCList. fold_left
585+ (fun (a , r , e ) resource ->
586+ match resource.Resource. change with
587+ | `Add -> (resource :: a, r, e)
588+ | `Remove -> (a, resource :: r, e)
589+ | `Noop -> (a, r, resource :: e))
590+ ([] , [] , [] )
591+ t.resources
592+ in
593+
594+ (* Add resource counts *)
595+ let lines =
596+ lines
597+ @ [
598+ " \n " ;
599+ Printf. sprintf " %-2s %-10s %d\n " " 🟢" " Added:" (CCList. length adds);
600+ Printf. sprintf " %-2s %-10s %d\n " " 🔴" " Removed:" (CCList. length removes);
601+ Printf. sprintf " %-2s %-10s %d\n " " ⚪" " Existing:" (CCList. length existing);
602+ ]
603+ in
604+
605+ (* Helper to render a section of resources *)
606+ let render_section title resources =
607+ if CCList. is_empty resources then " "
608+ else
609+ let header = Printf. sprintf " \n %s:\n %s\n " title (String. make (String. length title) '-' ) in
610+ let table_header =
611+ Printf. sprintf " %-40s %-20s %12s %12s\n " " Resource" " Type" " Before" " After"
612+ in
613+ let table_divider =
614+ Printf. sprintf
615+ " %-40s %-20s %12s %12s\n "
616+ (String. make 40 '-' )
617+ (String. make 20 '-' )
618+ (String. make 12 '-' )
619+ (String. make 12 '-' )
620+ in
621+ let rows =
622+ CCString. concat
623+ " "
624+ (CCList. map
625+ (fun r ->
626+ let name =
627+ if
628+ not
629+ (CCString. equal r.Resource. address (r.Resource. type_ ^ " ." ^ r.Resource. name))
630+ then
631+ CCString. concat " ." @@ CCList. tl @@ CCString. split_on_char '.' r.Resource. address
632+ else r.Resource. name
633+ in
634+ let typ = r.Resource. type_ in
635+ let est_min = r.Resource. price.Oiq_range. min in
636+ let est_max = r.Resource. price.Oiq_range. max in
637+ (* Use spaces around hyphens and preserve signs for removed resources *)
638+ let before_range =
639+ Printf. sprintf
640+ " %s - %s"
641+ (fmt_with_sign t.prev_price.Oiq_range. min)
642+ (fmt_with_sign t.prev_price.Oiq_range. max)
643+ in
644+ let after_range =
645+ Printf. sprintf " %s - %s" (fmt_with_sign est_min) (fmt_with_sign est_max)
646+ in
647+ Printf. sprintf " %-40s %-20s %12s %12s\n " name typ before_range after_range)
648+ resources)
649+ in
650+ header ^ table_header ^ table_divider ^ rows
651+ in
652+
653+ CCString. concat
654+ " "
655+ (lines
656+ @ [
657+ render_section " Added resources" adds;
658+ render_section " Removed resources" removes;
659+ render_section " Existing resources" existing;
660+ ])
0 commit comments