class.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. // Package renderer provides rendering functionality for class diagrams
  2. package renderer
  3. import (
  4. "fmt"
  5. "strings"
  6. "mermaid-go/pkg/ast"
  7. )
  8. // ClassRenderer renders class diagrams back to mermaid syntax
  9. type ClassRenderer struct{}
  10. // NewClassRenderer creates a new class renderer
  11. func NewClassRenderer() *ClassRenderer {
  12. return &ClassRenderer{}
  13. }
  14. // Render renders a class diagram to mermaid syntax
  15. func (r *ClassRenderer) Render(diagram *ast.ClassDiagram) (string, error) {
  16. var builder strings.Builder
  17. // Start with diagram declaration
  18. builder.WriteString("classDiagram\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 classes
  28. for _, class := range diagram.Classes {
  29. builder.WriteString(" class ")
  30. builder.WriteString(class.ID)
  31. // Render class body if it has members or methods
  32. if len(class.Members) > 0 || len(class.Methods) > 0 || len(class.Annotations) > 0 {
  33. builder.WriteString(" {\n")
  34. // Render annotations first
  35. for _, annotation := range class.Annotations {
  36. builder.WriteString(" <<")
  37. builder.WriteString(annotation)
  38. builder.WriteString(">>\n")
  39. }
  40. // Render members
  41. for _, member := range class.Members {
  42. builder.WriteString(" ")
  43. builder.WriteString(string(member.Visibility))
  44. builder.WriteString(member.Name)
  45. if member.Type != "" {
  46. builder.WriteString(" ")
  47. // Handle generics in member type
  48. if len(member.Generics) > 0 {
  49. builder.WriteString(r.renderGenericType(member.Type, member.Generics))
  50. } else {
  51. builder.WriteString(member.Type)
  52. }
  53. }
  54. if member.Classifier != nil {
  55. builder.WriteString(" ")
  56. builder.WriteString(*member.Classifier)
  57. }
  58. builder.WriteString("\n")
  59. }
  60. // Render methods
  61. for _, method := range class.Methods {
  62. builder.WriteString(" ")
  63. builder.WriteString(string(method.Visibility))
  64. builder.WriteString(method.Name)
  65. builder.WriteString("(")
  66. for i, param := range method.Parameters {
  67. if i > 0 {
  68. builder.WriteString(", ")
  69. }
  70. builder.WriteString(param)
  71. }
  72. builder.WriteString(")")
  73. if method.Type != "" {
  74. builder.WriteString(" ")
  75. // Handle generics in return type
  76. if len(method.Generics) > 0 {
  77. builder.WriteString(r.renderGenericType(method.Type, method.Generics))
  78. } else {
  79. builder.WriteString(method.Type)
  80. }
  81. }
  82. if method.Classifier != nil {
  83. builder.WriteString(" ")
  84. builder.WriteString(*method.Classifier)
  85. }
  86. builder.WriteString("\n")
  87. }
  88. builder.WriteString(" }")
  89. }
  90. builder.WriteString("\n")
  91. // Render links
  92. if class.Link != nil {
  93. builder.WriteString(" link ")
  94. builder.WriteString(class.ID)
  95. builder.WriteString(" \"")
  96. builder.WriteString(*class.Link)
  97. builder.WriteString("\"")
  98. if class.LinkTarget != nil {
  99. builder.WriteString(" ")
  100. builder.WriteString(*class.LinkTarget)
  101. }
  102. builder.WriteString("\n")
  103. }
  104. // Render tooltip
  105. if class.Tooltip != nil {
  106. builder.WriteString(" ")
  107. builder.WriteString(class.ID)
  108. builder.WriteString(" : ")
  109. builder.WriteString(*class.Tooltip)
  110. builder.WriteString("\n")
  111. }
  112. }
  113. // Render relations
  114. for _, relation := range diagram.Relations {
  115. builder.WriteString(" ")
  116. builder.WriteString(relation.From)
  117. builder.WriteString(" ")
  118. // Render relation type
  119. switch relation.Type {
  120. case ast.RelationInheritance:
  121. builder.WriteString("<|--")
  122. case ast.RelationComposition:
  123. builder.WriteString("*--")
  124. case ast.RelationAggregation:
  125. builder.WriteString("o--")
  126. case ast.RelationAssociation:
  127. builder.WriteString("-->")
  128. case ast.RelationRealization:
  129. builder.WriteString("..|>")
  130. case ast.RelationDependency:
  131. builder.WriteString("..>")
  132. default:
  133. builder.WriteString("-->") // Default to association
  134. }
  135. builder.WriteString(" ")
  136. builder.WriteString(relation.To)
  137. // Add label if present
  138. if relation.Label != nil {
  139. builder.WriteString(" : ")
  140. builder.WriteString(*relation.Label)
  141. }
  142. // Add cardinality if present
  143. if relation.Cardinality != nil {
  144. if relation.Cardinality.From != "" {
  145. builder.WriteString(" \"")
  146. builder.WriteString(relation.Cardinality.From)
  147. builder.WriteString("\"")
  148. }
  149. if relation.Cardinality.To != "" {
  150. builder.WriteString(" \"")
  151. builder.WriteString(relation.Cardinality.To)
  152. builder.WriteString("\"")
  153. }
  154. }
  155. builder.WriteString("\n")
  156. }
  157. // Render class definitions
  158. for _, classDef := range diagram.ClassDefs {
  159. builder.WriteString(" classDef ")
  160. builder.WriteString(classDef.ID)
  161. for _, style := range classDef.Styles {
  162. builder.WriteString(" ")
  163. builder.WriteString(style)
  164. }
  165. builder.WriteString("\n")
  166. }
  167. // Render notes
  168. for _, note := range diagram.Notes {
  169. if note.ForClass != nil {
  170. builder.WriteString(fmt.Sprintf(" note for %s \"%s\"\n", *note.ForClass, note.Text))
  171. } else {
  172. builder.WriteString(fmt.Sprintf(" note \"%s\"\n", note.Text))
  173. }
  174. }
  175. return builder.String(), nil
  176. }
  177. // renderGenericType renders a type with its generic parameters
  178. func (r *ClassRenderer) renderGenericType(baseType string, generics []*ast.Generic) string {
  179. if len(generics) == 0 {
  180. return baseType
  181. }
  182. result := "~" + baseType
  183. if len(generics) > 0 {
  184. result += "<"
  185. for i, generic := range generics {
  186. if i > 0 {
  187. result += ", "
  188. }
  189. if len(generic.Arguments) > 0 {
  190. result += r.renderGenericType(generic.Name, generic.Arguments)
  191. } else {
  192. result += generic.Name
  193. }
  194. }
  195. result += ">"
  196. }
  197. result += "~"
  198. return result
  199. }