【Go】cobraとtext/templateを使って、ファイル作成の効率化を目指す
例えばDDDに基づく開発では、ValueObjectやEntityなどファイルを作成することが
多く、手動でファイルを作成することが手間です。
今回はその対策として、自作したコマンドでファイルを作成する ということを調べながらやっていきたいと思います。
Cobraを使う
CLIツールを作成するためのライブラリとしてCobraが人気なようです。
readmeを見てインストールを行います。
go get -u github.com/spf13/cobra@latest
go install github.com/spf13/cobra-cli@latest
cobra-cliはcobra-cliはcobraアプリケーションとコマンドファイルを生成するコマンドラインプログラムで、作業を効率化してくれるそうです。
cobra-cli init
を実行し、初期化を行い試しにコマンドを追加していきます。
cobra-cli add hello
以下のようなコードがcmd配下に作成されます。
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
// helloCmd represents the hello command
var helloCmd = &cobra.Command{
Use: "hello",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("hello called")
},
}
func init() {
rootCmd.AddCommand(helloCmd)
}
コマンドを実行し、fmt.Println("hello called")が実行されていることが確認できました。
go run main.go hello
hello called
text/templateを使う
標準パッケージになります。
テンプレートとなるtxtファイルを作成し、{{.ValueName}}の部分を置換していくことにします。
package vo
type {{.ValueName}}Vo struct {
}
func New{{.ValueName}}Vo() {{.ValueName}}Vo {
return {{.ValueName}}Vo{}
t, err := template.New("valueObjectTemplate.txt").ParseFiles("valueObjectTemplate.txt")
if err != nil {
fmt.Println(err)
}
if err = t.Execute(
os.Stdout,
map[string]string{"ValueName": "Email"}); err != nil {
fmt.Println(err)
}
注意点はNewの部分。記事がありました。
実行結果
package vo
type EmailVo struct {
}
func NewEmailVo() EmailVo {
return EmailVo{}
}
このままだとターミナル上に表示されているだけなので、ファイルを作成し内容を書き出すことにします。
Executeの定義が
func (t *template.Template) Execute(wr io.Writer, data any) error
io.Writerなので、以下のようにしてあげることにより、
テンプレート内容を置換した内容が新しいファイルの内容として反映されます。
+ file, err := os.Create("vo/emailVo.go"); if err != nil {
+ fmt.Println(err)
+ }
+ defer file.Close()
t, err := template.New("valueObjectTemplate.txt").ParseFiles("valueObjectTemplate.txt")
if err != nil {
fmt.Println(err)
}
if err = t.Execute(
- os.Stdout,
+ file,
map[string]string{"ValueName": "Email"}); err != nil {
fmt.Println(err)
}
組み合わせる
コマンドの追加
cobra-cli add makeVo
テンプレート作成
#text%2Ftemplate%E3%82%92%E4%BD%BF%E3%81%86
上記で作成したテンプレートを修正し、置換部分をvalueName、ValueNameの2種類に変更しました。
package vo
type {{.valueName}}Vo struct {
}
func New{{.ValueName}}Vo() {{.valueName}}Vo {
return {{.valueName}}Vo{}
}
コマンド処理実装
package cmd
import (
"fmt"
"os"
"strings"
"text/template"
"github.com/spf13/cobra"
)
var makeVoCmd = &cobra.Command{
Use: "makeVo",
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1{
fmt.Println("ValueObjectNameを入力してください")
return
}
firstLower := strings.ToLower(string(args[0][0])) + args[0][1:]
firstUpper := strings.ToUpper(string(args[0][0])) + args[0][1:]
file, err := os.Create("vo/"+firstLower+"Vo.go"); if err != nil {
fmt.Println(err)
}
defer file.Close()
t, err := template.New("valueObjectTemplate.txt").ParseFiles("valueObjectTemplate.txt")
if err != nil {
fmt.Println(err)
}
if err = t.Execute(
file,
map[string]string{
"ValueName": firstUpper,
"valueName": firstLower,
}); err != nil {
fmt.Println(err)
}
},
}
func init() {
rootCmd.AddCommand(makeVoCmd)
}
コマンド実行
以下ファイルが生成されました!
package vo
type exampleVo struct {
}
func NewExampleVo() exampleVo {
return exampleVo{}
}
今回のケースは簡単なものでしたが、コマンドからファイル生成まで実装できました。
コマンドの引数や処理、テンプレートを複雑にすることでより自由で便利なコマンドを作ることが可能だと感じました。
参考記事
Discussion