電波ビーチ

☆(ゝω・)v

Goで設定ファイル(.ini)を雑にさわる

南蛮千治の再発明じゃがすくなくともわたしは毎回ググるのでメモ

ちなみに

アプリやプログラムの設定におけるこのような変数に関しては、オライリー実用 Go言語によれば

プログラムの挙動を変更する手段として、近年見直されているのが環境変数です。<中略>クラウド時代になって環境変数が強く推奨されるようになりました。<中略>特に実行ログに残ってほしくない、外部サーバーへのアクセスに用いる認証情報などは環境変数を利用してアプリケーションに渡すことが多いでしょう。

(引用: 1.7.2 環境変数

と書かれており、けっこう前から設定ファイルなんてのはもう古臭い感じがあるらしい。知らんかった...まあdockerとかAWSとかとは無縁なのでgithubで設定ファイルおもらしなんてのがありえないっていうそういう感じでおりますので...(そもそも今の社はIT企業ですら無いのでgithubなんぞ使ってないので...)

いいわけはこのへんにしといてさっさと実装いきます

ディレクト

以下のような感じ。おわかりのとおり「ちょっと試してみるか」用のサブディレクトsandbox下に書いたので記事内のプログラムにハードコーディングで出てくるディレクトリ階層は実際につかうときはこんなんじゃないけど、めんどくさいのでそのまま載せることにしてます。意識低い

$ tree
<application name(project root)>
├── go.mod
├── go.sum
├── sandbox
│   ├── config
│   │   └── config.go
│   ├── config.ini
│   └── main.go
.
..
...(ほんとはこの先にアプリとかある)

sandbox/config/config.goからsandbox/config.iniを読み出したりします。

実装

設定ファイルはこんな感じにしてみました。データベースとか認証情報とかあるけど今回は実際に使うわけじゃないです。ただの例です

[app]
client_id     = <id>
client_secret = <secret value>
access_token  = <access token>

[web]
port = 6666

[db]
driver = sqlite3

で、キモとなるのがこちらのconfig.go

package config

import (
    "log"

    "gopkg.in/ini.v1"
)

type ConfigList struct {
    App
    WebCfg
    DbCfg
}

type App struct {
    ClientId     string
    ClientSecret string
    AccessToken  string
}

type WebCfg struct {
    Port string
}

type DbCfg struct {
    SQLDriver string
}

var Cfg ConfigList

func init() {
    LoadConfig()
}

func LoadConfig() {
    cfg, err := ini.Load("sandbox/config.ini")
    if err != nil {
        log.Fatalln(err)
    }

    Cfg = ConfigList{
        App: App{
            ClientId:     cfg.Section("app").Key("client_id").String(),
            ClientSecret: cfg.Section("app").Key("client_secret").String(),
            AccessToken:  cfg.Section("app").Key("access_token").String(),
        },
        WebCfg: WebCfg{
            Port: cfg.Section("web").Key("port").String(),
        },

        DbCfg: DbCfg{
            SQLDriver: cfg.Section("db").Key("driver").String(),
        },
    }
}

func GetConfig(sectionName, key string) (string, error) {
    cfg, err := ini.Load("config.ini")
    if err != nil {
        return "", err
    }
    return cfg.Section(sectionName).Key(key).String(), nil
}

func UpdateConfig(sectionName, key, value string) error {
    cfg, err := ini.Load("sandbox/config.ini")
    if err != nil {
        return err
    }
    cfg.Section(sectionName).Key(key).SetValue(value)
    cfg.SaveTo("sandbox/config.ini")
    LoadConfig()
    return nil
}

ようはgopkg.in/ini.v1のマニュアル読みましょうねということなんだけど、まあハンズオンとしてね....

こちらがmain.go。とくになんもしてない。

package main

import (
    "<application name>/sandbox/config"
    "fmt"
)

func main() {
    fmt.Println("config.iniを設定ファイルとして読み書きするテスト")
    fmt.Println(config.Cfg.App)
    fmt.Println(config.Cfg.App.AccessToken)
    fmt.Println(config.Cfg.WebCfg)
    fmt.Println(config.Cfg.DbCfg)
    fmt.Println(config.Cfg.DbCfg.SQLDriver)

    config.UpdateConfig("web", "port", "8888")
    fmt.Println(config.Cfg.WebCfg.Port)
}

ファイルの更新は、正直そうとう雑なのでもっとちゃんとしたやり方があるはずなんだけどよくわかんないので直接渡してるだけ。ここはなんの参考にもならなさそう。あれ?結局環境変数使ったほうが楽そうじゃない??

参考

Developers IO - Golangでini形式のファイルを読み書きしてみる