🌲

Goでtreeを表現する

3 min read

どういうこと?

Linuxのtreeコマンド結果のような出力をGoのソースコードで実現できるパッケージを作りました(その宣伝です)。
この記事を公開する前に「Markdown形式の入力からtreeを出力するCLI」という記事を書きました。こちらの記事で紹介しているCLIのコードを流用しています。

デモ

まずは見て頂くのが早いと思うので以下にサンプルを載せます。

package main

import (
	"os"

	"github.com/ddddddO/gtree/v6"
)

func main() {
	var root *gtree.Node
	root = gtree.NewRoot("root")
	root.Add("child 1").Add("child 2")
	root.Add("child 1").Add("child 3")
	child4 := root.Add("child 4")

	var child7 *gtree.Node
	child7 = child4.Add("child 5").Add("child 6").Add("child 7")
	child7.Add("child 8")

	if err := gtree.ExecuteProgrammably(os.Stdout, root); err != nil {
		panic(err)
	}
}

上記の実行結果が以下です。

root
├── child 1
│   ├── child 2
│   └── child 3
└── child 4
    └── child 5
        └── child 6
            └── child 7
                └── child 8

このように、ツリーを構成するノード群をGoのコードで生成し、出力することができます。

どんなときに使えるの?

...今のところ思いついていません。ただ、使う人によっていろいろな事物をツリーで表現できるのでは?と思いました。
例えば、findコマンドの結果を標準入力に渡しGoコードを実行するとLinux tree結果のような出力をするプログラムを以下に載せます。

// cd github.com/ddddddO/gtree
// find . -type d -name .git -prune -o -type f -print | go run sample/find_pipe_programmable-gtree/main.go
func main() {
	var (
		root *gtree.Node
		node *gtree.Node
	)
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		line := scanner.Text()
		splited := strings.Split(line, "/")

		for i, s := range splited {
			if i == 0 {
				if root == nil {
					root = gtree.NewRoot(s)
					node = root
				}
				continue
			}
			tmp := node.Add(s)
			node = tmp
		}
		node = root
	}

	if err := gtree.ExecuteProgrammably(os.Stdout, root); err != nil {
		panic(err)
	}
	// .
	// ├── .github
	// │   └── workflows
	// │       ├── cd.yaml
	// │       └── ci.yaml
	// ├── .gitignore
	// ├── .goreleaser.yml
	// ├── cmd
	// │   └── gtree
	// │       └── main.go
	// ├── example_programmable_test.go
	// ├── go.mod
	// ├── go.sum
	// ├── LICENSE
	// ├── node.go
	// ├── programmable.go
	// ├── programmable_test.go
	// ├── README.md
	// ├── sample
	// │   ├── find_pipe_programmable-gtree
	// │   │   └── main.go
	// │   ├── like_cli
	// │   │   ├── adapter
	// │   │   │   ├── executor.go
	// │   │   │   └── indentation.go
	// │   │   └── main.go
	// │   └── programmable
	// │       └── main.go
	// ├── stack.go
	// ├── testdata
	// │   ├── demo.md
	// │   ├── sample0.md
	// │   ├── sample1.md
	// │   ├── sample2.md
	// │   ├── sample3.md
	// │   ├── sample4.md
	// │   ├── sample5.md
	// │   └── sample6.md
	// ├── tmp.md
	// ├── tree.go
	// └── tree_test.go
}

任意のコマンドをパイプして、上記のようなGoプログラムに実行させれば夢は広がるのでは?と思いました。

その他

インストール

go get github.com/ddddddO/gtree/v6

リポジトリ

https://github.com/ddddddO/gtree

ドキュメント

https://pkg.go.dev/github.com/ddddddO/gtree/v6

感想

途中で、書いてるコードが複雑になり始めてきて諦めようと思いました。しかし、少し間をおいて考えた結果、シンプルな実装にすることが出来たので、頭を冷やす時間は大事と思いました。
また、ちょっとしたリファクタリング(変数・メソッド名を説明的にする/役割り毎にファイルを分けるなど)を度々繰り返すことで、最初はCLIとして作っていましたが、パッケージとして提供する際はかなりスムーズにできました。リファクタリング大事。
あとは、GoDocの作り方とか、Goはv2からgo.modを編集しないといけないなどの学びがありました。

追記

  • パッケージ限定で、ユーザー自身が枝のフォーマットを決められる機能を追加しました🎉
    • こちらにサンプルを載せています🙇

Discussion

ログインするとコメントできます