Open8

Golangのユースケース

nabetsunabetsu

JSONの取扱

Structで定義

以下のようにstructで型を定義して、それに従ってUnmarshallでパースする。
このやり方だと型情報が入るので、IDEの補完なども使える。

structの定義にはJSON-to-Go: Convert JSON to Go instantly が便利。

type beer struct {
	ID     string `json:"id"`
	Fields struct {
		Name        string   `json:"Name"`
		NameEn      string   `json:"NameEn"`
		Description string   `json:"Description"`
		...
}

type beerList struct {
	Records []struct {
		beer
	}
	Offset string `json:"offset"`
}

data := new(beerList)
if err := json.Unmarshal(body, data); err != nil {
	fmt.Println("JSON Unmarshal error:", err)
	return
}

fmt.Println(data.Records[0])

for i, v := range data.Records {
	fmt.Println(i, v)
	fmt.Println(v.Fields.Name)
}

interfaceで定義

interfaceを定義することで任意のデータを読み込むことができる。

	var a interface{}

	err = json.Unmarshal([]byte(body), &a)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%#v\n", a)

取得したJSONデータについては以下のようにアクセスできる。

b := a.(map[string]interface{})["records"]
fmt.Println(b)

参考資料

nabetsunabetsu

コマンドラインで起動するアプリケーションでの引数の扱い

まず、コマンドライン引数の渡し方として以下2つがある。

  • Flagを使わない場合
    • go run main.go 123 456のように値だけを渡す
  • Flagを使う場合
    • go run main.go -a=123 -b=456のようにキーバーリューの形で渡す。

Flagとは上記の例からわかるように

flag package - flag - pkg.go.dev

Flagとは

Flagという言葉にピンときていませんでしたが、Wikipediaに記載されているようにCLIで起動するプログラムで動作を変えるオプションのことを一般的にフラグともいうようです。
なので、flag packageもGolang独自の用語でもなんでもなく、プログラミング一般で使われるフラグの用途として使うためのスタンダードライブラリということのようです。

コマンドラインオプションまたは単にオプション(フラグまたはスイッチとも呼ばれる)は、コマンドの動作を変更するもので、その効果はコマンドのプログラムによって決定されます。オプションは、コマンドライン上のコマンド名の後に、スペースで区切られた形で続きます。
Command-line interface - Wikipedia をDeepLにて和訳

使い方

以下3つの方法があるっぽい。

  • F全てをまとめて取得する方法
  • 特定の要素を取得する方法
  • フラグを定義して取得する方法

Go公式に記載されている以下の例は「フラグを定義して取得する方法」を使っている。

import "flag"
var nFlag = flag.Int("n", 1234, "help message for flag n")

Flagを使わない

全ての引数をまとめて取得
not_flag.go
func main() {
	flag.Parse()
	args := flag.Args()
	fmt.Println(args)
}
# 何も指定しない場合
% go run not_flag.go 
[]

# 複数の引数を渡す場合
% go run not_flag.go 123 456
[123 456]
要素を指定して取得
not_flag_select.go
func main() {
	flag.Parse()
	fmt.Println(flag.Arg(0), flag.Arg(1))
}
% go run not_flag_select.go    
 
% go run not_flag_select.go 123
123 
% go run not_flag_select.go 123 456
123 456
% go run not_flag_select.go 123 456 789
123 456

Flagを使う

flag.Intflag.Stringのように型名でFlagの定義を行なった後、flag.Parseを実行することで定義した変数に値が格納される。
注意点として、ドキュメントに記載があるようにポインタが返ってくる。

Flags may then be used directly. If you're using the flags themselves, they are all pointers; if you bind to variables, they're values.
flag package - flag - pkg.go.dev

flag.go
func main() {
	var nFlag = flag.Int("n", 1234, "help message for flag n") // Flag名、デフォルト値、メッセージ
	flag.Parse()
	fmt.Println(*nFlag)
}

実際の動作としては以下の通りで、Flagが指定されない場合はデフォルト値が入る

# 引数に何も指定しない場合
% go run flag.go
1234
# 値だけを渡した場合
% go run flag.go 123
1234
# Flagの正しい形式で渡した場合
% go run flag.go -n=456
456
# 異なる型の値を渡した場合
% go run flag.go -n=0.3
invalid value "0.3" for flag -n: parse error
Usage of /var/folders/rc/74wzrts94h14rsn2z3lqmv3m0000gn/T/go-build1055359882/b001/exe/flag:
  -n int
        help message for flag n (default 1234)
exit status 2

Varを使った記載

型名Var()を使って定義することもできる。
その場合には定義した変数にバインドされる。

func main() {
	var nFlag int
	flag.IntVar(&nFlag, "n", 1234, "help message for flag n")
	flag.Parse()
	fmt.Println(nFlag)
}

GitHub

https://github.com/panyoriokome/learning-go/tree/main/flags

参考資料

nabetsunabetsu

GitHub Actionsでの静的解析

Linterの種類

  • govet
  • gofmt

プラクティス

  • golangci-lintを使うのが良さそう。Actionsも提供されている。
  • PRにはreviewdogとgolangci-lintを組み合わせればいいかな

golangci-lint

以下がとりあえずgolangci-lintを動かすために必要な最低限のもの。

jobs:
  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Set up Gp
        uses: actions/setup-go@v2
        with:
          go-version: 1.17
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v2
        with:
          # Optional: working directory, useful for monorepos
          working-directory: twitter

これでプッシュすると以下のように解析が走って問題があれば落ちる。

reviewdog

filter_mode: nofilterを指定すると、差分以外に対する指摘もAnnotationとして表示される。
逆にこの指定をしない(デフォルト)だと差分に問題がなければチェックはパスする。

name: lint-mr
on:
  pull_request:
    types: [opened, synchronize]
jobs:
  golangci-lint:
    runs-on: ubuntu-latest
    steps:
      - name: Check out code into the Go module directory
        uses: actions/checkout@v2
      - name: golangci-lint
        uses: reviewdog/action-golangci-lint@v2
        with:
#         # Optional: working directory, useful for monorepos
          workdir: twitter/
          github_token: ${{ github.token }}
          filter_mode: nofilter

以下のように問題のある箇所にコメントを表示してくれる。

参考資料