Skip to content

Commit

Permalink
Merge pull request #14 from ddddddO/mermaid
Browse files Browse the repository at this point in the history
mermaid対応
  • Loading branch information
ddddddO authored Feb 15, 2022
2 parents 30c691d + 183caa6 commit 81448e9
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 4 deletions.
113 changes: 112 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# gdag
Easily manage 🕸DAG🕷 with Go.<br>
DAG is an acronym for Directed Acyclic Graph.<br>
Output is in PlantUML format.<br>
Output is in PlantUML or Mermaid format.<br>
Useful for progressing tasks.

⚠It is incompatible with v0.2.0 and earlier versions⚠
Expand Down Expand Up @@ -104,6 +104,117 @@ end note
![image](dag.svg)


## Mermaidjs

1. `go run main.go`

```go
package main

import (
"fmt"

g "github.com/ddddddO/gdag"
)

func main() {
var dag *g.Node = g.DAG("ゴール(目的)")

var design *g.Node = g.Task("設計")
reviewDesign := g.Task("レビュー対応")

developFeature1 := g.Task("feature1開発")
developFeature1.Note("noop")
reviewDevelopFeature1 := g.Task("レビュー対応")

developFeature2 := g.Task("feature2開発")
developFeature2.Note("noop")
reviewDevelopFeature2 := g.Task("レビュー対応")

prepareInfra := g.Task("インフラ準備")
prepareInfra.Note("noop")

test := g.Task("結合テスト")
release := g.Task("リリース")
finish := g.Task("finish")

dag.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test)
reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test)
reviewDesign.Con(prepareInfra).Con(test)
test.Con(release).Con(finish)

g.Done(design, reviewDesign, developFeature2, finish)

mermaid, err := dag.Mermaid()
if err != nil {
panic(err)
}
fmt.Println(mermaid)
}

```

```
graph TD
classDef doneColor fill:#868787
67("ゴール(目的)")
68(["設計"]):::doneColor
69(["レビュー対応"]):::doneColor
70(["feature1開発"])
71(["レビュー対応"])
75(["結合テスト"])
76(["リリース"])
77(["finish"]):::doneColor
72(["feature2開発"]):::doneColor
73(["レビュー対応"])
74(["インフラ準備"])
67 --> 68
68 --> 69
69 --> 70
70 --> 71
71 --> 75
75 --> 76
76 --> 77
69 --> 72
72 --> 73
73 --> 75
69 --> 74
74 --> 75
```

2. rendering

```mermaid
graph TD
classDef doneColor fill:#868787
67("ゴール(目的)")
68(["設計"]):::doneColor
69(["レビュー対応"]):::doneColor
70(["feature1開発"])
71(["レビュー対応"])
75(["結合テスト"])
76(["リリース"])
77(["finish"]):::doneColor
72(["feature2開発"]):::doneColor
73(["レビュー対応"])
74(["インフラ準備"])
67 --> 68
68 --> 69
69 --> 70
70 --> 71
71 --> 75
75 --> 76
76 --> 77
69 --> 72
72 --> 73
73 --> 75
69 --> 74
74 --> 75
```


## CheckList

1. `go run main.go`
Expand Down
8 changes: 5 additions & 3 deletions gdag.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ type Node struct {
// の予定だったが、自動で生成する。連番。たぶん他の人がumlいじることはないとも思う。
// せめて、連番ではなく、置換しやすいように少し長めのユニークなIDにしたほうがいいかも。テストできそうかも考えて実装した方が良さそう。
// かつ、ソータブルな値が必須(ソートして使っているところがあるため)
as int
note string
color string // done: #DarkGray
as int // mermaidjsの識別子としても利用する
note string
color string // done: #DarkGray
colorMermaid string // done: doneColor

upstream []*Node
downstream []*Node
Expand Down Expand Up @@ -54,6 +55,7 @@ const (
func Done(nodes ...*Node) {
for _, n := range nodes {
n.color = colorDone
n.colorMermaid = colorDoneMermaid
}
}

Expand Down
92 changes: 92 additions & 0 deletions gdag_mermaid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package gdag

import (
"fmt"
)

const (
colorDoneMermaid = "doneColor"
)

type mermaidGenerator struct {
uniqueC map[int]struct{}
uniqueR map[string]struct{}
}

func newMermaidGenerator() *mermaidGenerator {
return &mermaidGenerator{
uniqueC: map[int]struct{}{},
uniqueR: map[string]struct{}{},
}
}

// Mermaid outputs dag mermaidjs.
func (start *Node) Mermaid() (string, error) {
mg := newMermaidGenerator()

ret := "graph TD" + "\n"
ret += " classDef doneColor fill:#868787" + "\n"
ret += mg.generateComponents(start) + "\n"
ret += mg.generateRelations(start) + "\n"
return ret, nil
}

func (mg *mermaidGenerator) generateComponents(start *Node) string {
return mg.generateComponent(start)
}

func (mg *mermaidGenerator) generateComponent(n *Node) string {
if _, ok := mg.uniqueC[n.as]; ok {
return ""
}
mg.uniqueC[n.as] = struct{}{}

dst := ""
// TODO: mermaidjs用に修正するかどうか。リファクタは必要
switch n.nodeType {
case rectangle:
s := fmt.Sprintf(" %d(\"%s\")", n.as, n.text)
if len(n.colorMermaid) != 0 {
s += fmt.Sprintf(":::%s", n.colorMermaid)
}
s += "\n"

dst += s
case usecase:
s := fmt.Sprintf(" %d([\"%s\"])", n.as, n.text)
if len(n.colorMermaid) != 0 {
s += fmt.Sprintf(":::%s", n.colorMermaid)
}
s += "\n"

dst += s
}
if len(n.note) != 0 {
// noop
}

for _, d := range n.downstream {
dst += mg.generateComponent(d)
}

return dst
}

func (mg *mermaidGenerator) generateRelations(start *Node) string {
return mg.generateRelation(start, "")
}

func (mg *mermaidGenerator) generateRelation(n *Node, out string) string {
r := fmt.Sprintf("%d --> ", n.as)
for _, d := range n.downstream {
key := fmt.Sprintf("%d-%d", n.as, d.as)
if _, ok := mg.uniqueR[key]; ok {
continue
}
mg.uniqueR[key] = struct{}{}

tmp := fmt.Sprintf(" %s%d\n", r, d.as)
out += mg.generateRelation(d, tmp)
}
return out
}
63 changes: 63 additions & 0 deletions gdag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,3 +417,66 @@ func ExampleMultipleCheckLists() {
// - [ ] リリース
// - [x] finish
}

func ExampleMermaid() {
var dag *g.Node = g.DAG("ゴール(目的)")

var design *g.Node = g.Task("設計")
reviewDesign := g.Task("レビュー対応")

developFeature1 := g.Task("feature1開発")
developFeature1.Note("noop")
reviewDevelopFeature1 := g.Task("レビュー対応")

developFeature2 := g.Task("feature2開発")
developFeature2.Note("noop")
reviewDevelopFeature2 := g.Task("レビュー対応")

prepareInfra := g.Task("インフラ準備")
prepareInfra.Note("noop")

test := g.Task("結合テスト")
release := g.Task("リリース")
finish := g.Task("finish")

dag.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test)
reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test)
reviewDesign.Con(prepareInfra).Con(test)
test.Con(release).Con(finish)

g.Done(design, reviewDesign, developFeature2, finish)

mermaid, err := dag.Mermaid()
if err != nil {
panic(err)
}
fmt.Println(mermaid)
// Output:
// graph TD
// classDef doneColor fill:#868787
// 67("ゴール(目的)")
// 68(["設計"]):::doneColor
// 69(["レビュー対応"]):::doneColor
// 70(["feature1開発"])
// 71(["レビュー対応"])
// 75(["結合テスト"])
// 76(["リリース"])
// 77(["finish"]):::doneColor
// 72(["feature2開発"]):::doneColor
// 73(["レビュー対応"])
// 74(["インフラ準備"])
//
// 67 --> 68
// 68 --> 69
// 69 --> 70
// 70 --> 71
// 71 --> 75
// 75 --> 76
// 76 --> 77
// 69 --> 72
// 72 --> 73
// 73 --> 75
// 69 --> 74
// 74 --> 75

}

0 comments on commit 81448e9

Please sign in to comment.