これからのText to DiagramツールはD2で決まり！

## この記事について

ドメイン固有言語(DSL: Domain-Specific Language)の1つであり、Declarative Diagrammingの略であるD2言語の環境構築、および簡単な作図例を示したものです。

## D2とは

2022年11月22日にオープンソースとなったドメイン固有言語です。MPL-2.0ライセンスの元公開されています。

https://github.com/terrastruct/d2

Mermaid, Graphviz, PlantUMLとの比較を行えるサイトもD2の開発元であるTerrastructによって公開されています。

https://text-to-diagram.com/

一部抜粋したものが次の表になります。
他のText to Diagramツールと比較して、D2では様々な機能が追加されていることが分かります。

|  特徴  |  D2  |  PlantUML  |  MermaidJS  |  GraphViz  |
| ---- | ---- | ---- | ---- | ---- |
|  エラーメッセージ　<br> ただ単に"syntax error"とだけ表示しない |  〇  |  ×  |  ×  | × |
| メンテナンスの継続性 | 〇 | 〇 | 〇 | サポートは限定的で終了する可能性 |
| 図とアイコンの挿入 | どのURLでも使用可能 | × | FontAwesome <br> imgタグ | × |
| シーケンス図 | 試験的 | 〇 | 〇 | × |
| SQLテーブル <br> ER図への対応 | 〇 | 〇 | 〇 | × |
| マークダウン記法 | 〇 | × | × | × |
| コードスニペット | 〇 | × | × | × |
| オートフォーマット | 〇 | × | × | × |

特にD2におけるエラーメッセージは非常に分かりやすく、
次のように図形に表示する語句を空白にしたままコンパイルを行うと

```plaintext:in.d2
test: 
```

このようにエラーの発生する箇所を行数及び行頭からの文字数、また具体的なエラーの内容まで教えてくれます。

```plaintext
$ d2 --watch in.d2
[11:11:49] success: listening on http://127.0.0.1:50817
[11:11:49] info: compiling in.d2...
[11:11:49] err: failed to compile: 1:5: missing value after colon
```

このエラーの内容は複数個所存在する場合は一度に表示してくれます。

```plaintext:in.d2
test1: 
test2:
```

```plaintext
[11:18:29] err: failed to recompile: 1:6: missing value after colon
[11:18:29] err: 2:6: missing value after colon
```


## 環境構築

~~2022年11月28日時点では、Windows向けの環境構築方法の公開、及びDocker Imageの公式配布はありません。~~

:::note info
2022年12月7日にリリースされたv0.1.0にてWindowsへのサポートが対応されました！
:::

今回は2通りの環境構築を行っていますが、作図例で用いた環境は次の通りです。

- `Windows 11 Home 22H2`
- `Git Bash`
- `D2 v0.1.1`

### WSL2でのインストール
WSL2の環境構築に関しては他の方の記事を参考にしてください。

https://qiita.com/whim0321/items/ed76b490daaec152dc69

https://qiita.com/na-777/items/7ead86b723c683346eba

インストール自体は次のコマンドで行えます。
```bash:
$ curl -fsSL https://d2lang.com/install.sh | sh -s --
```

インストールの挙動が気になる方はdry-runを行ってみても良いでしょう。

```bash:
$ curl -fsSL https://d2lang.com/install.sh | sh -s -- --dry-run
```

#### インストールされたか確認

```bash:
$ d2 --version
v0.1.1
```
バージョン情報が表示されればインストール成功です。

### Git Bashでのインストール
:::note info
2022年12月7日にリリースされたv0.1.0にてサポートされました！
:::

Git Bashのインストールに関しては他の方の記事を参考にしてください。

https://qiita.com/suke_masa/items/404f06309bb32ca6c9c5

https://qiita.com/HyunwookPark/items/d399f6959fc922a15ee1

今回は2022年12月8日にリリースされたv0.1.1を用いてみます。
こちらから環境に適したアセットをダウンロードしましょう。

https://github.com/terrastruct/d2/releases/tag/v0.1.1

適当なディレクトリに配置しtarコマンドで解凍します。

```bash:
$ ls
d2-v0.1.1-windows-amd64.tar.gz

$ tar -zxvf d2-v0.1.1-windows-amd64.tar.gz
d2-v0.1.1/
d2-v0.1.1/man/
d2-v0.1.1/bin/
d2-v0.1.1/Makefile
d2-v0.1.1/README.md
d2-v0.1.1/scripts/
d2-v0.1.1/LICENSE.txt
d2-v0.1.1/scripts/uninstall.sh
d2-v0.1.1/scripts/install.sh
d2-v0.1.1/scripts/lib.sh
d2-v0.1.1/bin/d2.exe
d2-v0.1.1/man/d2.1

```

あとはbinディレクトリに対してpathを通してあげれば完了です。

#### インストールされたか確認

```bash:
$ d2 --version
v0.1.1
```
バージョン情報が表示されればインストール成功です。

## 動かしてみる

[公式でチュートリアル](https://github.com/terrastruct/d2#quickstart)が用意されています。
次のコマンドを叩くことで、自動的にブラウザが立ち上がりsvgが表示されます。

```bash:
$ echo 'x -> y -> z' > in.d2
$ d2 --watch in.d2 out.svg
```

<img width="200px" alt="out.svg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/e5bed75e-974d-36b6-2bd3-d5b653a36648.png">

今回はd2ファイルに対してコマンドを挿入して、out.svgとして出力しましたが、

```bash:
$ d2 --watch input.d2
```

とした上で、input.d2ファイルを編集することで、自動的に図を作成、表示までしてくれます。

### VSCodeで動かしてみる

:::note info
2022年12月14日現在、シンタックスハイライトのみサポートされています
:::

またVSCodeでは[公式から拡張機能](https://github.com/terrastruct/d2-vscode)が提供されています。
拡張機能のパネルから、d2と入力しTerrastructが出しているものをインストールしましょう。
![d2-extension.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/da9102d3-c76e-c848-362b-1269691a163a.png)
ターミナルを起動し先ほどと同じように`d2 --watch input.d2`を叩くことで、input.d2ファイルを更新する度にブラウザに表示される図が更新されていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/29ca6054-15eb-db83-5e17-1dedbc92bac4.png)

## 図形の種類

D2ではデフォルトで様々な形の図形が用意されています。

https://d2lang.com/tour/shapes

図形の指定は図形を定義したうえで、shapeフィールドを指定します。

```plaintext
object: 長方形 # objectという名前の図形を定義し「長方形」と表示する
object.shape: rectangle # objectの形をrectangle(長方形)として指定
```
ただしデフォルトで長方形が指定されているため、長方形を用いる場合は指定する必要はありません。
公式ドキュメントでは一部のみ掲載されているため、利用可能な全種類の図形を表示しておきます。

### 長方形

```plaintext
rect: rectangle
rect.shape: rectangle
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/883b69a2-3d8e-3505-bb98-a8ca0b3359da.png)


### 正方形

```plaintext
squa: square
squa.shape: square
```
![square.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/8554afc9-650b-aa2a-d262-6c7e9eb0f0ca.png)

### ページ

```plaintext
page: page
page.shape: page
```
![page.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/45683328-8684-d8c3-806d-0d3de54399c7.png)


### 平行四辺形

```plaintext
paral: parallelogram
paral.shape: parallelogram
```
![parallelogram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/de45599e-ce1e-156f-a989-f14aba95a2e0.png)


### ドキュメント

```plaintext
docu: document
docu.shape: document
```
![document.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/3190615d-9ca7-4393-c1cd-50744043c8de.png)


### 円柱(縦)

```plaintext
cyli: cylinder
cyli.shape: cylinder
```
![cylinder.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/85ffbfd6-61bc-3beb-6f91-15ffeb516465.png)

### 円柱(横)

```plaintext
que: queue
que.shape: queue
```
![queue.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/1b508e98-1eee-ca71-b7c4-ba59abc72a70.png)

### パッケージ

```plaintext
pack: package
pack.shape: package
```
![package.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/0b5ac243-b664-d769-c14e-a270bdce1be9.png)


### ステップ

```plaintext
step: step
step.shape: step
```
![step.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/f041d061-da20-304d-0a89-9599411852bc.png)

### 吹き出し

```plaintext
call: callout
call.shape: callout
```
![callout.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/9b2b0ccb-c1aa-e5ad-63ef-3e39adbf8de7.png)


### データストア
```plaintext
stdt: stored_data
stdt.shape: stored_data
```
![stored_data.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/6c94d093-7460-4124-af90-ceec0d4d5cee.png)

### 人型
```plaintext
pers: person
pers.shape: person
```
![person.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/64d6312d-1222-cbd6-2e60-5333aaaf5982.png)

### ひし形
```plaintext
dia: diamond
dia.shape: diamond
```
![diamond.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/b924b031-eea7-c912-b5f6-a91ac0880112.png)

### 楕円
```plaintext
oval: oval
oval.shape: oval
```
![oval.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/7542d42a-b6a2-b586-b8cb-c81bb8e5fe47.png)

### 円
```plaintext
circ: circle
circ.shape: circle
```
![circle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/3322d427-451c-9faa-b0cf-22b0fd480812.png)

### 六角形
```plaintext
hex: hexagon
hex.shape: hexagon
```
![hexagon.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/638aefeb-fe93-d841-fc0c-752bf56de54a.png)

### クラウド
```plaintext
cloud: cloud
cloud.shape: cloud
```
![cloud.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/37decba0-0b92-e948-d2a1-0c758de12d3d.png)

### その他

このほかにも特殊な図形として

1. text: md形式で文章を追加可能
1. code: md形式をコードブロックに変換して配置可能
1. class: UML図を記述することが可能
1. sql_table: ER図を記述することが可能
1. image: アイコンや画像の挿入が可能
1. sequence_diagram: シーケンス図を記述することが可能

が定義されています。

:::note info
v0.1.0にてシーケンス図が試験的に導入されました！
:::

## 作図例
### フローチャート

まずは簡単なフローチャートを作図してみました。
直感的で分かりやすく記述することが可能ですね。

```plaintext
start: 開始
start.shape: oval
if: 条件
if.shape: diamond
process: 処理
process.shape: rectangle
exception: 例外処理
exception.shape: rectangle
end: 終了
end.shape: oval

start -> if
if -> process: true
if -> exception: false
process -> end
exception -> end
```

![flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/d5964739-dcff-aa26-082e-0ebbffaa4f3d.png)

### UML図

次にUML図に加え、D2内で使用可能なmarkdownでの文章の記述と、コードブロックでのコードの記述も試してみました。

```plaintext
SuperClass: {
    shape: class

    -testStatus: String
    \#testId: String

    +getStatus(String): String
}

SubClass: {
    shape: class

    \#piyo: String

    \#getHoge(int): Class<?>
    setHuga(T)
}

SuperClass <- SubClass

SuperClassExplanation: |md
    # test
    - If you write in English, it will display nicely.
    - In v0.0.13, the background of here is black due to the dark mode. This problem will be fixed next release.
    - [render: consistent md render regardless of system dark mode](https://github.com/terrastruct/d2/pull/211)
| {
    near: SuperClass
}

JapaneseExplanation: |md
    日本語のテストです、現時点だと欠けちゃいます
|{ 
    near: SubClass
}

SubClassCode: |java
    public class SubClass extends SuperClass {
        protected String piyo;

        protected Class<?> getHoge(int aaa) {

        }

        private void setHuga(T) {

        }
    }
| {
    near: SubClass
}
```

![UML2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/d7469f87-18fc-e3a0-66dd-14a1a7453952.png)

:::note warn
v0.1.0となり'near'はTALAレイアウトエンジンでのみ動作する記述が追加されました <br> 他のレイアウトエンジンでも動作するよう開発中だそうです
:::

:::note warn
執筆時点でissueへの投稿は見られませんでしたが、
日本語で記述した部分に関しては見切れてしまうようです
:::

### ER図

次のER図は[公式ドキュメント](https://d2lang.com/tour/sql-tables)で用意されているサンプルになります。
先に紹介した図形と併用して利用することも可能です。

```plaintext
cloud: {
  disks: {
    shape: sql_table
    id: int {constraint: primary_key}
  }
  blocks: {
    shape: sql_table
    id: int {constraint: primary_key}
    disk: int {constraint: foreign_key}
    blob: blob
  }
  blocks.disk -> disks.id

  AWS S3 Vancouver -> disks
}
```

![ER.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/270ac69a-6177-bd8a-1d41-1f8a2f72a0a5.png)

### シーケンス図

次のシーケンス図は[公式ドキュメント](https://d2lang.com/tour/sequence-diagrams)で用意されているサンプルになります。
実線や点線のカスタマイズも容易に行えますね。

```plaintext
shape: sequence_diagram
scorer: { shape: person }
scorer.t -> itemResponse.t: getItem()
scorer.t <- itemResponse.t: item {
    stroke-dash: 5
}

scorer.t -> item.t1: getRubric()
scorer.t <- item.t1: rubric {
    stroke-dash: 5
}

scorer.t -> essayRubric.t: applyTo(essayResp)
itemResponse -> essayRubric.t.c
essayRubric.t.c -> concept.t: match(essayResponse)
scorer <- essayRubric.t: score {
    stroke-dash: 5
}

scorer.t -> itemOutcome.t1: new
scorer.t -> item.t2: getNormalMinimum()
scorer.t -> item.t3: getNormalMaximum()

scorer.t -> itemOutcome.t2: setScore(score)
scorer.t -> itemOutcome.t3: setFeedback(missingConcepts)
```

![SequenceDiagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230034/33082a8c-60cd-a294-f46d-164ab463b730.png)


## 終わりに
D2はv0.1.0からWindowsでも容易に利用可能になり、現在も活発に開発されている言語です。
執筆中もリリースが行われ、バグ修正が行われていました。
言語としては直感的で分かりやすく、利用シーンは多々ありそうですね。
最後までお読みいただきありがとうございました。