state.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // Package renderer provides rendering functionality for state diagrams
  2. package renderer
  3. import (
  4. "fmt"
  5. "strings"
  6. "mermaid-go/pkg/ast"
  7. )
  8. // StateRenderer renders state diagrams back to mermaid syntax
  9. type StateRenderer struct{}
  10. // NewStateRenderer creates a new state renderer
  11. func NewStateRenderer() *StateRenderer {
  12. return &StateRenderer{}
  13. }
  14. // Render renders a state diagram to mermaid syntax
  15. func (r *StateRenderer) Render(diagram *ast.StateDiagram) (string, error) {
  16. var builder strings.Builder
  17. // Start with diagram declaration
  18. builder.WriteString("stateDiagram-v2\n")
  19. // Add title if present
  20. if diagram.Title != nil {
  21. builder.WriteString(fmt.Sprintf(" title %s\n", *diagram.Title))
  22. }
  23. // Add direction if present
  24. if diagram.Direction != "" {
  25. builder.WriteString(fmt.Sprintf(" direction %s\n", diagram.Direction))
  26. }
  27. // Render states
  28. for _, state := range diagram.States {
  29. builder.WriteString(" state ")
  30. builder.WriteString(state.ID)
  31. // Add alias if different from ID
  32. if state.Label != state.ID {
  33. builder.WriteString(" as ")
  34. if strings.Contains(state.Label, " ") {
  35. builder.WriteString(fmt.Sprintf("\"%s\"", state.Label))
  36. } else {
  37. builder.WriteString(state.Label)
  38. }
  39. }
  40. // Add description or special type
  41. if state.Description != nil {
  42. builder.WriteString(" : ")
  43. builder.WriteString(*state.Description)
  44. } else {
  45. switch state.Type {
  46. case ast.StateTypeFork:
  47. builder.WriteString(" : <<fork>>")
  48. case ast.StateTypeJoin:
  49. builder.WriteString(" : <<join>>")
  50. case ast.StateTypeChoice:
  51. builder.WriteString(" : <<choice>>")
  52. case ast.StateTypeHistory:
  53. builder.WriteString(" : <<history>>")
  54. case ast.StateTypeDeepHistory:
  55. builder.WriteString(" : <<deepHistory>>")
  56. }
  57. }
  58. builder.WriteString("\n")
  59. // Render composite state body if it has sub-states
  60. if len(state.SubStates) > 0 {
  61. builder.WriteString(" state ")
  62. builder.WriteString(state.ID)
  63. builder.WriteString(" {\n")
  64. for _, subState := range state.SubStates {
  65. builder.WriteString(" state ")
  66. builder.WriteString(subState.ID)
  67. if subState.Label != subState.ID {
  68. builder.WriteString(" as ")
  69. builder.WriteString(subState.Label)
  70. }
  71. if subState.Description != nil {
  72. builder.WriteString(" : ")
  73. builder.WriteString(*subState.Description)
  74. }
  75. builder.WriteString("\n")
  76. }
  77. builder.WriteString(" }\n")
  78. }
  79. // Render note if present
  80. if state.Note != nil {
  81. builder.WriteString(" note ")
  82. builder.WriteString(string(state.Note.Position))
  83. builder.WriteString(" ")
  84. builder.WriteString(state.ID)
  85. builder.WriteString(" : ")
  86. builder.WriteString(state.Note.Text)
  87. builder.WriteString("\n")
  88. }
  89. // Render state actions
  90. if state.EntryAction != nil {
  91. builder.WriteString(" ")
  92. builder.WriteString(state.ID)
  93. builder.WriteString(" : entry ")
  94. builder.WriteString(*state.EntryAction)
  95. builder.WriteString("\n")
  96. }
  97. if state.ExitAction != nil {
  98. builder.WriteString(" ")
  99. builder.WriteString(state.ID)
  100. builder.WriteString(" : exit ")
  101. builder.WriteString(*state.ExitAction)
  102. builder.WriteString("\n")
  103. }
  104. if state.DoAction != nil {
  105. builder.WriteString(" ")
  106. builder.WriteString(state.ID)
  107. builder.WriteString(" : do ")
  108. builder.WriteString(*state.DoAction)
  109. builder.WriteString("\n")
  110. }
  111. }
  112. // Render transitions
  113. for _, transition := range diagram.Transitions {
  114. builder.WriteString(" ")
  115. builder.WriteString(transition.From)
  116. builder.WriteString(" --> ")
  117. builder.WriteString(transition.To)
  118. // Add transition decorations: label, [guard], /action
  119. var decorations []string
  120. if transition.Label != nil {
  121. decorations = append(decorations, *transition.Label)
  122. }
  123. if transition.Condition != nil {
  124. decorations = append(decorations, fmt.Sprintf("[%s]", *transition.Condition))
  125. }
  126. if transition.Action != nil {
  127. decorations = append(decorations, fmt.Sprintf("/%s", *transition.Action))
  128. }
  129. if len(decorations) > 0 {
  130. builder.WriteString(" : ")
  131. builder.WriteString(strings.Join(decorations, " "))
  132. }
  133. builder.WriteString("\n")
  134. }
  135. return builder.String(), nil
  136. }