er.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. // Package renderer provides ER diagram rendering
  2. package renderer
  3. import (
  4. "fmt"
  5. "strings"
  6. "mermaid-go/pkg/ast"
  7. )
  8. // ERRenderer renders ER diagrams back to mermaid syntax
  9. type ERRenderer struct{}
  10. // NewERRenderer creates a new ER renderer
  11. func NewERRenderer() *ERRenderer {
  12. return &ERRenderer{}
  13. }
  14. // Render renders an ER diagram to mermaid syntax
  15. func (r *ERRenderer) Render(diagram *ast.ERDiagram) (string, error) {
  16. var builder strings.Builder
  17. builder.WriteString("erDiagram\n")
  18. // Render title if present
  19. if diagram.Title != nil {
  20. builder.WriteString(fmt.Sprintf(" title %s\n", *diagram.Title))
  21. }
  22. // Render entities with attributes
  23. for _, entity := range diagram.Entities {
  24. if len(entity.Attributes) > 0 {
  25. builder.WriteString(fmt.Sprintf(" %s {\n", r.quoteIfNeeded(entity.Name)))
  26. for _, attr := range entity.Attributes {
  27. builder.WriteString(" ")
  28. builder.WriteString(attr.Type)
  29. builder.WriteString(" ")
  30. builder.WriteString(attr.Name)
  31. // Add key constraint if present
  32. if attr.Key != nil {
  33. switch *attr.Key {
  34. case ast.ERKeyPrimary:
  35. builder.WriteString(" PK")
  36. case ast.ERKeyForeign:
  37. builder.WriteString(" FK")
  38. case ast.ERKeyUnique:
  39. builder.WriteString(" UK")
  40. }
  41. }
  42. // Add comment if present
  43. if attr.Comment != nil {
  44. builder.WriteString(fmt.Sprintf(" \"%s\"", *attr.Comment))
  45. }
  46. builder.WriteString("\n")
  47. }
  48. builder.WriteString(" }\n")
  49. }
  50. }
  51. // Render relationships
  52. for _, relation := range diagram.Relations {
  53. builder.WriteString(" ")
  54. builder.WriteString(r.quoteIfNeeded(relation.From))
  55. builder.WriteString(" ")
  56. builder.WriteString(r.renderRelationType(relation.Type))
  57. builder.WriteString(" ")
  58. builder.WriteString(r.quoteIfNeeded(relation.To))
  59. // Add label if present
  60. if relation.Label != nil {
  61. builder.WriteString(" : ")
  62. builder.WriteString(*relation.Label)
  63. }
  64. builder.WriteString("\n")
  65. }
  66. return builder.String(), nil
  67. }
  68. // renderRelationType converts relation type to mermaid syntax
  69. func (r *ERRenderer) renderRelationType(relType ast.ERRelationType) string {
  70. switch relType {
  71. case ast.ERRelationOneToOne:
  72. return "||--||"
  73. case ast.ERRelationOneToMany:
  74. return "||--o{"
  75. case ast.ERRelationManyToOne:
  76. return "}o--||"
  77. case ast.ERRelationManyToMany:
  78. return "}o--o{"
  79. default:
  80. return "||--o{" // Default to one-to-many
  81. }
  82. }
  83. // quoteIfNeeded adds quotes around entity names if they contain spaces
  84. func (r *ERRenderer) quoteIfNeeded(name string) string {
  85. if strings.Contains(name, " ") {
  86. return fmt.Sprintf("\"%s\"", name)
  87. }
  88. return name
  89. }