// Package renderer provides rendering functionality for sequence diagrams package renderer import ( "fmt" "strings" "mermaid-go/pkg/ast" ) // SequenceRenderer renders sequence diagrams back to mermaid syntax type SequenceRenderer struct{} // NewSequenceRenderer creates a new sequence renderer func NewSequenceRenderer() *SequenceRenderer { return &SequenceRenderer{} } // Render renders a sequence diagram to mermaid syntax func (r *SequenceRenderer) Render(diagram *ast.SequenceDiagram) (string, error) { var builder strings.Builder // Start with diagram declaration builder.WriteString("sequenceDiagram\n") // Add title if present if diagram.Title != nil { builder.WriteString(fmt.Sprintf(" title %s\n", *diagram.Title)) } // Render participants for _, participant := range diagram.Participants { builder.WriteString(" ") builder.WriteString(string(participant.Type)) builder.WriteString(" ") builder.WriteString(participant.ID) if participant.Name != participant.ID { builder.WriteString(" as ") if strings.Contains(participant.Name, " ") { builder.WriteString(fmt.Sprintf("\"%s\"", participant.Name)) } else { builder.WriteString(participant.Name) } } builder.WriteString("\n") } // Render boxes for _, box := range diagram.Boxes { builder.WriteString(" box ") if strings.Contains(box.Name, " ") { builder.WriteString(fmt.Sprintf("\"%s\"", box.Name)) } else { builder.WriteString(box.Name) } if box.Color != nil { builder.WriteString(" ") builder.WriteString(*box.Color) } for _, participant := range box.Participants { builder.WriteString(" ") builder.WriteString(participant) } builder.WriteString("\n end\n") } // Render activations for _, activation := range diagram.Activations { builder.WriteString(" ") builder.WriteString(string(activation.Type)) builder.WriteString(" ") builder.WriteString(activation.Actor) builder.WriteString("\n") } // Render messages for _, message := range diagram.Messages { builder.WriteString(" ") builder.WriteString(message.From) builder.WriteString(" ") builder.WriteString(string(message.Type)) builder.WriteString(" ") builder.WriteString(message.To) if message.Message != "" { builder.WriteString(" : ") builder.WriteString(message.Message) } builder.WriteString("\n") } // Render notes for _, note := range diagram.Notes { builder.WriteString(" Note ") builder.WriteString(string(note.Placement)) builder.WriteString(" ") builder.WriteString(note.Actor) builder.WriteString(" : ") builder.WriteString(note.Message) builder.WriteString("\n") } // Render loops for _, loop := range diagram.Loops { builder.WriteString(" loop ") builder.WriteString(loop.Label) builder.WriteString("\n") for _, message := range loop.Messages { builder.WriteString(" ") builder.WriteString(message.From) builder.WriteString(" ") builder.WriteString(string(message.Type)) builder.WriteString(" ") builder.WriteString(message.To) if message.Message != "" { builder.WriteString(" : ") builder.WriteString(message.Message) } builder.WriteString("\n") } builder.WriteString(" end\n") } // Render alts for _, alt := range diagram.Alts { builder.WriteString(" alt ") builder.WriteString(alt.Label) builder.WriteString("\n") for _, message := range alt.IfMessages { builder.WriteString(" ") builder.WriteString(message.From) builder.WriteString(" ") builder.WriteString(string(message.Type)) builder.WriteString(" ") builder.WriteString(message.To) if message.Message != "" { builder.WriteString(" : ") builder.WriteString(message.Message) } builder.WriteString("\n") } if len(alt.ElseMessages) > 0 { builder.WriteString(" else\n") for _, message := range alt.ElseMessages { builder.WriteString(" ") builder.WriteString(message.From) builder.WriteString(" ") builder.WriteString(string(message.Type)) builder.WriteString(" ") builder.WriteString(message.To) if message.Message != "" { builder.WriteString(" : ") builder.WriteString(message.Message) } builder.WriteString("\n") } } builder.WriteString(" end\n") } // Render opts for _, opt := range diagram.Opts { builder.WriteString(" opt ") builder.WriteString(opt.Label) builder.WriteString("\n") for _, message := range opt.Messages { builder.WriteString(" ") builder.WriteString(message.From) builder.WriteString(" ") builder.WriteString(string(message.Type)) builder.WriteString(" ") builder.WriteString(message.To) if message.Message != "" { builder.WriteString(" : ") builder.WriteString(message.Message) } builder.WriteString("\n") } builder.WriteString(" end\n") } // Render pars for _, par := range diagram.Pars { builder.WriteString(" par") for i, section := range par.Sections { if i == 0 { // First section starts with par builder.WriteString("\n") } else { // Subsequent sections start with and builder.WriteString(" and") if section.Label != nil { builder.WriteString(" ") builder.WriteString(*section.Label) } builder.WriteString("\n") } for _, message := range section.Messages { builder.WriteString(" ") builder.WriteString(message.From) builder.WriteString(" ") builder.WriteString(string(message.Type)) builder.WriteString(" ") builder.WriteString(message.To) if message.Message != "" { builder.WriteString(" : ") builder.WriteString(message.Message) } builder.WriteString("\n") } } builder.WriteString(" end\n") } // Render rects for _, rect := range diagram.Rects { builder.WriteString(" rect") if rect.Color != nil { builder.WriteString(" ") builder.WriteString(*rect.Color) } builder.WriteString("\n") for _, message := range rect.Messages { builder.WriteString(" ") builder.WriteString(message.From) builder.WriteString(" ") builder.WriteString(string(message.Type)) builder.WriteString(" ") builder.WriteString(message.To) if message.Message != "" { builder.WriteString(" : ") builder.WriteString(message.Message) } builder.WriteString("\n") } builder.WriteString(" end\n") } // Render criticals for _, critical := range diagram.Criticals { builder.WriteString(" critical") if critical.Label != "" { builder.WriteString(" ") builder.WriteString(critical.Label) } builder.WriteString("\n") // Render critical messages for _, message := range critical.Messages { builder.WriteString(" ") builder.WriteString(message.From) builder.WriteString(" ") builder.WriteString(string(message.Type)) builder.WriteString(" ") builder.WriteString(message.To) if message.Message != "" { builder.WriteString(" : ") builder.WriteString(message.Message) } builder.WriteString("\n") } // Render options for _, option := range critical.Options { builder.WriteString(" option") if option.Label != "" { builder.WriteString(" ") builder.WriteString(option.Label) } builder.WriteString("\n") for _, message := range option.Messages { builder.WriteString(" ") builder.WriteString(message.From) builder.WriteString(" ") builder.WriteString(string(message.Type)) builder.WriteString(" ") builder.WriteString(message.To) if message.Message != "" { builder.WriteString(" : ") builder.WriteString(message.Message) } builder.WriteString("\n") } } builder.WriteString(" end\n") } // Render breaks for _, breakBlock := range diagram.Breaks { builder.WriteString(" break") if breakBlock.Label != "" { builder.WriteString(" ") builder.WriteString(breakBlock.Label) } builder.WriteString("\n") for _, message := range breakBlock.Messages { builder.WriteString(" ") builder.WriteString(message.From) builder.WriteString(" ") builder.WriteString(string(message.Type)) builder.WriteString(" ") builder.WriteString(message.To) if message.Message != "" { builder.WriteString(" : ") builder.WriteString(message.Message) } builder.WriteString("\n") } builder.WriteString(" end\n") } return builder.String(), nil }