Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support config from json and env #56

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/env/env.go
@@ -0,0 +1,79 @@
package env

import (
"fmt"
"log"
"os"
"reflect"
"strconv"
"strings"
)

const sep = "_"

func Fill(prefix string, v interface{}) error {
ind := reflect.Indirect(reflect.ValueOf(v))
if reflect.ValueOf(v).Kind() != reflect.Ptr || ind.Kind() != reflect.Struct {
return fmt.Errorf("only the pointer to a struct is supported")
}
if prefix == "" {
prefix = ind.Type().Name()
}
for i := 0; i < ind.NumField(); i++ {
f := ind.Type().Field(i)
name := f.Name
envName, exist := f.Tag.Lookup("env")
if exist {
name = envName
}
p := strings.ToUpper(prefix + sep + name)
err := parse(p, ind.Field(i), f)
if err != nil {
return err
}
}
return nil
}

func parse(prefix string, f reflect.Value, sf reflect.StructField) error {
ev, exist := os.LookupEnv(prefix)
if !exist {
return nil
}
log.Printf("key: %s, env:%s\n", prefix, ev)
switch f.Kind() {
case reflect.String:
f.SetString(ev)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
iv, err := strconv.ParseInt(ev, 10, f.Type().Bits())
if err != nil {
return fmt.Errorf("%s:%s", prefix, err)
}
f.SetInt(iv)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
uiv, err := strconv.ParseUint(ev, 10, f.Type().Bits())
if err != nil {
return fmt.Errorf("%s:%s", prefix, err)
}
f.SetUint(uiv)
case reflect.Float32, reflect.Float64:
floatValue, err := strconv.ParseFloat(ev, f.Type().Bits())
if err != nil {
return fmt.Errorf("%s:%s", prefix, err)
}
f.SetFloat(floatValue)
case reflect.Bool:
if ev == "" {
f.SetBool(false)
} else {
b, err := strconv.ParseBool(ev)
if err != nil {
return fmt.Errorf("%s:%s", prefix, err)
}
f.SetBool(b)
}
default:
return fmt.Errorf("kind %s not supported", f.Kind())
}
return nil
}
40 changes: 40 additions & 0 deletions src/env/env_test.go
@@ -0,0 +1,40 @@
package env

import (
"os"
"reflect"
"testing"
)

type Specification struct {
Bool bool
Int int
Float float32
String string
}

func TestFill(t *testing.T) {
var got Specification

expected := Specification{
Bool: true,
Int: 8080,
Float: 0.5,
String: "foo",
}

os.Clearenv()
os.Setenv("ENV_BOOL", "true")
os.Setenv("ENV_INT", "8080")
os.Setenv("ENV_FLOAT", "0.5")
os.Setenv("ENV_STRING", "foo")
err := Fill("env", &got)
if err != nil {
t.Error(err.Error())
}

if !reflect.DeepEqual(got, expected) {
t.Errorf("expected %+v", expected)
t.Errorf("got %+v", got)
}
}
89 changes: 68 additions & 21 deletions src/main.go
Expand Up @@ -2,56 +2,103 @@ package main

import (
"bufio"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"

"github.com/nkanaev/yarr/src/env"
"github.com/nkanaev/yarr/src/platform"
"github.com/nkanaev/yarr/src/server"
"github.com/nkanaev/yarr/src/storage"
)

const APP = "yarr"

var Version string = "0.0"
var GitHash string = "unknown"

type Config struct {
Address string `json:"address"`
Database string `json:"database"`
AuthFile string `json:"auth-file"`
CertFile string `json:"cert-file"`
KeyFile string `json:"key-file"`
BasePath string `json:"base-path"`
LogPath string `json:"log-path"`
OpenBrowser bool `json:"open-browser"`
}

func initConfig(appPath string) *Config {
config := &Config{
Address: "127.0.0.1:7070",
Database: filepath.Join(appPath, "storage.db"),
}
appConfigPath := filepath.Join(appPath, "yarr.json")
if _, err := os.Stat(appConfigPath); err == nil {
// log.Printf("config path: %s", appConfigPath)
f, err := os.Open(appConfigPath)
if err != nil {
log.Fatal("Failed to open config file: ", err)
}
defer f.Close()
body, err := ioutil.ReadAll(f)
if err != nil {
log.Fatal("Failed to read config file: ", err)
}

json.Unmarshal(body, config)
}
return config
}

func main() {
log.SetOutput(os.Stdout)
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
configPath, err := os.UserConfigDir()
if err != nil {
log.Fatal("Failed to get config dir: ", err)
}
appPath := filepath.Join(configPath, APP)
if err := os.MkdirAll(appPath, 0755); err != nil {
log.Fatal("Failed to create app config dir: ", err)
}
config := initConfig(appPath)
if err := env.Fill(APP, config); err != nil {
log.Fatal("Failed to fill env: ", err)
}

var addr, db, authfile, certfile, keyfile, basepath string
var addr, db, authfile, certfile, keyfile, basepath, logpath string
var ver, open bool
flag.StringVar(&addr, "addr", "127.0.0.1:7070", "address to run server on")
flag.StringVar(&authfile, "auth-file", "", "path to a file containing username:password")
flag.StringVar(&basepath, "base", "", "base path of the service url")
flag.StringVar(&certfile, "cert-file", "", "path to cert file for https")
flag.StringVar(&keyfile, "key-file", "", "path to key file for https")
flag.StringVar(&db, "db", "", "storage file path")
flag.StringVar(&addr, "addr", config.Address, "address to run server on")
flag.StringVar(&authfile, "auth-file", config.AuthFile, "path to a file containing username:password")
flag.StringVar(&basepath, "base", config.BasePath, "base path of the service url")
flag.StringVar(&certfile, "cert-file", config.CertFile, "path to cert file for https")
flag.StringVar(&keyfile, "key-file", config.KeyFile, "path to key file for https")
flag.StringVar(&logpath, "log-path", config.LogPath, "server log path")
flag.StringVar(&db, "db", config.Database, "storage file path")
flag.BoolVar(&open, "open", config.OpenBrowser, "open the server in browser")
flag.BoolVar(&ver, "version", false, "print application version")
flag.BoolVar(&open, "open", false, "open the server in browser")
flag.Parse()

if ver {
fmt.Printf("v%s (%s)\n", Version, GitHash)
return
}

configPath, err := os.UserConfigDir()
if err != nil {
log.Fatal("Failed to get config dir: ", err)
}

if db == "" {
storagePath := filepath.Join(configPath, "yarr")
if err := os.MkdirAll(storagePath, 0755); err != nil {
log.Fatal("Failed to create app config dir: ", err)
if logpath != "" {
f, err := os.OpenFile(logpath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
db = filepath.Join(storagePath, "storage.db")
log.SetOutput(f)
} else {
log.SetOutput(os.Stdout)
}

log.Printf("using db file %s", db)
log.Printf("config %+v", config)

var username, password string
if authfile != "" {
Expand Down