package block
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Mermaid block syntax templates
const (
tplSpace = basediagram.Indentation + "space\n"
tplSpaceWidth = basediagram.Indentation + "space:%d\n"
tplBlockStart = basediagram.Indentation + "block:%s:%d\n"
tplBlockSimple = basediagram.Indentation + "block:%s\n"
tplColumns = basediagram.Indentation + "columns %d\n"
tplChildBlock = basediagram.Indentation + "%s%s\n"
tplChildSimple = basediagram.Indentation + "%s\n"
tplBlockEnd = basediagram.Indentation + "end\n"
tplBlockWidth = basediagram.Indentation + "%s%s:%d\n"
tplBlockNoWidth = basediagram.Indentation + "%s%s\n"
tplBlockID = basediagram.Indentation + "%s\n"
tplStyle = basediagram.Indentation + "style %s %s\n"
)
// BlockShape defines the visual appearance of a block
type blockShape string
// Available block shapes for use in diagrams
const (
BlockShapeDefault blockShape = `["%s"]`
BlockShapeRoundEdges blockShape = `("%s")`
BlockShapeStadium blockShape = `(["%s"])`
BlockShapeSubroutine blockShape = `[["%s"]]`
BlockShapeCylindrical blockShape = `[("%s")]`
BlockShapeCircle blockShape = `(("%s"))`
BlockShapeAsymmetric blockShape = `>"%s"]`
BlockShapeRhombus blockShape = `{"%s"}`
BlockShapeHexagon blockShape = `{{"%s"}}`
BlockShapeParallelogram blockShape = `[/"%s"/]`
BlockShapeTrapezoid blockShape = `[/"%s"\]`
BlockShapeTrapezoidAlt blockShape = `[\"%s"/]`
BlockShapeDoubleCircle blockShape = `((("%s")))`
)
// Block represents a node in a block diagram
type Block struct {
ID string
Text string
Style string
Shape blockShape
Children []*Block
IsSpace bool
Width int
diagram *Diagram
isArrow bool
direction []BlockArrowDirection
columns int
}
// NewBlock creates a block with the given ID and text
func NewBlock(id, text string) *Block {
return &Block{
ID: id,
Text: text,
Shape: BlockShapeDefault,
Children: make([]*Block, 0),
Width: 1,
}
}
// SetWidth sets the number of columns this block spans
func (b *Block) SetWidth(width int) *Block {
b.Width = width
return b
}
// SetStyle sets CSS style properties for this block
func (b *Block) SetStyle(style string) *Block {
b.Style = style
return b
}
// SetShape sets the visual shape of this block
func (b *Block) SetShape(shape blockShape) *Block {
b.Shape = shape
return b
}
// AddBlock creates a nested block with the given text
func (b *Block) AddBlock(text string) *Block {
block := NewBlock(idGenerator.NextID(), text)
b.Children = append(b.Children, block)
return block
}
// SetArrow configures this block as an arrow with the given directions
func (b *Block) SetArrow(directions ...BlockArrowDirection) *Block {
b.isArrow = true
b.direction = directions
return b
}
// SetColumns sets the number of columns for this block's children
func (b *Block) SetColumns(count int) *Block {
b.columns = count
return b
}
// AddColumn increases the number of columns for this block's children by one
func (b *Block) AddColumn() *Block {
b.columns += 1
return b
}
// RemoveColumn decreases the number of columns for this block's children by one
func (b *Block) RemoveColumn() *Block {
b.columns -= 1
return b
}
// String returns the Mermaid syntax representation of this block
func (b *Block) String() string {
var sb strings.Builder
if b.IsSpace {
if b.Width > 0 {
sb.WriteString(fmt.Sprintf(tplSpaceWidth, b.Width))
} else {
sb.WriteString(tplSpace)
}
return sb.String()
}
if len(b.Children) > 0 {
// Parent block with children
if b.Width > 0 {
sb.WriteString(fmt.Sprintf(tplBlockStart, b.ID, b.Width))
} else {
sb.WriteString(fmt.Sprintf(tplBlockSimple, b.ID))
}
if b.columns > 0 {
sb.WriteString(fmt.Sprintf(tplColumns, b.columns))
}
for _, child := range b.Children {
if child.Text != "" {
if child.isArrow {
sb.WriteString(fmt.Sprintf(tplChildBlock, child.ID, BlockArrowShape(child.Text, child.direction...)))
} else {
sb.WriteString(fmt.Sprintf(tplChildBlock, child.ID, fmt.Sprintf(string(child.Shape), child.Text)))
}
} else {
sb.WriteString(fmt.Sprintf(tplChildSimple, child.ID))
}
}
sb.WriteString(tplBlockEnd)
} else {
if b.Text != "" {
if b.isArrow {
if b.Width > 1 {
sb.WriteString(fmt.Sprintf(tplBlockWidth, b.ID, BlockArrowShape(b.Text, b.direction...), b.Width))
} else {
sb.WriteString(fmt.Sprintf(tplBlockNoWidth, b.ID, BlockArrowShape(b.Text, b.direction...)))
}
} else {
if b.Width > 1 {
sb.WriteString(fmt.Sprintf(tplBlockWidth, b.ID, fmt.Sprintf(string(b.Shape), b.Text), b.Width))
} else {
sb.WriteString(fmt.Sprintf(tplBlockNoWidth, b.ID, fmt.Sprintf(string(b.Shape), b.Text)))
}
}
} else {
if b.Width > 1 {
sb.WriteString(fmt.Sprintf(tplBlockWidth, b.ID, "", b.Width))
} else {
sb.WriteString(fmt.Sprintf(tplBlockID, b.ID))
}
}
}
if b.Style != "" {
sb.WriteString(fmt.Sprintf(tplStyle, b.ID, b.Style))
}
return sb.String()
}
package block
import (
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
const (
baseBlockConfigurationProperties string = "block:\n"
blockPropertyPadding string = "padding"
)
// BlockConfigurationProperties holds block-specific configuration
type BlockConfigurationProperties struct {
basediagram.ConfigurationProperties
properties map[string]basediagram.DiagramProperty
}
func NewBlockConfigurationProperties() BlockConfigurationProperties {
return BlockConfigurationProperties{
ConfigurationProperties: basediagram.NewConfigurationProperties(),
properties: make(map[string]basediagram.DiagramProperty),
}
}
func (c *BlockConfigurationProperties) SetPadding(v int) *BlockConfigurationProperties {
c.properties[blockPropertyPadding] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: blockPropertyPadding,
Val: v,
},
}
return c
}
func (c BlockConfigurationProperties) String() string {
var sb strings.Builder
sb.WriteString(c.ConfigurationProperties.String())
if len(c.properties) > 0 {
sb.WriteString(baseBlockConfigurationProperties)
for _, prop := range c.properties {
sb.WriteString(prop.Format())
}
}
return sb.String()
}
// Package block provides functionality for creating Mermaid block diagrams
package block
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Global ID generator for the package
var idGenerator = utils.NewIDGenerator()
// Mermaid diagram syntax templates
const (
baseDiagramType = "block-beta\n"
tplDiagramCols = basediagram.Indentation + "columns %d\n"
)
// Diagram represents a Mermaid block diagram
type Diagram struct {
basediagram.BaseDiagram[BlockConfigurationProperties]
Blocks []*Block
Links []*Link
Columns int
}
// NewDiagram creates a new block diagram
func NewDiagram() *Diagram {
return &Diagram{
BaseDiagram: basediagram.NewBaseDiagram(NewBlockConfigurationProperties()),
Blocks: make([]*Block, 0),
Links: make([]*Link, 0),
Columns: 0,
}
}
// SetColumns sets the number of columns in the diagram
func (d *Diagram) SetColumns(count int) *Diagram {
d.Columns = count
return d
}
// AddColumn increases the number of columns by one
func (d *Diagram) AddColumn() *Diagram {
d.Columns += 1
return d
}
// RemoveColumn decreases the number of columns by one
func (d *Diagram) RemoveColumn() *Diagram {
d.Columns -= 1
return d
}
// AddBlock creates and adds a new block to the diagram
func (d *Diagram) AddBlock(text string) *Block {
block := NewBlock(idGenerator.NextID(), text)
block.diagram = d
d.Blocks = append(d.Blocks, block)
return block
}
// AddSpace adds a space block of one column width
func (d *Diagram) AddSpace() {
d.Blocks = append(d.Blocks, &Block{IsSpace: true})
}
// AddSpaceWithWidth adds a space block with specified width
func (d *Diagram) AddSpaceWithWidth(width int) {
d.Blocks = append(d.Blocks, &Block{IsSpace: true, Width: width})
}
// AddLink creates a link between two blocks
func (d *Diagram) AddLink(from, to *Block) *Link {
link := NewLink(from, to)
d.Links = append(d.Links, link)
return link
}
// String returns the Mermaid syntax representation of this diagram
func (d *Diagram) String() string {
var sb strings.Builder
sb.WriteString(baseDiagramType)
if d.Columns > 0 {
sb.WriteString(fmt.Sprintf(tplDiagramCols, d.Columns))
}
for _, block := range d.Blocks {
sb.WriteString(block.String())
}
for _, link := range d.Links {
sb.WriteString(link.String())
}
return d.BaseDiagram.String(sb.String())
}
// RenderToFile saves the diagram to a file at the specified path
func (d *Diagram) RenderToFile(path string) error {
return utils.RenderToFile(path, d.String())
}
package block
import (
"fmt"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Mermaid link syntax templates
const (
tplLinkWithText = basediagram.Indentation + "%s -- \"%s\" --> %s\n"
tplLink = basediagram.Indentation + "%s --> %s\n"
)
// Link represents a connection between two blocks
type Link struct {
From *Block
To *Block
Text string
}
// NewLink creates a link between two blocks
func NewLink(from, to *Block) *Link {
return &Link{
From: from,
To: to,
}
}
// SetText sets the text label for this link
func (l *Link) SetText(text string) *Link {
l.Text = text
return l
}
// String returns the Mermaid syntax representation of this link
func (l *Link) String() string {
if l.Text != "" {
return fmt.Sprintf(tplLinkWithText, l.From.ID, l.Text, l.To.ID)
}
return fmt.Sprintf(tplLink, l.From.ID, l.To.ID)
}
package block
import (
"fmt"
"strings"
)
// BlockArrowDirection specifies the direction of a block arrow
type BlockArrowDirection string
const (
baseBlockArrowShape = `<["%s"]>(%s)`
)
// Available arrow directions for block arrows
const (
BlockArrowDirectionRight BlockArrowDirection = "right"
BlockArrowDirectionLeft BlockArrowDirection = "left"
BlockArrowDirectionUp BlockArrowDirection = "up"
BlockArrowDirectionDown BlockArrowDirection = "down"
BlockArrowDirectionX BlockArrowDirection = "x"
BlockArrowDirectionY BlockArrowDirection = "y"
)
// BlockArrowShape formats a block arrow with given text and direction(s)
func BlockArrowShape(text string, directions ...BlockArrowDirection) string {
strs := make([]string, len(directions))
for i, d := range directions {
strs[i] = string(d)
}
dirStr := strings.Join(strs, ", ")
return fmt.Sprintf(baseBlockArrowShape, text, dirStr)
}
// Package class provides functionality for creating Mermaid class diagrams
package class
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
type classAnnotation string
// Available class annotations
const (
ClassAnnotationNone classAnnotation = ""
ClassAnnotationInterface classAnnotation = "<<Interface>>"
ClassAnnotationAbstract classAnnotation = "<<Abstract>>"
ClassAnnotationService classAnnotation = "<<Service>>"
ClassAnnotationEnumeration classAnnotation = "<<Enumeration>>"
)
// Mermaid class syntax templates
const (
baseClassStartString string = basediagram.Indentation + "class %s%s{\n"
baseClassEndString string = basediagram.Indentation + "}\n"
baseClassLabelString string = "[\"%s\"]"
baseClassAnnotationString string = basediagram.Indentation + "%s\n"
baseClassMemberString string = basediagram.Indentation + "%s\n"
)
// Class represents a class in a Mermaid class diagram
type Class struct {
Name string
Label string
Annotation classAnnotation
methods []*Method
fields []*Field
}
// NewClass creates a new Class with the given name
func NewClass(name string) (newClass *Class) {
newClass = &Class{
Name: name,
}
return
}
// SetLabel sets the class label
func (c *Class) SetLabel(label string) *Class {
c.Label = label
return c
}
// SetAnnotation sets the class annotation
func (c *Class) SetAnnotation(annotation classAnnotation) *Class {
c.Annotation = annotation
return c
}
// AddMethod creates and adds a new method
func (c *Class) AddMethod(name string) *Method {
method := NewMethod(name)
c.methods = append(c.methods, method)
return method
}
// AddField creates and adds a new field
func (c *Class) AddField(fieldName string, fieldType string) *Field {
field := NewField(fieldName, fieldType)
c.fields = append(c.fields, field)
return field
}
// String returns the Mermaid syntax representation of this class
func (c *Class) String(curIndentation string) string {
var sb strings.Builder
label := ""
if len(c.Label) > 0 {
label = fmt.Sprintf(string(baseClassLabelString), c.Label)
}
sb.WriteString(fmt.Sprintf(curIndentation, fmt.Sprintf(string(baseClassStartString), c.Name, label)))
if c.Annotation != ClassAnnotationNone {
sb.WriteString(fmt.Sprintf(curIndentation, fmt.Sprintf(string(baseClassAnnotationString), string(c.Annotation))))
}
for _, field := range c.fields {
sb.WriteString(fmt.Sprintf(curIndentation, fmt.Sprintf(string(baseClassMemberString), field.String())))
}
for _, method := range c.methods {
sb.WriteString(fmt.Sprintf(curIndentation, fmt.Sprintf(string(baseClassMemberString), method.String())))
}
sb.WriteString(fmt.Sprintf(curIndentation, baseClassEndString))
return sb.String()
}
package class
import (
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
const (
baseClassConfigurationProperties string = basediagram.Indentation + "class:\n"
classPropertyTitleTopMargin string = "titleTopMargin"
classPropertyArrowMarkerAbsolute string = "arrowMarkerAbsolute"
classPropertyDividerMargin string = "dividerMargin"
classPropertyPadding string = "padding"
classPropertyTextHeight string = "textHeight"
classPropertyDefaultRenderer string = "defaultRenderer"
classPropertyNodeSpacing string = "nodeSpacing"
classPropertyRankSpacing string = "rankSpacing"
classPropertyDiagramPadding string = "diagramPadding"
classPropertyHtmlLabels string = "htmlLabels"
classPropertyHideEmptyMembersBox string = "hideEmptyMembersBox"
)
// ClassConfigurationProperties holds class-specific configuration
type ClassConfigurationProperties struct {
basediagram.ConfigurationProperties
properties map[string]basediagram.DiagramProperty
}
func NewClassConfigurationProperties() ClassConfigurationProperties {
return ClassConfigurationProperties{
ConfigurationProperties: basediagram.NewConfigurationProperties(),
properties: make(map[string]basediagram.DiagramProperty),
}
}
// Setters for each property from the schema
func (c *ClassConfigurationProperties) SetTitleTopMargin(v int) *ClassConfigurationProperties {
c.properties[classPropertyTitleTopMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: classPropertyTitleTopMargin,
Val: v,
},
}
return c
}
func (c *ClassConfigurationProperties) SetArrowMarkerAbsolute(v bool) *ClassConfigurationProperties {
c.properties[classPropertyArrowMarkerAbsolute] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: classPropertyArrowMarkerAbsolute,
Val: v,
},
}
return c
}
func (c *ClassConfigurationProperties) SetDividerMargin(v int) *ClassConfigurationProperties {
c.properties[classPropertyDividerMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: classPropertyDividerMargin,
Val: v,
},
}
return c
}
func (c *ClassConfigurationProperties) SetPadding(v int) *ClassConfigurationProperties {
c.properties[classPropertyPadding] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: classPropertyPadding,
Val: v,
},
}
return c
}
func (c *ClassConfigurationProperties) SetTextHeight(v int) *ClassConfigurationProperties {
c.properties[classPropertyTextHeight] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: classPropertyTextHeight,
Val: v,
},
}
return c
}
func (c *ClassConfigurationProperties) SetDefaultRenderer(v string) *ClassConfigurationProperties {
c.properties[classPropertyDefaultRenderer] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: classPropertyDefaultRenderer,
Val: v,
},
}
return c
}
func (c *ClassConfigurationProperties) SetNodeSpacing(v int) *ClassConfigurationProperties {
c.properties[classPropertyNodeSpacing] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: classPropertyNodeSpacing,
Val: v,
},
}
return c
}
func (c *ClassConfigurationProperties) SetRankSpacing(v int) *ClassConfigurationProperties {
c.properties[classPropertyRankSpacing] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: classPropertyRankSpacing,
Val: v,
},
}
return c
}
func (c *ClassConfigurationProperties) SetDiagramPadding(v int) *ClassConfigurationProperties {
c.properties[classPropertyDiagramPadding] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: classPropertyDiagramPadding,
Val: v,
},
}
return c
}
func (c *ClassConfigurationProperties) SetHtmlLabels(v bool) *ClassConfigurationProperties {
c.properties[classPropertyHtmlLabels] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: classPropertyHtmlLabels,
Val: v,
},
}
return c
}
func (c *ClassConfigurationProperties) SetHideEmptyMembersBox(v bool) *ClassConfigurationProperties {
c.properties[classPropertyHideEmptyMembersBox] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: classPropertyHideEmptyMembersBox,
Val: v,
},
}
return c
}
func (c ClassConfigurationProperties) String() string {
var sb strings.Builder
sb.WriteString(c.ConfigurationProperties.String())
if len(c.properties) > 0 {
sb.WriteString(baseClassConfigurationProperties)
for _, prop := range c.properties {
sb.WriteString(prop.Format())
}
}
return sb.String()
}
// Package class provides functionality for creating Mermaid class diagrams
package class
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
type classDiagramDirection string
// List of possible Class Diagram directions.
// Reference: https://mermaid.js.org/syntax/classDiagram.html#setting-the-direction-of-the-diagram
const (
ClassDiagramDirectionTopToBottom classDiagramDirection = "TB"
ClassDiagramDirectionBottomUp classDiagramDirection = "BT"
ClassDiagramDirectionRightLeft classDiagramDirection = "RL"
ClassDiagramDirectionLeftRight classDiagramDirection = "LR"
)
const (
baseDiagramType string = "classDiagram\n"
baseClassDiagramDirectionString string = basediagram.Indentation + "direction %s\n"
)
// ClassDiagram represents a Mermaid class diagram with various diagram components
// such as classes, namespaces, relations, and notes.
type ClassDiagram struct {
basediagram.BaseDiagram[ClassConfigurationProperties]
Direction classDiagramDirection
namespaces []*Namespace
notes []*Note
classes []*Class
relations []*Relation
}
// SetDirection sets the diagram direction and returns the diagram for chaining
func (cd *ClassDiagram) SetDirection(direction classDiagramDirection) *ClassDiagram {
cd.Direction = direction
return cd
}
// NewClassDiagram creates and returns a new ClassDiagram with default settings.
// The default direction is set to top-to-bottom.
func NewClassDiagram() (newClassDiagram *ClassDiagram) {
newClassDiagram = &ClassDiagram{
BaseDiagram: basediagram.NewBaseDiagram(NewClassConfigurationProperties()),
Direction: ClassDiagramDirectionTopToBottom,
}
return
}
// String generates the Mermaid syntax representation of the class diagram.
func (cd *ClassDiagram) String() string {
var sb strings.Builder
sb.WriteString(baseDiagramType)
sb.WriteString(fmt.Sprintf(string(baseClassDiagramDirectionString), string(cd.Direction)))
for _, note := range cd.notes {
sb.WriteString(note.String())
}
for _, namespace := range cd.namespaces {
sb.WriteString(namespace.String(""))
}
for _, class := range cd.classes {
sb.WriteString(class.String("%s"))
}
for _, relation := range cd.relations {
sb.WriteString(relation.String())
}
return cd.BaseDiagram.String(sb.String())
}
// RenderToFile saves the diagram to a file at the specified path.
func (cd *ClassDiagram) RenderToFile(path string) error {
return utils.RenderToFile(path, cd.String())
}
// AddNamespace creates and adds a new namespace to the class diagram.
// It returns the newly created Namespace.
func (cd *ClassDiagram) AddNamespace(name string) (newNamespace *Namespace) {
newNamespace = NewNamespace(name)
cd.namespaces = append(cd.namespaces, newNamespace)
return
}
// AddNote creates and adds a new note to the class diagram.
// The note can be associated with a specific class or be a general diagram note.
func (cd *ClassDiagram) AddNote(text string, class *Class) {
newNote := NewNote(text, class)
cd.notes = append(cd.notes, newNote)
}
// AddClass creates and adds a new class to the class diagram.
// If namespace is nil, the class is added directly to the diagram.
// Returns the newly created Class.
func (cd *ClassDiagram) AddClass(name string, namespace *Namespace) (newClass *Class) {
newClass = NewClass(name)
if namespace == nil {
cd.classes = append(cd.classes, newClass)
} else {
namespace.AddClass(newClass)
}
return
}
// AddRelation creates and adds a new relation between two classes.
// Returns the newly created Relation.
func (cd *ClassDiagram) AddRelation(classA *Class, classB *Class) (newRelation *Relation) {
newRelation = NewRelation(classA, classB)
cd.relations = append(cd.relations, newRelation)
return
}
package class
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// fieldVisibility represents the access modifier for a field in a class diagram.
type fieldVisibility string
// fieldClassifier represents additional modifiers for a field, such as static.
type fieldClassifier string
// Field visibility constants define the access levels for class fields.
const (
FieldVisibilityPublic fieldVisibility = "+"
FieldVisibilityPrivate fieldVisibility = "-"
FieldVisibilityProtected fieldVisibility = "#"
FieldVisibilityInternal fieldVisibility = "~"
)
// Field classifier constants define additional field characteristics.
const (
FieldClassifierStatic fieldClassifier = "$"
)
// Formatting constants for field string representation.
const (
baseFieldBaseString string = basediagram.Indentation + "%s%s %s%s"
baseFieldParamString string = "%s:%s,"
)
// Field represents a class field with visibility and type information
type Field struct {
Name string
Type string
Visibility fieldVisibility
Classifier fieldClassifier
}
// NewField creates a field with the given name and type
func NewField(name string, fieldType string) *Field {
return &Field{
Name: name,
Type: fieldType,
Visibility: FieldVisibilityPublic,
}
}
// SetVisibility sets the field's visibility
func (f *Field) SetVisibility(visibility fieldVisibility) *Field {
f.Visibility = visibility
return f
}
// String returns the Mermaid syntax representation of this field
func (f *Field) String() string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf(string(baseFieldBaseString), f.Visibility, f.Type, f.Name, f.Classifier))
return sb.String()
}
package class
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// methodVisibility represents the access modifier for a method
type methodVisibility string
// methodClassifier represents additional modifiers for a method
type methodClassifier string
// Method visibility constants
const (
MethodVisibilityPublic methodVisibility = "+"
MethodVisibilityPrivate methodVisibility = "-"
MethodVisibilityProtected methodVisibility = "#"
MethodVisibilityInternal methodVisibility = "~"
)
// Method classifier constants
const (
MethodClassifierAbstract methodClassifier = "*"
MethodClassifierStatic methodClassifier = "$"
)
// Mermaid method syntax templates
const (
baseMethodBaseString string = basediagram.Indentation + "%s%s(%s)%s %s"
baseMethodParamString string = "%s:%s,"
)
// Parameter represents a method parameter
type Parameter struct {
Name string
Type string
}
// Method represents a class method
type Method struct {
Name string
Parameters []Parameter
ReturnType string
Visibility methodVisibility
Classifier methodClassifier
}
// NewMethod creates a method with the given name
func NewMethod(name string) (newMethod *Method) {
newMethod = &Method{
Name: name,
Visibility: MethodVisibilityPublic,
}
return
}
// SetVisibility sets the method's visibility
func (m *Method) SetVisibility(visibility methodVisibility) *Method {
m.Visibility = visibility
return m
}
// SetReturnType sets the method's return type
func (m *Method) SetReturnType(returnType string) *Method {
m.ReturnType = returnType
return m
}
// SetClassifier sets the method's classifier
func (m *Method) SetClassifier(classifier methodClassifier) *Method {
m.Classifier = classifier
return m
}
// AddParameter adds a parameter to this method
func (m *Method) AddParameter(paramName string, paramType string) {
m.Parameters = append(m.Parameters, Parameter{Name: paramName, Type: paramType})
}
// String returns the Mermaid syntax representation of this method
func (m *Method) String() string {
var sb strings.Builder
params := ""
for _, param := range m.Parameters {
params += fmt.Sprintf(string(baseMethodParamString), param.Name, param.Type)
}
params = strings.Trim(params, ",")
sb.WriteString(fmt.Sprintf(string(baseMethodBaseString), m.Visibility, m.Name, params, m.Classifier, m.ReturnType))
return sb.String()
}
package class
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Namespace constants for formatting the Mermaid syntax representation.
const (
baseNamespaceStartString string = basediagram.Indentation + "namespace %s{\n"
baseNamespaceEndString string = basediagram.Indentation + "}\n"
)
// Namespace represents a container for grouping related classes
type Namespace struct {
Name string
Classes []*Class
Children []*Namespace
}
// NewNamespace creates a namespace with the given name
func NewNamespace(name string) *Namespace {
return &Namespace{
Name: name,
Classes: make([]*Class, 0),
Children: make([]*Namespace, 0),
}
}
// AddClass adds a class to this namespace
func (n *Namespace) AddClass(class *Class) {
n.Classes = append(n.Classes, class)
}
// AddNamespace creates and adds a nested namespace
func (n *Namespace) AddNamespace(name string) *Namespace {
namespace := NewNamespace(name)
n.Children = append(n.Children, namespace)
return namespace
}
// String returns the Mermaid syntax representation of this namespace
func (n *Namespace) String(curIndentation string) string {
var sb strings.Builder
if len(n.Classes) > 0 {
sb.WriteString(fmt.Sprintf(string(baseNamespaceStartString), n.Name))
for _, class := range n.Classes {
sb.WriteString(class.String(curIndentation + basediagram.Indentation + "%s"))
}
sb.WriteString(baseNamespaceEndString)
}
return sb.String()
}
package class
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Note constants for formatting the Mermaid syntax representation.
const (
baseDiagramNoteString string = basediagram.Indentation + "note \"%s\"\n"
baseClassNoteString string = basediagram.Indentation + "note for %s \"%s\"\n"
)
// Note represents an annotation or comment in a class diagram.
// It can be either a general diagram note or a note associated with a specific class.
type Note struct {
Text string
Class *Class
}
// NewNote creates a new Note with the given text and optional associated class.
// If no class is provided, the note is treated as a general diagram note.
func NewNote(text string, class *Class) (newNote *Note) {
newNote = &Note{
Text: text,
Class: class,
}
return
}
// String generates the Mermaid syntax representation of the note.
// If the note is associated with a class, it uses the class-specific note format.
// Otherwise, it uses the general diagram note format.
func (n *Note) String() string {
var sb strings.Builder
if n.Class == nil {
sb.WriteString(fmt.Sprintf(string(baseDiagramNoteString), n.Text))
} else {
sb.WriteString(fmt.Sprintf(string(baseClassNoteString), n.Class.Name, n.Text))
}
return sb.String()
}
package class
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// relationType represents the type of relationship between classes.
type relationType string
// relationLink represents the visual style of the relationship line.
type relationLink string
// relationCardinality represents the multiplicity of the relationship.
type relationCardinality string
// Relationship type constants define different types of class relationships.
const (
RelationTypeAssociation relationType = ">"
RelationTypeAssociationLeft relationType = "<"
RelationTypeInheritance relationType = "|>"
RelationTypeInheritanceLeft relationType = "<|"
RelationTypeComposition relationType = "*"
RelationTypeAggregation relationType = "o"
)
// Relationship link constants define the line style for relationships.
const (
RelationLinkSolid relationLink = "--"
RelationLinkDashed relationLink = ".."
)
// Relationship cardinality constants define the multiplicity of relationships.
const (
RelationCardinalityOnlyOne relationCardinality = "\"1\""
RelationCardinalityZeroOrOne relationCardinality = "\"0..1\""
RelationCardinalityOneOrMore relationCardinality = "\"1..*\""
RelationCardinalityMany relationCardinality = "\"*\""
RelationCardinalityN relationCardinality = "\"n\""
RelationCardinalityZeroToN relationCardinality = "\"0..n\""
RelationCardinalityOneToN relationCardinality = "\"1..n\""
)
// Formatting constants for relation string representation.
const (
baseRelationString string = basediagram.Indentation + "%s %s%s%s%s%s %s%s\n"
baseRelationTextString string = " : %s"
)
// Relation represents a relationship between two classes in a class diagram.
// It includes information about the related classes, relationship types,
// cardinalities, link style, and optional label.
type Relation struct {
ClassA *Class
ClassB *Class
RelationToClassA relationType
RelationToClassB relationType
CardinalityToClassA relationCardinality
CardinalityToClassB relationCardinality
Link relationLink
Label string
}
// NewRelation creates a new Relation between two classes.
// It initializes the relation with a default solid link.
func NewRelation(classA *Class, classB *Class) (newRelation *Relation) {
newRelation = &Relation{
ClassA: classA,
ClassB: classB,
Link: RelationLinkSolid,
}
return
}
// String generates the Mermaid syntax representation of the relationship.
// It includes the related classes, relationship types, cardinalities, link style, and optional label.
func (r *Relation) String() string {
var sb strings.Builder
label := ""
if len(r.Label) > 0 {
label = fmt.Sprintf(string(baseRelationTextString), r.Label)
}
sb.WriteString(fmt.Sprintf(string(baseRelationString), r.ClassA.Name, r.CardinalityToClassA, r.RelationToClassA, r.Link, r.RelationToClassB, r.CardinalityToClassB, r.ClassB.Name, label))
return sb.String()
}
package entityrelationship
import (
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
const (
baseErConfigurationProperties string = basediagram.Indentation + "er:\n"
erPropertyTitleTopMargin string = "titleTopMargin"
erPropertyDiagramPadding string = "diagramPadding"
erPropertyLayoutDirection string = "layoutDirection"
erPropertyMinEntityWidth string = "minEntityWidth"
erPropertyMinEntityHeight string = "minEntityHeight"
erPropertyEntityPadding string = "entityPadding"
erPropertyStroke string = "stroke"
erPropertyFill string = "fill"
erPropertyFontSize string = "fontSize"
)
type ErConfigurationProperties struct {
basediagram.ConfigurationProperties
properties map[string]basediagram.DiagramProperty
}
func NewErConfigurationProperties() ErConfigurationProperties {
return ErConfigurationProperties{
ConfigurationProperties: basediagram.NewConfigurationProperties(),
properties: make(map[string]basediagram.DiagramProperty),
}
}
// Setters for each property from the schema
func (c *ErConfigurationProperties) SetTitleTopMargin(v int) *ErConfigurationProperties {
c.properties[erPropertyTitleTopMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: erPropertyTitleTopMargin,
Val: v,
},
}
return c
}
func (c *ErConfigurationProperties) SetDiagramPadding(v int) *ErConfigurationProperties {
c.properties[erPropertyDiagramPadding] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: erPropertyDiagramPadding,
Val: v,
},
}
return c
}
func (c *ErConfigurationProperties) SetLayoutDirection(v string) *ErConfigurationProperties {
c.properties[erPropertyLayoutDirection] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: erPropertyLayoutDirection,
Val: v,
},
}
return c
}
func (c *ErConfigurationProperties) SetMinEntityWidth(v int) *ErConfigurationProperties {
c.properties[erPropertyMinEntityWidth] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: erPropertyMinEntityWidth,
Val: v,
},
}
return c
}
func (c *ErConfigurationProperties) SetMinEntityHeight(v int) *ErConfigurationProperties {
c.properties[erPropertyMinEntityHeight] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: erPropertyMinEntityHeight,
Val: v,
},
}
return c
}
func (c *ErConfigurationProperties) SetEntityPadding(v int) *ErConfigurationProperties {
c.properties[erPropertyEntityPadding] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: erPropertyEntityPadding,
Val: v,
},
}
return c
}
func (c *ErConfigurationProperties) SetStroke(v string) *ErConfigurationProperties {
c.properties[erPropertyStroke] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: erPropertyStroke,
Val: v,
},
}
return c
}
func (c *ErConfigurationProperties) SetFill(v string) *ErConfigurationProperties {
c.properties[erPropertyFill] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: erPropertyFill,
Val: v,
},
}
return c
}
func (c *ErConfigurationProperties) SetFontSize(v int) *ErConfigurationProperties {
c.properties[erPropertyFontSize] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: erPropertyFontSize,
Val: v,
},
}
return c
}
func (c ErConfigurationProperties) String() string {
var sb strings.Builder
sb.WriteString(c.ConfigurationProperties.String())
if len(c.properties) > 0 {
sb.WriteString(baseErConfigurationProperties)
for _, prop := range c.properties {
sb.WriteString(prop.Format())
}
}
return sb.String()
}
// Package entityrelationship provides functionality for creating Mermaid ER diagrams
package entityrelationship
import (
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Base string formats for entity relationship diagrams
const (
baseDiagramType string = "erDiagram\n"
)
// Diagram represents an entity relationship diagram
type Diagram struct {
basediagram.BaseDiagram[ErConfigurationProperties]
Entities []*Entity
Relationships []*Relationship
}
// NewDiagram creates a new ERD diagram
func NewDiagram() *Diagram {
return &Diagram{
BaseDiagram: basediagram.NewBaseDiagram(NewErConfigurationProperties()),
Entities: make([]*Entity, 0),
Relationships: make([]*Relationship, 0),
}
}
// AddEntity creates and adds a new entity to the diagram
func (d *Diagram) AddEntity(name string) *Entity {
entity := NewEntity(name)
d.Entities = append(d.Entities, entity)
return entity
}
// AddRelationship creates a new relationship between two entities
func (d *Diagram) AddRelationship(from, to *Entity) *Relationship {
rel := NewRelationship(from, to)
d.Relationships = append(d.Relationships, rel)
return rel
}
// String generates the Mermaid syntax for the diagram
func (d *Diagram) String() string {
var sb strings.Builder
sb.WriteString(baseDiagramType)
// Add entities
for _, entity := range d.Entities {
sb.WriteString(entity.String())
}
// Add relationships
if len(d.Relationships) > 0 {
sb.WriteString("\n")
for _, rel := range d.Relationships {
sb.WriteString(rel.String())
}
}
return d.BaseDiagram.String(sb.String())
}
// RenderToFile saves the diagram to a file at the specified path.
func (d *Diagram) RenderToFile(path string) error {
return utils.RenderToFile(path, d.String())
}
package entityrelationship
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// DataType represents the type of an attribute
type DataType string
const (
TypeString DataType = "string"
TypeInteger DataType = "int"
TypeFloat DataType = "float"
TypeBoolean DataType = "boolean"
TypeDateTime DataType = "datetime"
)
const (
baseEntityNoAliasString = basediagram.Indentation + "%s {\n"
baseEntityWithAliasString = basediagram.Indentation + "%s [%s] {\n"
baseEntityAttributeString = basediagram.Indentation + basediagram.Indentation + "%s %s%s\n"
)
// Entity represents a table or entity in the ERD
type Entity struct {
Name string
Alias string
Attributes []*Attribute
}
// Attribute represents a column or field in an entity
type Attribute struct {
Name string
Type DataType
PK bool
FK bool
Required bool
}
// NewEntity creates a new Entity
func NewEntity(name string) *Entity {
return &Entity{
Name: name,
Attributes: make([]*Attribute, 0),
}
}
// AddAttribute adds a new attribute to the entity
func (e *Entity) AddAttribute(name string, dataType DataType) *Attribute {
attr := &Attribute{
Name: name,
Type: dataType,
}
e.Attributes = append(e.Attributes, attr)
return attr
}
// SetPrimaryKey marks the attribute as a primary key and returns it for chaining
func (a *Attribute) SetPrimaryKey() *Attribute {
a.PK = true
return a
}
// SetForeignKey marks the attribute as a foreign key and returns it for chaining
func (a *Attribute) SetForeignKey() *Attribute {
a.FK = true
return a
}
// SetRequired marks the attribute as required and returns it for chaining
func (a *Attribute) SetRequired() *Attribute {
a.Required = true
return a
}
// SetAlias sets an alternative display name for the entity and returns it for chaining
func (e *Entity) SetAlias(alias string) *Entity {
e.Alias = alias
return e
}
// String generates the Mermaid syntax for the entity
func (e *Entity) String() string {
var sb strings.Builder
if e.Alias != "" {
sb.WriteString(fmt.Sprintf(string(baseEntityWithAliasString), e.Name, e.Alias))
} else {
sb.WriteString(fmt.Sprintf(string(baseEntityNoAliasString), e.Name))
}
for _, attr := range e.Attributes {
keys := ""
if attr.PK && attr.FK {
keys = " PK,FK"
} else if attr.PK {
keys = " PK"
} else if attr.FK {
keys = " FK"
}
sb.WriteString(fmt.Sprintf(string(baseEntityAttributeString), attr.Type, attr.Name, keys))
}
sb.WriteString(basediagram.Indentation + "}\n")
return sb.String()
}
package entityrelationship
import (
"fmt"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Cardinality represents the relationship cardinality
type Cardinality string
const (
baseRelationshipString = basediagram.Indentation + "%s %s %s : %s\n"
)
// Common relationship patterns
const (
OneToZeroOrMore Cardinality = "||--o{" // One to many (optional)
OneToOneOrMore Cardinality = "||--|{" // One to many (required)
OneToExactlyOne Cardinality = "||--||" // One to one
ZeroOrOneToMany Cardinality = "|o--o{" // Optional one to many
ManyToMany Cardinality = "}o--o{" // Many to many
// Base cardinality symbols
ZeroOrOne Cardinality = "|o"
ExactlyOne Cardinality = "||"
ZeroOrMore Cardinality = "o{"
OneOrMore Cardinality = "|{"
)
// Relationship represents a relationship between two entities
type Relationship struct {
From *Entity
To *Entity
Label string
Cardinality Cardinality
}
// NewRelationship creates a new relationship between entities
func NewRelationship(from, to *Entity) *Relationship {
return &Relationship{
From: from,
To: to,
Cardinality: ExactlyOne, // default cardinality
}
}
// SetLabel sets the relationship label
func (r *Relationship) SetLabel(label string) *Relationship {
r.Label = label
return r
}
// SetCardinality sets the relationship cardinality
func (r *Relationship) SetCardinality(cardinality Cardinality) *Relationship {
r.Cardinality = cardinality
return r
}
// String generates the Mermaid syntax for the relationship
func (r *Relationship) String() string {
label := r.Label
if label == "" {
label = "relates"
}
return fmt.Sprintf(string(baseRelationshipString), r.From.Name, string(r.Cardinality), r.To.Name, label)
}
package flowchart
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
const (
baseClassString string = basediagram.Indentation + "classDef %s %s\n"
)
// Classes are a convenient way of creating a node style since you can attach them directly to a node.
// Reference: https://mermaid.js.org/syntax/flowchart.html#classes
type Class struct {
Name string
Style *NodeStyle
}
// NewClass creates a new Class with the given name and a default node style.
func NewClass(name string) (newClass *Class) {
newClass = &Class{
Name: name,
Style: NewNodeStyle(),
}
return
}
// String generates a Mermaid string representation of the class definition,
// including its name and style properties.
func (c *Class) String() string {
var sb strings.Builder
if c.Style != nil {
sb.WriteString(fmt.Sprintf(string(baseClassString), c.Name, c.Style.String()))
}
return sb.String()
}
package flowchart
import (
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
const (
baseFlowchartConfigurationProperties string = basediagram.Indentation + "flowchart:\n"
flowchartPropertyTitleTopMargin string = "titleTopMargin"
flowchartPropertyDiagramPadding string = "diagramPadding"
flowchartPropertyHtmlLabels string = "htmlLabels"
flowchartPropertyNodeSpacing string = "nodeSpacing"
flowchartPropertyRankSpacing string = "rankSpacing"
flowchartPropertyCurve string = "curve"
flowchartPropertyPadding string = "padding"
flowchartPropertyDefaultRenderer string = "defaultRenderer"
flowchartPropertyWrappingWidth string = "wrappingWidth"
flowchartPropertyArrowMarkerAbsolute string = "arrowMarkerAbsolute"
)
// FlowchartConfigurationProperties holds flowchart-specific configuration
type FlowchartConfigurationProperties struct {
basediagram.ConfigurationProperties
properties map[string]basediagram.DiagramProperty
}
func NewFlowchartConfigurationProperties() FlowchartConfigurationProperties {
return FlowchartConfigurationProperties{
ConfigurationProperties: basediagram.NewConfigurationProperties(),
properties: make(map[string]basediagram.DiagramProperty),
}
}
func (c *FlowchartConfigurationProperties) SetTitleTopMargin(v int) *FlowchartConfigurationProperties {
c.properties[flowchartPropertyTitleTopMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: flowchartPropertyTitleTopMargin,
Val: v,
},
}
return c
}
func (c *FlowchartConfigurationProperties) SetDiagramPadding(v int) *FlowchartConfigurationProperties {
c.properties[flowchartPropertyDiagramPadding] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: flowchartPropertyDiagramPadding,
Val: v,
},
}
return c
}
func (c *FlowchartConfigurationProperties) SetHtmlLabels(v bool) *FlowchartConfigurationProperties {
c.properties[flowchartPropertyHtmlLabels] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: flowchartPropertyHtmlLabels,
Val: v,
},
}
return c
}
func (c *FlowchartConfigurationProperties) SetNodeSpacing(v int) *FlowchartConfigurationProperties {
c.properties[flowchartPropertyNodeSpacing] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: flowchartPropertyNodeSpacing,
Val: v,
},
}
return c
}
func (c *FlowchartConfigurationProperties) SetRankSpacing(v int) *FlowchartConfigurationProperties {
c.properties[flowchartPropertyRankSpacing] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: flowchartPropertyRankSpacing,
Val: v,
},
}
return c
}
func (c *FlowchartConfigurationProperties) SetCurve(v string) *FlowchartConfigurationProperties {
c.properties[flowchartPropertyCurve] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: flowchartPropertyCurve,
Val: v,
},
}
return c
}
func (c *FlowchartConfigurationProperties) SetPadding(v int) *FlowchartConfigurationProperties {
c.properties[flowchartPropertyPadding] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: flowchartPropertyPadding,
Val: v,
},
}
return c
}
func (c *FlowchartConfigurationProperties) SetDefaultRenderer(v string) *FlowchartConfigurationProperties {
c.properties[flowchartPropertyDefaultRenderer] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: flowchartPropertyDefaultRenderer,
Val: v,
},
}
return c
}
func (c *FlowchartConfigurationProperties) SetWrappingWidth(v int) *FlowchartConfigurationProperties {
c.properties[flowchartPropertyWrappingWidth] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: flowchartPropertyWrappingWidth,
Val: v,
},
}
return c
}
func (c *FlowchartConfigurationProperties) SetArrowMarkerAbsolute(v bool) *FlowchartConfigurationProperties {
c.properties[flowchartPropertyArrowMarkerAbsolute] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: flowchartPropertyArrowMarkerAbsolute,
Val: v,
},
}
return c
}
func (c FlowchartConfigurationProperties) String() string {
var sb strings.Builder
sb.WriteString(c.ConfigurationProperties.String())
if len(c.properties) > 0 {
sb.WriteString(baseFlowchartConfigurationProperties)
for _, prop := range c.properties {
sb.WriteString(prop.Format())
}
}
return sb.String()
}
// Package flowchart provides functionality for creating Mermaid flowcharts
package flowchart
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
type flowchartDirection string
type curveStyle string
// List of possible Flowchart directions.
// Reference: https://mermaid.js.org/syntax/flowchart.html#direction
const (
FlowchartDirectionTopToBottom flowchartDirection = "TB"
FlowchartDirectionTopDown flowchartDirection = "TD"
FlowchartDirectionBottomUp flowchartDirection = "BT"
FlowchartDirectionRightLeft flowchartDirection = "RL"
FlowchartDirectionLeftRight flowchartDirection = "LR"
)
// List of possible Flowchart directions.
// Reference: https://mermaid.js.org/syntax/flowchart.html#styling-line-curves
const (
CurveStyleNone curveStyle = ""
CurveStyleBasis curveStyle = "basis"
CurveStyleBumpX curveStyle = "bumpX"
CurveStyleBumpY curveStyle = "bumpY"
CurveStyleCardinal curveStyle = "cardinal"
CurveStyleCatmullRom curveStyle = "catmullRom"
CurveStyleLinear curveStyle = "linear"
CurveStyleMonotoneX curveStyle = "monotoneX"
CurveStyleMonotoneY curveStyle = "monotoneY"
CurveStyleNatural curveStyle = "natural"
CurveStyleStep curveStyle = "step"
CurveStyleStepAfter curveStyle = "stepAfter"
CurveStyleStepBefore curveStyle = "stepBefore"
)
const (
baseFlowchartDirectionString string = "flowchart %s\n"
)
// Flowcharts are composed of nodes (geometric shapes) and links (arrows or lines).
// The Mermaid code defines how nodes and links are made and accommodates different arrow types,
// multi-directional arrows, and any linking to and from subgraphs.
// Reference: https://mermaid.js.org/syntax/flowchart.html
type Flowchart struct {
basediagram.BaseDiagram[FlowchartConfigurationProperties]
Direction flowchartDirection
CurveStyle curveStyle
classes []*Class
nodes []*Node
subgraphs []*Subgraph
links []*Link
idGenerator utils.IDGenerator
}
// NewFlowchart creates a new flowchart diagram
func NewFlowchart() *Flowchart {
return &Flowchart{
BaseDiagram: basediagram.NewBaseDiagram(NewFlowchartConfigurationProperties()),
Direction: FlowchartDirectionTopToBottom,
CurveStyle: CurveStyleNone,
classes: make([]*Class, 0),
nodes: make([]*Node, 0),
subgraphs: make([]*Subgraph, 0),
links: make([]*Link, 0),
idGenerator: utils.NewIDGenerator(),
}
}
// SetDirection sets the flowchart direction and returns the flowchart for chaining
func (f *Flowchart) SetDirection(direction flowchartDirection) *Flowchart {
f.Direction = direction
return f
}
// RenderToFile saves the flowchart diagram to a file at the specified path.
func (f *Flowchart) RenderToFile(path string) error {
return utils.RenderToFile(path, f.String())
}
// AddSubgraph adds a new subgraph to the flowchart and returns the created subgraph.
func (f *Flowchart) AddSubgraph(title string) (newSubgraph *Subgraph) {
newSubgraph = NewSubgraph(f.idGenerator.NextID(), title)
f.subgraphs = append(f.subgraphs, newSubgraph)
return
}
// AddNode adds a new node to the flowchart and returns the created node.
func (f *Flowchart) AddNode(text string) (newNode *Node) {
newNode = NewNode(f.idGenerator.NextID(), text)
f.nodes = append(f.nodes, newNode)
return
}
// AddLink adds a new link between two nodes in the flowchart and returns the created link.
func (f *Flowchart) AddLink(from *Node, to *Node) (newLink *Link) {
newLink = NewLink(from, to)
f.links = append(f.links, newLink)
return
}
// AddClass adds a new class to the flowchart and returns the created class.
func (f *Flowchart) AddClass(name string) (newClass *Class) {
newClass = NewClass(name)
f.classes = append(f.classes, newClass)
return
}
// String generates a Mermaid flowchart string representation
func (f *Flowchart) String() string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf(string(baseFlowchartDirectionString), string(f.Direction)))
for _, class := range f.classes {
sb.WriteString(class.String())
}
for _, node := range f.nodes {
sb.WriteString(node.String())
}
for _, subgraph := range f.subgraphs {
sb.WriteString(subgraph.String("%s"))
}
for _, link := range f.links {
sb.WriteString(link.String())
}
return f.BaseDiagram.String(sb.String())
}
package flowchart
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
type linkShape string
type linkArrowType string
// List of possible Link shapes.
// Reference: https://mermaid.js.org/syntax/flowchart.html#links-between-nodes
const (
LinkShapeOpen linkShape = "--%s"
LinkShapeDotted linkShape = "-.%s-"
LinkShapeThick linkShape = "==%s"
LinkShapeInvisible linkShape = "~~%s"
)
// List of possible Link arrow types.
// Reference: https://mermaid.js.org/syntax/flowchart.html#links-between-nodes
const (
LinkArrowTypeNone linkArrowType = ""
LinkArrowTypeArrow linkArrowType = ">"
LinkArrowTypeLeftArrow linkArrowType = "<"
LinkArrowTypeBullet linkArrowType = "o"
LinkArrowTypeCross linkArrowType = "x"
)
const (
baseLinkString string = basediagram.Indentation + "%s %s%s%s%s %s\n"
baseLinkTextString string = "|%s|"
)
// Link represents a connection between nodes in a flowchart
type Link struct {
Shape linkShape
Head linkArrowType
Tail linkArrowType
Text string
From *Node
To *Node
Length int
}
// NewLink creates a new Link and sets default values to some attributes
func NewLink(from *Node, to *Node) (newLink *Link) {
newLink = &Link{
From: from,
To: to,
Shape: LinkShapeOpen,
Head: LinkArrowTypeArrow,
Tail: LinkArrowTypeNone,
Length: 0,
}
return
}
// SetText sets the link text and returns the link for chaining
func (l *Link) SetText(text string) *Link {
l.Text = text
return l
}
// SetShape sets the link shape and returns the link for chaining
func (l *Link) SetShape(shape linkShape) *Link {
l.Shape = shape
return l
}
// SetLength sets the link length and returns the link for chaining
func (l *Link) SetLength(length int) *Link {
l.Length = length
return l
}
// SetHead sets the head arrow type and returns the link for chaining
func (l *Link) SetHead(arrowType linkArrowType) *Link {
l.Head = arrowType
return l
}
// SetTail sets the tail arrow type and returns the link for chaining
func (l *Link) SetTail(arrowType linkArrowType) *Link {
l.Tail = arrowType
return l
}
// String generates a Mermaid string representation of the link,
// including its shape, arrow types, text, and length.
func (l *Link) String() string {
var sb strings.Builder
extension := ""
for i := 0; i < l.Length; i++ {
extension += string(l.Shape[1])
}
text := ""
if len(l.Text) > 0 {
text = fmt.Sprintf(string(baseLinkTextString), l.Text)
}
sb.WriteString(fmt.Sprintf(string(baseLinkString), l.From.ID, string(l.Tail), fmt.Sprintf(string(l.Shape), extension), string(l.Head), text, l.To.ID))
return sb.String()
}
package flowchart
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
type nodeShape string
// List of possible Node shapes.
// Reference: https://mermaid.js.org/syntax/flowchart.html#complete-list-of-new-shapes
const (
// Basic shapes
NodeShapeProcess nodeShape = `rect` // Process (Rectangle)
NodeShapeEvent nodeShape = `rounded` // Event (Rounded rectangle)
NodeShapeTerminal nodeShape = `stadium` // Terminal (Stadium-shaped)
NodeShapeSubprocess nodeShape = `fr-rect` // Subprocess (Framed rectangle)
NodeShapeDatabase nodeShape = `cyl` // Database (Cylinder)
NodeShapeStart nodeShape = `circle` // Start (Circle)
NodeShapeOdd nodeShape = `odd` // Odd shape (Asymmetric)
NodeShapeDecision nodeShape = `diam` // Decision (Diamond)
NodeShapePrepare nodeShape = `hex` // Prepare (Hexagon)
NodeShapeInputOutput nodeShape = `lean-r` // Input/Output (Parallelogram)
NodeShapeOutputInput nodeShape = `lean-l` // Output/Input (Alt Parallelogram)
NodeShapeManualOperation nodeShape = `trap-b` // Manual Operation (Trapezoid)
NodeShapeManual nodeShape = `trap-t` // Manual (Alt Trapezoid)
NodeShapeStopDouble nodeShape = `dbl-circ` // Stop (Double Circle)
NodeShapeText nodeShape = `text` // Text block
NodeShapeCard nodeShape = `notch-rect` // Card
NodeShapeLinedProcess nodeShape = `lin-rect` // Lined Process (Rectangle with shadow)
NodeShapeStartSmall nodeShape = `sm-circ` // Start (Small circle)
NodeShapeStopFramed nodeShape = `fr-circ` // Stop (Circle with frame)
NodeShapeForkJoin nodeShape = `fork` // Fork/Join
NodeShapeCollate nodeShape = `hourglass` // Collate (Hourglass)
NodeShapeComment nodeShape = `brace` // Comment (Left brace)
NodeShapeCommentRight nodeShape = `brace-r` // Comment Right (Right brace)
NodeShapeCommentBothSides nodeShape = `braces` // Comment (Both braces)
NodeShapeComLink nodeShape = `bolt` // Com Link (Lightning bolt)
NodeShapeDocument nodeShape = `doc` // Document
NodeShapeDelay nodeShape = `delay` // Delay
NodeShapeStorage nodeShape = `h-cyl` // Storage (Horizontal cylinder)
NodeShapeDiskStorage nodeShape = `lin-cyl` // Disk Storage (Lined cylinder)
NodeShapeDisplay nodeShape = `curv-trap` // Display (Curved trapezoid)
NodeShapeDividedProcess nodeShape = `div-rect` // Divided Process
NodeShapeExtract nodeShape = `tri` // Extract (Triangle)
NodeShapeInternalStorage nodeShape = `win-pane` // Internal Storage
NodeShapeJunction nodeShape = `f-circ` // Junction (Filled circle)
NodeShapeLinedDocument nodeShape = `lin-doc` // Lined Document
NodeShapeLoopLimit nodeShape = `notch-pent` // Loop Limit
NodeShapeManualFile nodeShape = `flip-tri` // Manual File
NodeShapeManualInput nodeShape = `sl-rect` // Manual Input (Sloped rectangle)
NodeShapeMultiDocument nodeShape = `docs` // Multi-Document
NodeShapeMultiProcess nodeShape = `st-rect` // Multi-Process
NodeShapePaperTape nodeShape = `flag` // Paper Tape
NodeShapeStoredData nodeShape = `bow-rect` // Stored Data
NodeShapeSummary nodeShape = `cross-circ` // Summary (Circle with cross)
NodeShapeTaggedDocument nodeShape = `tag-doc` // Tagged Document
NodeShapeTaggedProcess nodeShape = `tag-rect` // Tagged Process
)
const (
baseNodeShapeString string = basediagram.Indentation + "%s@{ shape: %s, label: \"%s\"}"
baseNodeClassString string = ":::%s"
baseNodeStyleString string = basediagram.Indentation + "style %s %s\n"
)
// Node represents a node in a flowchart
type Node struct {
ID string
Shape nodeShape
Text string
Style *NodeStyle
Class *Class
}
// NewNode creates a new Node with the given ID and text, setting default shape to round edges.
func NewNode(id string, text string) (newNode *Node) {
newNode = &Node{
ID: id,
Text: text,
Shape: NodeShapeProcess,
}
return
}
// SetClass sets the node class and returns the node for chaining
func (n *Node) SetClass(class *Class) *Node {
n.Class = class
return n
}
// SetText sets the node text and returns the node for chaining
func (n *Node) SetText(text string) *Node {
n.Text = text
return n
}
// SetStyle sets the style for the node and returns the node for chaining
func (n *Node) SetStyle(style *NodeStyle) *Node {
n.Style = style
return n
}
// SetShape sets the node shape and returns the node for chaining
func (n *Node) SetShape(shape nodeShape) *Node {
n.Shape = shape
return n
}
// String generates a Mermaid string representation of the node, including its shape, class, and style.
func (n *Node) String() string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf(string(baseNodeShapeString), n.ID, string(n.Shape), n.Text))
if n.Class != nil {
sb.WriteString(fmt.Sprintf(string(baseNodeClassString), n.Class.Name))
}
sb.WriteByte('\n')
if n.Style != nil {
sb.WriteString(fmt.Sprintf(string(baseNodeStyleString), n.ID, n.Style.String()))
}
return sb.String()
}
package flowchart
import (
"fmt"
"strings"
)
const (
baseNodeStyleColorString string = "color:%s,"
baseNodeStyleFillString string = "fill:%s,"
baseNodeStyleStrokeString string = "stroke:%s,"
baseNodeStyleStrokeWidthString string = "stroke-width:%d,"
baseNodeStyleStrokeDashString string = "stroke-dasharray:%s"
)
type NodeStyle struct {
Color string
Fill string
Stroke string
StrokeWidth int
StrokeDash string
}
// NewNodeStyle creates a new NodeStyle with default stroke width and dash settings.
func NewNodeStyle() (newNodeStyle *NodeStyle) {
newNodeStyle = &NodeStyle{
StrokeWidth: 1,
StrokeDash: "0",
}
return
}
// String generates a formatted string representation of the node style,
// including color, fill, stroke, width, and dash properties.
func (n *NodeStyle) String() string {
var sb strings.Builder
if n.Color != "" {
sb.WriteString(fmt.Sprintf(string(baseNodeStyleColorString), n.Color))
}
if n.Fill != "" {
sb.WriteString(fmt.Sprintf(string(baseNodeStyleFillString), n.Fill))
}
if n.Stroke != "" {
sb.WriteString(fmt.Sprintf(string(baseNodeStyleStrokeString), n.Stroke))
}
if n.StrokeWidth > 0 {
sb.WriteString(fmt.Sprintf(string(baseNodeStyleStrokeWidthString), n.StrokeWidth))
}
if n.StrokeDash != "" {
sb.WriteString(fmt.Sprintf(string(baseNodeStyleStrokeDashString), n.StrokeDash))
}
return strings.Trim(sb.String(), ",")
}
package flowchart
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
const (
baseSubgraphString string = basediagram.Indentation + "subgraph %s [%s]\n"
baseSubgraphDirectionString string = basediagram.Indentation + "direction %s\n"
baseSubgraphEndString string = basediagram.Indentation + "end\n"
baseSubgraphLinkString string = basediagram.Indentation + "%s"
baseSubgraphSubgraphString string = basediagram.Indentation + "%s"
)
// List of possible Subgraph directions.
// Reference: https://mermaid.js.org/syntax/flowchart.html#direction
const (
SubgraphDirectionNone subgraphDirection = ""
SubgraphDirectionTopToBottom subgraphDirection = "TB"
SubgraphDirectionBottomUp subgraphDirection = "BT"
SubgraphDirectionRightLeft subgraphDirection = "RL"
SubgraphDirectionLeftRight subgraphDirection = "LR"
)
type subgraphDirection string
type Subgraph struct {
ID string
Title string
Direction subgraphDirection
subgraphs []*Subgraph
links []*Link
idGenerator utils.IDGenerator
}
// NewSubgraph creates a new Subgraph with the given ID and title,
// setting the default direction to none.
func NewSubgraph(id string, title string) (newSubgraph *Subgraph) {
newSubgraph = &Subgraph{
ID: id,
Title: title,
Direction: SubgraphDirectionNone,
}
return
}
// AddSubgraph adds a new Subgraph to the current Subgraph and returns the created subgraph.
func (s *Subgraph) AddSubgraph(title string) (newSubgraph *Subgraph) {
if s.idGenerator == nil {
s.idGenerator = utils.NewIDGenerator()
}
newSubgraph = NewSubgraph(s.idGenerator.NextID(), title)
newSubgraph.idGenerator = s.idGenerator
s.subgraphs = append(s.subgraphs, newSubgraph)
return
}
// AddLink adds a new Link to the Subgraph and returns the created link.
func (s *Subgraph) AddLink(from *Node, to *Node) (newLink *Link) {
newLink = NewLink(from, to)
s.links = append(s.links, newLink)
return
}
// String generates a Mermaid string representation of the Subgraph,
// including its subgraphs, direction, and links with the specified indentation.
func (s *Subgraph) String(curIndentation string) string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf(string(curIndentation), fmt.Sprintf(string(baseSubgraphString), s.ID, s.Title)))
direction := ""
if s.Direction != SubgraphDirectionNone {
direction = fmt.Sprintf(string(curIndentation), fmt.Sprintf(string(baseSubgraphDirectionString), string(s.Direction)))
}
sb.WriteString(direction)
for _, subgraph := range s.subgraphs {
nextIndentation := fmt.Sprintf(string(baseSubgraphSubgraphString), string(curIndentation))
sb.WriteString(subgraph.String(nextIndentation))
}
for _, link := range s.links {
sb.WriteString(fmt.Sprintf(string(curIndentation), fmt.Sprintf(string(baseSubgraphLinkString), link.String())))
}
sb.WriteString(fmt.Sprintf(string(curIndentation), baseSubgraphEndString))
return sb.String()
}
package sequence
// ActorType represents the visual representation of an actor in a sequence diagram.
type ActorType string
// Predefined actor types for sequence diagrams.
const (
ActorParticipant ActorType = "participant"
ActorActor ActorType = "actor"
)
// Actor represents an entity participating in a sequence diagram.
type Actor struct {
ID string
Name string
Type ActorType
}
// NewActor creates a new Actor with the specified properties.
func NewActor(id, name string, actorType ActorType) *Actor {
return &Actor{
ID: id,
Name: name,
Type: actorType,
}
}
package sequence
import (
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
const (
baseSequenceConfigurationProperties string = basediagram.Indentation + "sequence:\n"
sequencePropertyArrowMarkerAbsolute string = "arrowMarkerAbsolute"
sequencePropertyHideUnusedParticipants string = "hideUnusedParticipants"
sequencePropertyActivationWidth string = "activationWidth"
sequencePropertyDiagramMarginX string = "diagramMarginX"
sequencePropertyDiagramMarginY string = "diagramMarginY"
sequencePropertyActorMargin string = "actorMargin"
sequencePropertyWidth string = "width"
sequencePropertyHeight string = "height"
sequencePropertyBoxMargin string = "boxMargin"
sequencePropertyBoxTextMargin string = "boxTextMargin"
sequencePropertyNoteMargin string = "noteMargin"
sequencePropertyMessageMargin string = "messageMargin"
sequencePropertyMessageAlign string = "messageAlign"
sequencePropertyMirrorActors string = "mirrorActors"
sequencePropertyForceMenus string = "forceMenus"
sequencePropertyBottomMarginAdj string = "bottomMarginAdj"
sequencePropertyRightAngles string = "rightAngles"
sequencePropertyShowSequenceNumbers string = "showSequenceNumbers"
sequencePropertyActorFontSize string = "actorFontSize"
sequencePropertyActorFontFamily string = "actorFontFamily"
sequencePropertyActorFontWeight string = "actorFontWeight"
sequencePropertyNoteFontSize string = "noteFontSize"
sequencePropertyNoteFontFamily string = "noteFontFamily"
sequencePropertyNoteFontWeight string = "noteFontWeight"
sequencePropertyNoteAlign string = "noteAlign"
sequencePropertyMessageFontSize string = "messageFontSize"
sequencePropertyMessageFontFamily string = "messageFontFamily"
sequencePropertyMessageFontWeight string = "messageFontWeight"
sequencePropertyWrap string = "wrap"
sequencePropertyWrapPadding string = "wrapPadding"
sequencePropertyLabelBoxWidth string = "labelBoxWidth"
sequencePropertyLabelBoxHeight string = "labelBoxHeight"
)
// SequenceConfigurationProperties holds sequence-specific configuration
type SequenceConfigurationProperties struct {
basediagram.ConfigurationProperties
properties map[string]basediagram.DiagramProperty
}
func NewSequenceConfigurationProperties() SequenceConfigurationProperties {
return SequenceConfigurationProperties{
ConfigurationProperties: basediagram.NewConfigurationProperties(),
properties: make(map[string]basediagram.DiagramProperty),
}
}
// Setters for each property
func (c *SequenceConfigurationProperties) SetArrowMarkerAbsolute(v bool) *SequenceConfigurationProperties {
c.properties[sequencePropertyArrowMarkerAbsolute] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyArrowMarkerAbsolute,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetHideUnusedParticipants(v bool) *SequenceConfigurationProperties {
c.properties[sequencePropertyHideUnusedParticipants] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyHideUnusedParticipants,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetActivationWidth(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyActivationWidth] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyActivationWidth,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetDiagramMarginX(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyDiagramMarginX] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyDiagramMarginX,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetDiagramMarginY(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyDiagramMarginY] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyDiagramMarginY,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetActorMargin(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyActorMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyActorMargin,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetWidth(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyWidth] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyWidth,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetHeight(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyHeight] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyHeight,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetBoxMargin(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyBoxMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyBoxMargin,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetBoxTextMargin(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyBoxTextMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyBoxTextMargin,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetNoteMargin(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyNoteMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyNoteMargin,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetMessageMargin(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyMessageMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyMessageMargin,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetMessageAlign(v string) *SequenceConfigurationProperties {
c.properties[sequencePropertyMessageAlign] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyMessageAlign,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetMirrorActors(v bool) *SequenceConfigurationProperties {
c.properties[sequencePropertyMirrorActors] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyMirrorActors,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetForceMenus(v bool) *SequenceConfigurationProperties {
c.properties[sequencePropertyForceMenus] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyForceMenus,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetBottomMarginAdj(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyBottomMarginAdj] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyBottomMarginAdj,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetRightAngles(v bool) *SequenceConfigurationProperties {
c.properties[sequencePropertyRightAngles] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyRightAngles,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetShowSequenceNumbers(v bool) *SequenceConfigurationProperties {
c.properties[sequencePropertyShowSequenceNumbers] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyShowSequenceNumbers,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetActorFontSize(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyActorFontSize] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyActorFontSize,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetActorFontFamily(v string) *SequenceConfigurationProperties {
c.properties[sequencePropertyActorFontFamily] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyActorFontFamily,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetActorFontWeight(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyActorFontWeight] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyActorFontWeight,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetNoteFontSize(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyNoteFontSize] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyNoteFontSize,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetNoteFontFamily(v string) *SequenceConfigurationProperties {
c.properties[sequencePropertyNoteFontFamily] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyNoteFontFamily,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetNoteFontWeight(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyNoteFontWeight] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyNoteFontWeight,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetNoteAlign(v string) *SequenceConfigurationProperties {
c.properties[sequencePropertyNoteAlign] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyNoteAlign,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetMessageFontSize(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyMessageFontSize] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyMessageFontSize,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetMessageFontFamily(v string) *SequenceConfigurationProperties {
c.properties[sequencePropertyMessageFontFamily] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyMessageFontFamily,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetMessageFontWeight(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyMessageFontWeight] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyMessageFontWeight,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetWrap(v bool) *SequenceConfigurationProperties {
c.properties[sequencePropertyWrap] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyWrap,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetWrapPadding(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyWrapPadding] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyWrapPadding,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetLabelBoxWidth(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyLabelBoxWidth] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyLabelBoxWidth,
Val: v,
},
}
return c
}
func (c *SequenceConfigurationProperties) SetLabelBoxHeight(v int) *SequenceConfigurationProperties {
c.properties[sequencePropertyLabelBoxHeight] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: sequencePropertyLabelBoxHeight,
Val: v,
},
}
return c
}
func (c SequenceConfigurationProperties) String() string {
var sb strings.Builder
sb.WriteString(c.ConfigurationProperties.String())
if len(c.properties) > 0 {
sb.WriteString(baseSequenceConfigurationProperties)
for _, prop := range c.properties {
sb.WriteString(prop.Format())
}
}
return sb.String()
}
// Package sequence provides functionality for creating Mermaid sequence diagrams
package sequence
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Base string formats for sequence diagrams
const (
baseDiagramType string = "sequenceDiagram\n"
)
// Diagram represents a sequence diagram with actors, messages, and rendering options.
type Diagram struct {
basediagram.BaseDiagram[SequenceConfigurationProperties]
Actors []*Actor
Messages []*Message
autonumber bool
}
// NewDiagram creates a new sequence diagram with default settings.
func NewDiagram() *Diagram {
return &Diagram{
BaseDiagram: basediagram.NewBaseDiagram(NewSequenceConfigurationProperties()),
Actors: make([]*Actor, 0),
Messages: make([]*Message, 0),
autonumber: false,
}
}
// EnableAutoNumber enables automatic numbering of messages in the sequence diagram.
func (d *Diagram) EnableAutoNumber() {
d.autonumber = true
}
// AddActor creates and adds a new actor to the diagram.
func (d *Diagram) AddActor(id, name string, actorType ActorType) *Actor {
actor := NewActor(id, name, actorType)
d.Actors = append(d.Actors, actor)
return actor
}
// CreateActor adds a new actor to the diagram with a creation message.
func (d *Diagram) CreateActor(creator *Actor, id, name string, actorType ActorType) *Actor {
newActor := NewActor(id, name, actorType)
d.Actors = append(d.Actors, newActor)
d.Messages = append(d.Messages, &Message{
From: creator,
To: newActor,
Type: MessageCreate,
})
return newActor
}
// DestroyActor adds a destroy message for the specified actor.
func (d *Diagram) DestroyActor(actor *Actor) {
d.Messages = append(d.Messages, &Message{
From: nil,
To: actor,
Type: MessageDestroy,
})
}
// AddMessage creates and adds a new message to the diagram.
func (d *Diagram) AddMessage(from, to *Actor, msgType MessageType, text string) *Message {
msg := NewMessage(from, to, msgType, text)
d.Messages = append(d.Messages, msg)
return msg
}
// String generates a Mermaid-formatted string representation of the sequence diagram.
func (d *Diagram) String() string {
var sb strings.Builder
sb.WriteString(baseDiagramType)
if d.autonumber {
sb.WriteString("autonumber\n")
}
for _, actor := range d.Actors {
sb.WriteString(fmt.Sprintf(basediagram.Indentation+"%s %s as %s\n",
actor.Type, actor.ID, actor.Name))
}
for _, message := range d.Messages {
sb.WriteString(message.String(""))
}
return d.BaseDiagram.String(sb.String())
}
// RenderToFile saves the diagram to a file at the specified path.
func (d *Diagram) RenderToFile(path string) error {
return utils.RenderToFile(path, d.String())
}
func (d *Diagram) AddNote(position NotePosition, text string, actors ...*Actor) *Note {
note := newNote(position, text, actors...)
msg := &Message{
Note: note,
}
d.Messages = append(d.Messages, msg)
return note
}
package sequence
import (
"fmt"
"strings"
)
// MessageType represents the different types of messages in a sequence diagram.
type MessageType string
// Predefined message types for sequence diagrams.
const (
// Regular message types
MessageSolid MessageType = "-->" // Solid line
MessageSolidArrow MessageType = "-->>" // Solid line with arrow
MessageDashed MessageType = "-->" // Dashed line (same as solid in Mermaid)
MessageAsync MessageType = "->>" // Async message
MessageDotted MessageType = "-->>>" // Dotted line with arrow
MessageResponse MessageType = "->>" // Response message (typically async)
// Activation/Deactivation arrows
MessageActivate MessageType = "+" // Activation
MessageDeactivate MessageType = "-" // Deactivation
// Special message types for tracking creation/destruction
MessageCreate MessageType = "create"
MessageDestroy MessageType = "destroy"
)
// Base string formats for message elements
const (
baseMessage string = "%s%s%s%s: %s\n" // indent, from, arrow, to, text
baseMessageNoDesc string = "%s%s%s%s\n" // indent, from, arrow, to
baseCreate string = "create "
baseDestroy string = "destroy %s\n"
baseActivate string = "activate %s\n"
baseDeactivate string = "deactivate %s\n"
)
// Message represents a message between actors in a sequence diagram.
type Message struct {
From *Actor
To *Actor
Type MessageType
Text string
Nested []*Message
Note *Note
}
// NewMessage creates a new Message between two actors.
func NewMessage(from, to *Actor, msgType MessageType, text string) *Message {
return &Message{
From: from,
To: to,
Type: msgType,
Text: text,
Nested: make([]*Message, 0),
}
}
// AddNestedMessage creates and adds a new nested message.
func (m *Message) AddNestedMessage(from, to *Actor, msgType MessageType, text string) *Message {
nested := NewMessage(from, to, msgType, text)
m.Nested = append(m.Nested, nested)
return nested
}
// SetType sets the message type and returns the message for chaining.
func (m *Message) SetType(msgType MessageType) *Message {
m.Type = msgType
return m
}
// SetText sets the message text and returns the message for chaining.
func (m *Message) SetText(text string) *Message {
m.Text = text
return m
}
// String generates a Mermaid-formatted string representation of the message with custom indentation.
func (m *Message) String(curIndentation string) string {
var sb strings.Builder
if m.Note != nil {
return m.Note.String(curIndentation)
}
switch m.Type {
case MessageCreate:
if m.Text != "" {
sb.WriteString(fmt.Sprintf("%s\t%s", curIndentation,
fmt.Sprintf(baseMessage, baseCreate, m.From.ID, MessageSolid, m.To.ID, m.Text)))
}
case MessageDestroy:
sb.WriteString(fmt.Sprintf("%s\t%s", curIndentation,
fmt.Sprintf(baseDestroy, m.To.ID)))
case MessageActivate:
if m.Text != "" {
sb.WriteString(fmt.Sprintf("%s\t%s", curIndentation,
fmt.Sprintf(baseMessage, "", m.From.ID, "-->", m.To.ID, m.Text)))
}
sb.WriteString(fmt.Sprintf("%s\t%s", curIndentation,
fmt.Sprintf(baseActivate, m.To.ID)))
case MessageDeactivate:
if m.Text != "" {
sb.WriteString(fmt.Sprintf("%s\t%s", curIndentation,
fmt.Sprintf(baseMessage, "", m.From.ID, MessageSolid, m.To.ID, m.Text)))
}
sb.WriteString(fmt.Sprintf("%s\t%s", curIndentation,
fmt.Sprintf(baseDeactivate, m.To.ID))) // Use To instead of From
default:
arrow := string(m.Type)
if m.Text != "" {
sb.WriteString(fmt.Sprintf("%s\t%s", curIndentation,
fmt.Sprintf(baseMessage, "", m.From.ID, arrow, m.To.ID, m.Text)))
} else {
sb.WriteString(fmt.Sprintf("%s\t%s", curIndentation,
fmt.Sprintf(baseMessageNoDesc, "", m.From.ID, arrow, m.To.ID)))
}
}
if len(m.Nested) > 0 {
nextIndentation := fmt.Sprintf("%s\t", curIndentation)
for _, nested := range m.Nested {
sb.WriteString(nested.String(nextIndentation))
}
}
return sb.String()
}
package sequence
import (
"fmt"
"strings"
)
// NotePosition represents the positioning of a note in a sequence diagram.
type NotePosition string
// Predefined note positioning options.
const (
NoteLeft NotePosition = "left of"
NoteRight NotePosition = "right of"
NoteOver NotePosition = "over"
)
// Base string formats for note elements
const (
baseNoteLeft string = "Note left of %s: %s\n"
baseNoteRight string = "Note right of %s: %s\n"
baseNoteOver string = "Note over %s: %s\n"
baseNoteOverMulti string = "Note over %s,%s: %s\n"
)
// Note represents an annotation or comment in a sequence diagram.
type Note struct {
Position NotePosition
Text string
Actors []*Actor
}
// newNote creates a new Note with the specified properties.
func newNote(position NotePosition, text string, actors ...*Actor) *Note {
return &Note{
Position: position,
Text: text,
Actors: actors,
}
}
// String generates a Mermaid-formatted string representation of the note with custom indentation.
func (n *Note) String(curIndentation string) string {
if len(n.Actors) == 0 {
return ""
}
var sb strings.Builder
switch n.Position {
case NoteLeft:
if len(n.Actors) == 1 {
sb.WriteString(fmt.Sprintf("%s\t%s", curIndentation,
fmt.Sprintf(baseNoteLeft, n.Actors[0].ID, n.Text)))
}
case NoteRight:
if len(n.Actors) == 1 {
sb.WriteString(fmt.Sprintf("%s\t%s", curIndentation,
fmt.Sprintf(baseNoteRight, n.Actors[0].ID, n.Text)))
}
case NoteOver:
if len(n.Actors) == 1 {
sb.WriteString(fmt.Sprintf("%s\t%s", curIndentation,
fmt.Sprintf(baseNoteOver, n.Actors[0].ID, n.Text)))
} else if len(n.Actors) == 2 {
sb.WriteString(fmt.Sprintf("%s\t%s", curIndentation,
fmt.Sprintf(baseNoteOverMulti, n.Actors[0].ID, n.Actors[1].ID, n.Text)))
}
}
return sb.String()
}
package state
import (
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
const (
baseStateConfigurationProperties string = basediagram.Indentation + "state:\n"
statePropertyTitleTopMargin string = "titleTopMargin"
statePropertyArrowMarkerAbsolute string = "arrowMarkerAbsolute"
statePropertyDividerMargin string = "dividerMargin"
statePropertySizeUnit string = "sizeUnit"
statePropertyPadding string = "padding"
statePropertyTextHeight string = "textHeight"
statePropertyTitleShift string = "titleShift"
statePropertyNoteMargin string = "noteMargin"
statePropertyNodeSpacing string = "nodeSpacing"
statePropertyRankSpacing string = "rankSpacing"
statePropertyForkWidth string = "forkWidth"
statePropertyForkHeight string = "forkHeight"
statePropertyMiniPadding string = "miniPadding"
statePropertyFontSizeFactor string = "fontSizeFactor"
statePropertyFontSize string = "fontSize"
statePropertyLabelHeight string = "labelHeight"
statePropertyEdgeLengthFactor string = "edgeLengthFactor"
statePropertyCompositTitleSize string = "compositTitleSize"
statePropertyRadius string = "radius"
statePropertyDefaultRenderer string = "defaultRenderer"
)
// StateConfigurationProperties holds state-specific configuration
type StateConfigurationProperties struct {
basediagram.ConfigurationProperties
properties map[string]basediagram.DiagramProperty
}
func NewStateConfigurationProperties() StateConfigurationProperties {
return StateConfigurationProperties{
ConfigurationProperties: basediagram.NewConfigurationProperties(),
properties: make(map[string]basediagram.DiagramProperty),
}
}
func (c *StateConfigurationProperties) SetTitleTopMargin(v int) *StateConfigurationProperties {
c.properties[statePropertyTitleTopMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyTitleTopMargin,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetArrowMarkerAbsolute(v bool) *StateConfigurationProperties {
c.properties[statePropertyArrowMarkerAbsolute] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyArrowMarkerAbsolute,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetDividerMargin(v int) *StateConfigurationProperties {
c.properties[statePropertyDividerMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyDividerMargin,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetSizeUnit(v int) *StateConfigurationProperties {
c.properties[statePropertySizeUnit] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertySizeUnit,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetPadding(v int) *StateConfigurationProperties {
c.properties[statePropertyPadding] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyPadding,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetTextHeight(v int) *StateConfigurationProperties {
c.properties[statePropertyTextHeight] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyTextHeight,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetTitleShift(v int) *StateConfigurationProperties {
c.properties[statePropertyTitleShift] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyTitleShift,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetNoteMargin(v int) *StateConfigurationProperties {
c.properties[statePropertyNoteMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyNoteMargin,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetNodeSpacing(v int) *StateConfigurationProperties {
c.properties[statePropertyNodeSpacing] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyNodeSpacing,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetRankSpacing(v int) *StateConfigurationProperties {
c.properties[statePropertyRankSpacing] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyRankSpacing,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetForkWidth(v int) *StateConfigurationProperties {
c.properties[statePropertyForkWidth] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyForkWidth,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetForkHeight(v int) *StateConfigurationProperties {
c.properties[statePropertyForkHeight] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyForkHeight,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetMiniPadding(v int) *StateConfigurationProperties {
c.properties[statePropertyMiniPadding] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyMiniPadding,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetFontSizeFactor(v int) *StateConfigurationProperties {
c.properties[statePropertyFontSizeFactor] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyFontSizeFactor,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetFontSize(v int) *StateConfigurationProperties {
c.properties[statePropertyFontSize] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyFontSize,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetLabelHeight(v int) *StateConfigurationProperties {
c.properties[statePropertyLabelHeight] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyLabelHeight,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetEdgeLengthFactor(v string) *StateConfigurationProperties {
c.properties[statePropertyEdgeLengthFactor] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyEdgeLengthFactor,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetCompositTitleSize(v int) *StateConfigurationProperties {
c.properties[statePropertyCompositTitleSize] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyCompositTitleSize,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetRadius(v int) *StateConfigurationProperties {
c.properties[statePropertyRadius] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyRadius,
Val: v,
},
}
return c
}
func (c *StateConfigurationProperties) SetDefaultRenderer(v string) *StateConfigurationProperties {
c.properties[statePropertyDefaultRenderer] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: statePropertyDefaultRenderer,
Val: v,
},
}
return c
}
func (c StateConfigurationProperties) String() string {
var sb strings.Builder
sb.WriteString(c.ConfigurationProperties.String())
if len(c.properties) > 0 {
sb.WriteString(baseStateConfigurationProperties)
for _, prop := range c.properties {
sb.WriteString(prop.Format())
}
}
return sb.String()
}
// Package state provides functionality for creating Mermaid state diagrams
package state
import (
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Base string formats for state diagrams
const (
baseDiagramType string = "stateDiagram-v2\n"
)
// Diagram represents a state diagram with states, transitions, and rendering options.
type Diagram struct {
basediagram.BaseDiagram[StateConfigurationProperties]
States []*State
Transitions []*Transition
}
// NewDiagram creates a new state diagram with default settings.
func NewDiagram() *Diagram {
return &Diagram{
BaseDiagram: basediagram.NewBaseDiagram(NewStateConfigurationProperties()),
States: make([]*State, 0),
Transitions: make([]*Transition, 0),
}
}
// AddState creates and adds a new state to the diagram.
func (d *Diagram) AddState(id, description string, stateType StateType) *State {
state := NewState(id, description, stateType)
d.States = append(d.States, state)
return state
}
// AddTransition creates and adds a new transition between states.
func (d *Diagram) AddTransition(from, to *State, description string) *Transition {
transition := NewTransition(from, to, description)
d.Transitions = append(d.Transitions, transition)
return transition
}
// String generates a Mermaid-formatted string representation of the state diagram.
func (d *Diagram) String() string {
var sb strings.Builder
sb.WriteString(baseDiagramType)
for _, state := range d.States {
sb.WriteString(state.String(""))
}
for _, transition := range d.Transitions {
sb.WriteString(transition.String(""))
}
return d.BaseDiagram.String(sb.String())
}
// RenderToFile saves the diagram to a file at the specified path.
func (d *Diagram) RenderToFile(path string) error {
return utils.RenderToFile(path, d.String())
}
package state
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// StateType represents the different types of states in a state diagram.
type StateType string
// Predefined state types for state diagrams.
const (
StateNormal StateType = "normal"
StateStart StateType = "start"
StateEnd StateType = "end"
StateChoice StateType = "choice"
StateFork StateType = "fork"
StateJoin StateType = "join"
StateComposite StateType = "composite"
)
// Base string formats for state diagram elements
const (
baseStartState string = basediagram.Indentation + "[*] --> %s\n"
baseEndState string = basediagram.Indentation + "%s --> [*]\n"
baseChoiceState string = basediagram.Indentation + "state %s <<choice>>\n"
baseForkState string = basediagram.Indentation + "state %s <<fork>>\n"
baseJoinState string = basediagram.Indentation + "state %s <<join>>\n"
baseNormalState string = basediagram.Indentation + "state %q as %s\n"
baseCompositeStart string = basediagram.Indentation + "state %s {\n"
baseCompositeEnd string = basediagram.Indentation + "}\n"
baseNote string = basediagram.Indentation + "note %s of %s: %s\n"
)
// NotePosition represents the positioning of a note in a state diagram.
type NotePosition string
// Predefined note positions
const (
NoteLeft NotePosition = "left"
NoteRight NotePosition = "right"
)
// Note represents an annotation attached to a state
type Note struct {
Text string
Position NotePosition
}
// State represents a state in a state diagram.
type State struct {
ID string
Description string
Type StateType
Nested []*State
Note *Note
}
// NewState creates a new State with the specified properties.
func NewState(id, description string, stateType StateType) *State {
return &State{
ID: id,
Description: description,
Type: stateType,
Nested: make([]*State, 0),
}
}
// AddNestedState adds a nested state to the current state.
func (s *State) AddNestedState(id, description string, stateType StateType) *State {
nested := NewState(id, description, stateType)
s.Nested = append(s.Nested, nested)
return nested
}
// AddNote adds a note to the state
func (s *State) AddNote(text string, position NotePosition) *State {
s.Note = &Note{
Text: text,
Position: position,
}
return s
}
// String generates a Mermaid-formatted string representation of the state with custom indentation.
func (s *State) String(curIndentation string) string {
var sb strings.Builder
switch s.Type {
case StateStart:
sb.WriteString(fmt.Sprintf("%s%s", curIndentation, fmt.Sprintf(baseStartState, s.ID)))
case StateEnd:
sb.WriteString(fmt.Sprintf("%s%s", curIndentation, fmt.Sprintf(baseEndState, s.ID)))
case StateChoice:
sb.WriteString(fmt.Sprintf("%s%s", curIndentation, fmt.Sprintf(baseChoiceState, s.ID)))
case StateFork:
sb.WriteString(fmt.Sprintf("%s%s", curIndentation, fmt.Sprintf(baseForkState, s.ID)))
case StateJoin:
sb.WriteString(fmt.Sprintf("%s%s", curIndentation, fmt.Sprintf(baseJoinState, s.ID)))
default:
if s.Description != "" {
sb.WriteString(fmt.Sprintf("%s%s", curIndentation, fmt.Sprintf(baseNormalState, s.Description, s.ID)))
}
}
if len(s.Nested) > 0 {
sb.WriteString(fmt.Sprintf("%s%s", curIndentation, fmt.Sprintf(baseCompositeStart, s.ID)))
nextIndentation := fmt.Sprintf("%s ", curIndentation)
for _, nested := range s.Nested {
sb.WriteString(nested.String(nextIndentation))
}
sb.WriteString(fmt.Sprintf("%s%s", curIndentation, baseCompositeEnd))
}
if s.Note != nil {
sb.WriteString(fmt.Sprintf("%s%s", curIndentation,
fmt.Sprintf(baseNote, s.Note.Position, s.ID, s.Note.Text)))
}
return sb.String()
}
package state
import (
"fmt"
)
// TransitionType represents the different types of transitions in a state diagram.
type TransitionType string
// Predefined transition types for state diagrams.
const (
TransitionSolid TransitionType = "solid"
TransitionDashed TransitionType = "dashed"
)
// Base string formats for transition diagram elements
const (
baseTransition string = "%s\t%s --> %s\n"
baseTransitionWithDesc string = "%s\t%s --> %s: %s\n"
terminalState string = "[*]"
)
// Transition represents a transition between states in a state diagram.
type Transition struct {
From *State
To *State
Description string
Type TransitionType
}
// NewTransition creates a new Transition between two states.
func NewTransition(from, to *State, description string) *Transition {
return &Transition{
From: from,
To: to,
Description: description,
Type: TransitionSolid,
}
}
// SetType sets the transition type and returns the transition for chaining
func (t *Transition) SetType(transitionType TransitionType) *Transition {
t.Type = transitionType
return t
}
// String generates a Mermaid-formatted string representation of the transition with custom indentation.
func (t *Transition) String(indentation string) string {
var fromID, toID string
if t.From == nil {
fromID = terminalState
} else {
fromID = t.From.ID
}
if t.To == nil {
toID = terminalState
} else {
toID = t.To.ID
}
if t.Description == "" {
return fmt.Sprintf(baseTransition, indentation, fromID, toID)
}
return fmt.Sprintf(baseTransitionWithDesc, indentation, fromID, toID, t.Description)
}
package timeline
import (
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
const (
baseTimelineConfigurationProperties string = basediagram.Indentation + "timeline\n"
timelinePropertyDisableMulticolor string = "disableMulticolor"
timelinePropertyDiagramMarginX string = "diagramMarginX"
timelinePropertyDiagramMarginY string = "diagramMarginY"
timelinePropertyLeftMargin string = "leftMargin"
timelinePropertyWidth string = "width"
timelinePropertyHeight string = "height"
timelinePropertyPadding string = "padding"
timelinePropertyBoxMargin string = "boxMargin"
timelinePropertyBoxTextMargin string = "boxTextMargin"
timelinePropertyNoteMargin string = "noteMargin"
timelinePropertyMessageMargin string = "messageMargin"
timelinePropertyMessageAlign string = "messageAlign"
timelinePropertyBottomMarginAdj string = "bottomMarginAdj"
timelinePropertyRightAngles string = "rightAngles"
timelinePropertyTaskFontSize string = "taskFontSize"
timelinePropertyTaskFontFamily string = "taskFontFamily"
timelinePropertyTaskMargin string = "taskMargin"
timelinePropertyActivationWidth string = "activationWidth"
timelinePropertyTextPlacement string = "textPlacement"
)
// TimelineConfigurationProperties holds timeline-specific configuration
type TimelineConfigurationProperties struct {
basediagram.ConfigurationProperties
properties map[string]basediagram.DiagramProperty
}
func NewTimeLineConfigurationProperties() TimelineConfigurationProperties {
return TimelineConfigurationProperties{
ConfigurationProperties: basediagram.NewConfigurationProperties(),
properties: make(map[string]basediagram.DiagramProperty),
}
}
func (c *TimelineConfigurationProperties) SetDisableMulticolor(v bool) *TimelineConfigurationProperties {
c.properties[timelinePropertyDisableMulticolor] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyDisableMulticolor,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetDiagramMarginX(v int) *TimelineConfigurationProperties {
c.properties[timelinePropertyDiagramMarginX] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyDiagramMarginX,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetDiagramMarginY(v int) *TimelineConfigurationProperties {
c.properties[timelinePropertyDiagramMarginY] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyDiagramMarginY,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetLeftMargin(v int) *TimelineConfigurationProperties {
c.properties[timelinePropertyLeftMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyLeftMargin,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetWidth(v int) *TimelineConfigurationProperties {
c.properties[timelinePropertyWidth] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyWidth,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetHeight(v int) *TimelineConfigurationProperties {
c.properties[timelinePropertyHeight] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyHeight,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetPadding(v float64) *TimelineConfigurationProperties {
c.properties[timelinePropertyPadding] = &basediagram.FloatProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyPadding,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetBoxMargin(v int) *TimelineConfigurationProperties {
c.properties[timelinePropertyBoxMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyBoxMargin,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetBoxTextMargin(v int) *TimelineConfigurationProperties {
c.properties[timelinePropertyBoxTextMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyBoxTextMargin,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetNoteMargin(v int) *TimelineConfigurationProperties {
c.properties[timelinePropertyNoteMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyNoteMargin,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetMessageMargin(v int) *TimelineConfigurationProperties {
c.properties[timelinePropertyMessageMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyMessageMargin,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetMessageAlign(v string) *TimelineConfigurationProperties {
c.properties[timelinePropertyMessageAlign] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyMessageAlign,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetBottomMarginAdj(v int) *TimelineConfigurationProperties {
c.properties[timelinePropertyBottomMarginAdj] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyBottomMarginAdj,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetRightAngles(v bool) *TimelineConfigurationProperties {
c.properties[timelinePropertyRightAngles] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyRightAngles,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetTaskFontSize(v int) *TimelineConfigurationProperties {
c.properties[timelinePropertyTaskFontSize] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyTaskFontSize,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetTaskFontFamily(v string) *TimelineConfigurationProperties {
c.properties[timelinePropertyTaskFontFamily] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyTaskFontFamily,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetTaskMargin(v float64) *TimelineConfigurationProperties {
c.properties[timelinePropertyTaskMargin] = &basediagram.FloatProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyTaskMargin,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetActivationWidth(v float64) *TimelineConfigurationProperties {
c.properties[timelinePropertyActivationWidth] = &basediagram.FloatProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyActivationWidth,
Val: v,
},
}
return c
}
func (c *TimelineConfigurationProperties) SetTextPlacement(v string) *TimelineConfigurationProperties {
c.properties[timelinePropertyTextPlacement] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: timelinePropertyTextPlacement,
Val: v,
},
}
return c
}
func (c TimelineConfigurationProperties) String() string {
var sb strings.Builder
sb.WriteString(c.ConfigurationProperties.String())
if len(c.properties) > 0 {
sb.WriteString(baseTimelineConfigurationProperties)
for _, prop := range c.properties {
sb.WriteString(prop.Format())
}
}
return sb.String()
}
// Package timeline provides functionality for creating Mermaid timeline diagrams
package timeline
import (
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Base string formats for timeline diagrams
const (
baseDiagramType string = "timeline\n"
)
// Diagram represents a Mermaid timeline diagram
type Diagram struct {
basediagram.BaseDiagram[TimelineConfigurationProperties]
Sections []*Section
}
// NewDiagram creates a new timeline diagram
func NewDiagram() *Diagram {
return &Diagram{
BaseDiagram: basediagram.NewBaseDiagram(NewTimeLineConfigurationProperties()),
Sections: make([]*Section, 0),
}
}
// AddSection creates and adds a new section to the timeline
func (d *Diagram) AddSection(title string) *Section {
section := NewSection(title)
d.Sections = append(d.Sections, section)
return section
}
// String generates the Mermaid syntax for the timeline diagram
func (d *Diagram) String() string {
var sb strings.Builder
sb.WriteString(baseDiagramType)
for _, section := range d.Sections {
sb.WriteString(section.String())
}
return d.BaseDiagram.String(sb.String())
}
// RenderToFile saves the diagram to a file at the specified path
func (d *Diagram) RenderToFile(path string) error {
return utils.RenderToFile(path, d.String())
}
package timeline
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Base string formats for timeline events
const (
eventTitle string = basediagram.Indentation + "%s\n"
eventText string = basediagram.Indentation + ": %s\n"
)
// Event represents a single event in the timeline
type Event struct {
Title string
Text string
SubEvents []*Event
}
// NewEvent creates a new timeline event
func NewEvent(title string, text string) *Event {
return &Event{
Title: title,
Text: text,
}
}
func (e *Event) AddSubEvent(text string) *Event {
subEvent := NewEvent("", text)
e.SubEvents = append(e.SubEvents, subEvent)
return e
}
// String generates the Mermaid syntax for the event
func (e *Event) String() string {
var sb strings.Builder
if e.Title != "" {
sb.WriteString(fmt.Sprintf(eventTitle, e.Title))
}
if e.Text != "" {
sb.WriteString(fmt.Sprintf(eventText, e.Text))
}
for _, subEvent := range e.SubEvents {
sb.WriteString(subEvent.String())
}
return sb.String()
}
package timeline
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Base string formats for timeline sections
const (
baseSectionTitle string = basediagram.Indentation + "section %s\n"
)
// Section represents a section in the timeline diagram
type Section struct {
Title string
Events []*Event
}
// NewSection creates a new timeline section
func NewSection(title string) *Section {
return &Section{
Title: title,
Events: make([]*Event, 0),
}
}
// AddEvent adds a new event to the section
func (s *Section) AddEvent(title string, text string) *Event {
event := NewEvent(title, text)
s.Events = append(s.Events, event)
return event
}
// String generates the Mermaid syntax for the section
func (s *Section) String() string {
var sb strings.Builder
if s.Title != "" {
sb.WriteString(fmt.Sprintf(baseSectionTitle, s.Title))
}
for _, event := range s.Events {
sb.WriteString(event.String())
}
return sb.String()
}
package userjourney
import (
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
const (
baseJourneyConfigurationProperties string = basediagram.Indentation + "journey:\n"
journeyPropertyDiagramMarginX string = "diagramMarginX"
journeyPropertyDiagramMarginY string = "diagramMarginY"
journeyPropertyLeftMargin string = "leftMargin"
journeyPropertyWidth string = "width"
journeyPropertyHeight string = "height"
journeyPropertyBoxMargin string = "boxMargin"
journeyPropertyBoxTextMargin string = "boxTextMargin"
journeyPropertyNoteMargin string = "noteMargin"
journeyPropertyMessageMargin string = "messageMargin"
journeyPropertyMessageAlign string = "messageAlign"
journeyPropertyBottomMarginAdj string = "bottomMarginAdj"
journeyPropertyRightAngles string = "rightAngles"
journeyPropertyTaskFontSize string = "taskFontSize"
journeyPropertyTaskFontFamily string = "taskFontFamily"
journeyPropertyTaskMargin string = "taskMargin"
journeyPropertyActivationWidth string = "activationWidth"
journeyPropertyTextPlacement string = "textPlacement"
journeyPropertyActorColours string = "actorColours"
journeyPropertySectionFills string = "sectionFills"
journeyPropertySectionColours string = "sectionColours"
)
// JourneyConfigurationProperties holds journey-specific configuration
type JourneyConfigurationProperties struct {
basediagram.ConfigurationProperties
properties map[string]basediagram.DiagramProperty
}
func NewJourneyConfigurationProperties() JourneyConfigurationProperties {
return JourneyConfigurationProperties{
ConfigurationProperties: basediagram.NewConfigurationProperties(),
properties: make(map[string]basediagram.DiagramProperty),
}
}
func (c *JourneyConfigurationProperties) SetDiagramMarginX(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyDiagramMarginX] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyDiagramMarginX,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetDiagramMarginY(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyDiagramMarginY] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyDiagramMarginY,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetLeftMargin(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyLeftMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyLeftMargin,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetWidth(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyWidth] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyWidth,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetHeight(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyHeight] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyHeight,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetBoxMargin(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyBoxMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyBoxMargin,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetBoxTextMargin(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyBoxTextMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyBoxTextMargin,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetNoteMargin(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyNoteMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyNoteMargin,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetMessageMargin(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyMessageMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyMessageMargin,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetMessageAlign(v string) *JourneyConfigurationProperties {
c.properties[journeyPropertyMessageAlign] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyMessageAlign,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetBottomMarginAdj(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyBottomMarginAdj] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyBottomMarginAdj,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetRightAngles(v bool) *JourneyConfigurationProperties {
c.properties[journeyPropertyRightAngles] = &basediagram.BoolProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyRightAngles,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetTaskFontSize(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyTaskFontSize] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyTaskFontSize,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetTaskFontFamily(v string) *JourneyConfigurationProperties {
c.properties[journeyPropertyTaskFontFamily] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyTaskFontFamily,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetTaskMargin(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyTaskMargin] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyTaskMargin,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetActivationWidth(v int) *JourneyConfigurationProperties {
c.properties[journeyPropertyActivationWidth] = &basediagram.IntProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyActivationWidth,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetTextPlacement(v string) *JourneyConfigurationProperties {
c.properties[journeyPropertyTextPlacement] = &basediagram.StringProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyTextPlacement,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetActorColours(v []string) *JourneyConfigurationProperties {
c.properties[journeyPropertyActorColours] = &basediagram.StringArrayProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertyActorColours,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetSectionFills(v []string) *JourneyConfigurationProperties {
c.properties[journeyPropertySectionFills] = &basediagram.StringArrayProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertySectionFills,
Val: v,
},
}
return c
}
func (c *JourneyConfigurationProperties) SetSectionColours(v []string) *JourneyConfigurationProperties {
c.properties[journeyPropertySectionColours] = &basediagram.StringArrayProperty{
BaseProperty: basediagram.BaseProperty{
Name: journeyPropertySectionColours,
Val: v,
},
}
return c
}
func (c JourneyConfigurationProperties) String() string {
var sb strings.Builder
sb.WriteString(c.ConfigurationProperties.String())
if len(c.properties) > 0 {
sb.WriteString(baseJourneyConfigurationProperties)
for _, prop := range c.properties {
sb.WriteString(prop.Format())
}
}
return sb.String()
}
// Package userjourney provides functionality for creating Mermaid user journey diagrams
package userjourney
import (
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Base string formats for user journey diagrams
const (
baseDiagramType string = "journey\n"
)
// Diagram represents a Mermaid User Journey diagram
type Diagram struct {
basediagram.BaseDiagram[JourneyConfigurationProperties]
Sections []*Section
}
// NewDiagram creates a new User Journey diagram
func NewDiagram() *Diagram {
return &Diagram{
BaseDiagram: basediagram.NewBaseDiagram(NewJourneyConfigurationProperties()),
Sections: make([]*Section, 0),
}
}
// AddSection adds a new section to the diagram
func (d *Diagram) AddSection(title string) *Section {
section := NewSection(title)
d.Sections = append(d.Sections, section)
return section
}
// String generates the Mermaid syntax for the diagram
func (d *Diagram) String() string {
var sb strings.Builder
sb.WriteString(baseDiagramType)
for _, section := range d.Sections {
sb.WriteString(section.String())
}
return d.BaseDiagram.String(sb.String())
}
// RenderToFile renders the diagram to a file
func (d *Diagram) RenderToFile(path string) error {
return utils.RenderToFile(path, d.String())
}
package userjourney
import (
"fmt"
"strings"
"github.com/TyphonHill/go-mermaid/diagrams/utils/basediagram"
)
// Base string formats for user journey sections and tasks
const (
baseSectionTitle string = basediagram.Indentation + "section %s\n"
baseTaskWithPartic string = basediagram.Indentation + basediagram.Indentation + "%s: %d: %s\n"
baseTaskNoPartic string = basediagram.Indentation + basediagram.Indentation + "%s: %d\n"
)
// Section represents a section in the user journey
type Section struct {
Title string
Tasks []*Task
}
// Task represents a task in a section
type Task struct {
Title string
Score int // Score must be between 1-5
Participants []string // Optional list of participants
}
// NewSection creates a new section
func NewSection(title string) *Section {
return &Section{
Title: title,
Tasks: make([]*Task, 0),
}
}
// AddTask adds a new task to the section
func (s *Section) AddTask(title string, score int, participants ...string) *Task {
if score < 1 {
score = 1
}
if score > 5 {
score = 5
}
task := &Task{
Title: title,
Score: score,
Participants: participants,
}
s.Tasks = append(s.Tasks, task)
return task
}
// String generates the Mermaid syntax for the section
func (s *Section) String() string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf(baseSectionTitle, s.Title))
for _, task := range s.Tasks {
if len(task.Participants) > 0 {
sb.WriteString(fmt.Sprintf(baseTaskWithPartic,
task.Title,
task.Score,
strings.Join(task.Participants, ",")))
} else {
sb.WriteString(fmt.Sprintf(baseTaskNoPartic, task.Title, task.Score))
}
}
return sb.String()
}
package basediagram
import (
"fmt"
"strings"
)
const Indentation = " "
const (
baseDiagramSeparator = "---\n"
baseDiagramTitle = "title: %s\n"
)
type BaseDiagram[T DiagramProperties] struct {
Title string
Config T
MarkdownFencer
}
func NewBaseDiagram[T DiagramProperties](config T) BaseDiagram[T] {
return BaseDiagram[T]{
Title: "",
Config: config,
MarkdownFencer: NewMarkdownFencer(),
}
}
func (d *BaseDiagram[T]) SetTitle(title string) *BaseDiagram[T] {
d.Title = title
return d
}
func (d *BaseDiagram[T]) String(content string) string {
var sb strings.Builder
sb.WriteString(baseDiagramSeparator)
if d.Title != "" {
sb.WriteString(fmt.Sprintf(baseDiagramTitle, d.Title))
}
sb.WriteString(d.Config.String())
sb.WriteString(baseDiagramSeparator)
sb.WriteString(content)
return d.MarkdownFencer.WrapWithFence(sb.String())
}
package basediagram
import (
"fmt"
"strings"
)
type ConfigurationProperties struct {
Theme
maxTextSize int
maxEdges int
fontSize int
}
const (
configPropertyBase = "config:\n"
configPropertyMaxTextSize = "%smaxTextSize: %d\n"
configPropertyMaxEdges = "%smaxEdges: %d\n"
configPropertyFontSize = "%sfontSize: %d\n"
)
func NewConfigurationProperties() ConfigurationProperties {
return ConfigurationProperties{
Theme: Theme{
Name: ThemeDefault,
},
maxTextSize: 50000,
maxEdges: 500,
fontSize: 16,
}
}
func (c *ConfigurationProperties) SetMaxTextSize(maxTextSize int) *ConfigurationProperties {
c.maxTextSize = maxTextSize
return c
}
func (c *ConfigurationProperties) SetMaxEdges(maxEdges int) *ConfigurationProperties {
c.maxEdges = maxEdges
return c
}
func (c *ConfigurationProperties) SetFontSize(fontSize int) *ConfigurationProperties {
c.fontSize = fontSize
return c
}
func (c *ConfigurationProperties) String() string {
var sb strings.Builder
sb.WriteString(configPropertyBase)
sb.WriteString(c.Theme.String())
sb.WriteString(fmt.Sprintf(configPropertyMaxTextSize, Indentation, c.maxTextSize))
sb.WriteString(fmt.Sprintf(configPropertyMaxEdges, Indentation, c.maxEdges))
sb.WriteString(fmt.Sprintf(configPropertyFontSize, Indentation, c.fontSize))
return sb.String()
}
package basediagram
import "strings"
const (
markdownFenceStart = "```mermaid\n"
markdownFenceEnd = "\n```\n"
)
// MarkdownFencer provides common functionality for handling markdown fence state
type MarkdownFencer struct {
markdownFence bool
}
func NewMarkdownFencer() MarkdownFencer {
return MarkdownFencer{
markdownFence: false,
}
}
// EnableMarkdownFence enables markdown fence in output and returns receiver for chaining
func (m *MarkdownFencer) EnableMarkdownFence() *MarkdownFencer {
m.markdownFence = true
return m
}
// DisableMarkdownFence disables markdown fence in output
func (m *MarkdownFencer) DisableMarkdownFence() {
m.markdownFence = false
}
// IsMarkdownFenceEnabled returns current markdown fence state
func (m *MarkdownFencer) IsMarkdownFenceEnabled() bool {
return m.markdownFence
}
// WrapWithFence wraps content with markdown fence if enabled
func (m *MarkdownFencer) WrapWithFence(content string) string {
if !m.markdownFence {
return content
}
var sb strings.Builder
sb.WriteString(markdownFenceStart)
sb.WriteString(content)
sb.WriteString(markdownFenceEnd)
return sb.String()
}
package basediagram
import (
"fmt"
"strings"
)
// DiagramProperty represents a configurable diagram property
type DiagramProperty interface {
Format() string
Value() interface{}
}
// BaseProperty provides common functionality for diagram properties
type BaseProperty struct {
Name string
Val interface{}
}
func (p *BaseProperty) Format() string {
return fmt.Sprintf(Indentation+Indentation+"%s: %v\n", p.Name, p.Val)
}
// Add this method to BaseProperty
func (p *BaseProperty) Value() interface{} {
return p.Val
}
// Common property types
type BoolProperty struct {
BaseProperty
}
type IntProperty struct {
BaseProperty
}
type FloatProperty struct {
BaseProperty
}
type StringProperty struct {
BaseProperty
}
type StringArrayProperty struct {
BaseProperty
}
func (p *StringArrayProperty) Format() string {
vals := p.Val.([]string)
quotedVals := make([]string, len(vals))
for i, v := range vals {
quotedVals[i] = fmt.Sprintf("%q", v)
}
return fmt.Sprintf(Indentation+Indentation+"%s: [%s]\n", p.Name, strings.Join(quotedVals, ", "))
}
type DiagramProperties interface {
String() string
}
package basediagram
import (
"fmt"
"strings"
)
type ThemeName string
type Theme struct {
Name ThemeName
Variables map[string]interface{}
}
const (
baseThemeString = Indentation + "theme: %s\n"
themeVariableString = Indentation + Indentation + "%s: %v\n"
)
const (
ThemeDefault ThemeName = "default"
ThemeNeutral ThemeName = "neutral"
ThemeDark ThemeName = "dark"
ThemeForest ThemeName = "forest"
ThemeBase ThemeName = "base"
)
const (
ThemeVarDarkMode = "darkMode"
ThemeVarBackground = "background"
ThemeVarFontFamily = "fontFamily"
ThemeVarFontSize = "fontSize"
ThemeVarPrimaryColor = "primaryColor"
ThemeVarPrimaryTextColor = "primaryTextColor"
ThemeVarSecondaryColor = "secondaryColor"
ThemeVarPrimaryBorderColor = "primaryBorderColor"
ThemeVarTertiaryColor = "tertiaryColor"
ThemeVarNoteBkgColor = "noteBkgColor"
ThemeVarNoteTextColor = "noteTextColor"
ThemeVarNoteBorderColor = "noteBorderColor"
ThemeVarLineColor = "lineColor"
ThemeVarTextColor = "textColor"
ThemeVarMainBkg = "mainBkg"
ThemeVarErrorBkgColor = "errorBkgColor"
ThemeVarErrorTextColor = "errorTextColor"
)
func NewTheme() Theme {
return Theme{
Name: ThemeDefault,
Variables: make(map[string]interface{}),
}
}
func (t *Theme) SetTheme(name ThemeName) *Theme {
t.Name = name
return t
}
func (t *Theme) SetDarkMode(v bool) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarDarkMode] = v
return t
}
func (t *Theme) SetBackground(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarBackground] = v
return t
}
func (t *Theme) SetFontFamily(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarFontFamily] = v
return t
}
func (t *Theme) SetFontSize(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarFontSize] = v
return t
}
func (t *Theme) SetPrimaryColor(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarPrimaryColor] = v
return t
}
func (t *Theme) SetPrimaryTextColor(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarPrimaryTextColor] = v
return t
}
func (t *Theme) SetSecondaryColor(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarSecondaryColor] = v
return t
}
func (t *Theme) SetPrimaryBorderColor(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarPrimaryBorderColor] = v
return t
}
func (t *Theme) SetTertiaryColor(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarTertiaryColor] = v
return t
}
func (t *Theme) SetNoteBkgColor(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarNoteBkgColor] = v
return t
}
func (t *Theme) SetNoteTextColor(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarNoteTextColor] = v
return t
}
func (t *Theme) SetNoteBorderColor(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarNoteBorderColor] = v
return t
}
func (t *Theme) SetLineColor(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarLineColor] = v
return t
}
func (t *Theme) SetTextColor(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarTextColor] = v
return t
}
func (t *Theme) SetMainBkg(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarMainBkg] = v
return t
}
func (t *Theme) SetErrorBkgColor(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarErrorBkgColor] = v
return t
}
func (t *Theme) SetErrorTextColor(v string) *Theme {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
}
t.Variables[ThemeVarErrorTextColor] = v
return t
}
func (t *Theme) String() string {
if len(t.Variables) == 0 {
return fmt.Sprintf(baseThemeString, t.Name)
}
var sb strings.Builder
sb.WriteString(fmt.Sprintf(baseThemeString, t.Name))
sb.WriteString(Indentation + "themeVariables:\n")
for k, v := range t.Variables {
sb.WriteString(fmt.Sprintf(themeVariableString, k, v))
}
return sb.String()
}
package utils
import (
"fmt"
"os"
"path/filepath"
)
// RenderToFile writes content to a file, handling directory creation
func RenderToFile(path string, content string) error {
// Create directory if needed
dir := filepath.Dir(path)
if dir != "" {
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
}
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
return fmt.Errorf("failed to write file: %w", err)
}
return nil
}
package utils
import "fmt"
// IDGenerator defines the interface for generating unique IDs
type IDGenerator interface {
NextID() string
}
// DefaultIDGenerator provides a simple incremental ID generator
type DefaultIDGenerator struct {
nextID int
}
// NewIDGenerator creates a new DefaultIDGenerator
func NewIDGenerator() *DefaultIDGenerator {
return &DefaultIDGenerator{nextID: 0}
}
// NextID generates the next unique ID
func (g *DefaultIDGenerator) NextID() string {
id := g.nextID
g.nextID++
return fmt.Sprintf("%d", id)
}
// Reset resets the ID generator to its initial state
func (g *DefaultIDGenerator) Reset() *DefaultIDGenerator {
g.nextID = 0
return g
}
package testutils
import (
"os"
"strings"
"testing"
)
// TestFile represents a temporary test file
type TestFile struct {
Path string
}
// CreateTempFile creates a temporary file for testing
func CreateTempFile(t *testing.T, prefix string) *TestFile {
t.Helper()
f, err := os.CreateTemp("", prefix)
if err != nil {
t.Fatalf("Failed to create temp file: %v", err)
}
f.Close()
return &TestFile{Path: f.Name()}
}
// Cleanup removes the temporary file
func (tf *TestFile) Cleanup(t *testing.T) {
t.Helper()
if err := os.Remove(tf.Path); err != nil {
t.Errorf("Failed to remove temp file: %v", err)
}
}
// AssertFileContent checks if file content matches expected content
func AssertFileContent(t *testing.T, path string, want string) {
t.Helper()
content, err := os.ReadFile(path)
if err != nil {
t.Fatalf("Failed to read file: %v", err)
}
if got := string(content); got != want {
t.Errorf("File content mismatch:\nwant:\n%s\ngot:\n%s", want, got)
}
}
// AssertContains checks if output contains all expected strings
func AssertContains(t *testing.T, output string, wants ...string) {
t.Helper()
for _, want := range wants {
if !strings.Contains(output, want) {
t.Errorf("Output missing expected content %q in:\n%s", want, output)
}
}
}