Skip to content

Commit

Permalink
Merge pull request #2 from andreiavrammsd/dev
Browse files Browse the repository at this point in the history
Interpolate/Optimizations
  • Loading branch information
andreiavrammsd committed Dec 10, 2019
2 parents 47a8ead + b1941e9 commit 7a610b3
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 98 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
.idea
bin
cover.out
17 changes: 7 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,23 @@ GO111MODULE=on

all: test lint

GOLINT := $(shell which golint)

test:
go test -cover -v ./...

bench:
go test -bench=. -benchmem -v -run=Bench ./...

lint:
ifndef GOLINT
GO111MODULE=off && go get -u golang.org/x/lint/golint
endif
lint: check-lint
golint -set_exit_status ./...

@[ ! -f ./bin/golangci-lint ] && curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh \
| sh -s -- -b ./bin v1.21.0 || true
./bin/golangci-lint run
golangci-lint run

coverage:
go test -v -coverprofile $(COVER_PROFILE) ./... && go tool cover -html=$(COVER_PROFILE)

prepushhook:
echo '#!/bin/sh\n\nmake' > .git/hooks/pre-push && chmod +x .git/hooks/pre-push

check-lint:
@[ $(shell which golint) ] || (GO111MODULE=off && go get -u golang.org/x/lint/golint)
@[ $(shell which golangci-lint) ] || curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh \
| sh -s -- -b $(shell go env GOPATH)/bin v1.21.0
54 changes: 50 additions & 4 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
package config

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
)

Expand All @@ -23,6 +25,11 @@ type Loader struct {
i interface{}
}

// Load creates a Loader with given struct
func Load(i interface{}) *Loader {
return &Loader{i: i}
}

// Env loads config into struct from environment variables
func (l *Loader) Env() error {
if err := checkNilStruct(l.i); err != nil {
Expand All @@ -43,7 +50,35 @@ func (l *Loader) EnvFile(files ...string) error {
files = append(files, dotEnvFile)
}

return fromEnvFile(l.i, files...)
vars := make(map[string]string)

for i := 0; i < len(files); i++ {
f, err := os.Open(files[i])
if err != nil {
return fmt.Errorf("config: %s", err)
}

err = parseVars(f, vars)

if err != nil {
if e := f.Close(); e != nil {
return fmt.Errorf("config: %s", e)
}
return fmt.Errorf("config: %s", err)
}

if err = f.Close(); err != nil {
return fmt.Errorf("config: %s", err)
}
}

interpolateVars(vars)

f := func(s string) string {
return vars[s]
}

return parseIntoStruct(l.i, f)
}

// Bytes loads config into struct from byte array
Expand Down Expand Up @@ -81,7 +116,18 @@ func checkNilStruct(i interface{}) error {
return nil
}

// Load creates a Loader with given struct
func Load(i interface{}) *Loader {
return &Loader{i: i}
func fromBytes(i interface{}, input []byte) error {
vars := make(map[string]string)

if err := parseVars(bytes.NewReader(input), vars); err != nil {
return err
}

interpolateVars(vars)

f := func(s string) string {
return vars[s]
}

return parseIntoStruct(i, f)
}
80 changes: 45 additions & 35 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,25 @@ type Config struct {
Port int `env:"REDIS_PORT"`
}
}
String string `env:"ABC"`
Struct Struct
StructPtr *Struct
D int64
E int
ENeg int `env:"E_NEG"`
UD uint64
UE uint
F64 float64
Timeout time.Duration
C int32
UC uint32
F32 float32
B int16
UB uint16
A int8
UA uint8
IsSet bool
String string `env:"ABC"`
Struct Struct
StructPtr *Struct
D int64
E int
ENeg int `env:"E_NEG"`
UD uint64
UE uint
F64 float64
Timeout time.Duration
C int32
UC uint32
F32 float32
B int16
UB uint16
A int8
UA uint8
IsSet bool
Interpolated string
}

type Struct struct {
Expand All @@ -58,11 +59,14 @@ func TestEnv(t *testing.T) {
t.Fatal(err)
}

kv, err := vars(bytes.NewReader(input))
vars := make(map[string]string)
err = parseVars(bytes.NewReader(input), vars)
if err != nil {
t.Fatal(err)
}
for k, v := range kv {
interpolateVars(vars)

for k, v := range vars {
if err := os.Setenv(k, v); err != nil {
t.Fatalf(`cannot set env variable "%s" with value "%s": "%s"`, k, v, err)
}
Expand All @@ -77,7 +81,7 @@ func TestEnv(t *testing.T) {
t.Errorf("\nhave: %v\nwant: %v", actual, expected)
}

for k := range kv {
for k := range vars {
if err := os.Unsetenv(k); err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -144,7 +148,7 @@ func TestString(t *testing.T) {
}

func TestJson(t *testing.T) {
input := json.RawMessage(`{
input := json.RawMessage(`{
"StructPtr":null,
"String":" string\\\" ",
"A":1,
Expand All @@ -161,26 +165,27 @@ func TestJson(t *testing.T) {
"F32":15425.2231,
"F64":245232212.9844448,
"IsSet":true,
"Redis":{
"Connection":{
"Redis":{
"Connection":{
"Host":" localhost ",
"Port":6379
}
},
"Timeout":2000000000,
"Mongo":{
"Database":{
"Mongo":{
"Database":{
"Host":"mongodb://user:pass==@host.tld:955/?ssl=true&replicaSet=globaldb",
"Collection":{
"Collection":{
"Name":"dXM9ZXJz",
"Other":1,
"X":97
}
}
},
"Struct":{
"Struct":{
"Field":"Value"
}
},
"Interpolated":"$B env_1 $ $B \\3 6379 + $"
}`)

_, expected, err := testdata()
Expand Down Expand Up @@ -269,25 +274,29 @@ func (e *errReader) Read(p []byte) (n int, err error) {
}

func TestWithParseReaderError(t *testing.T) {
v, err := vars(&errReader{})
if v != nil {
t.Error("expected nil map")
kv := make(map[string]string)
err := parseVars(&errReader{}, kv)
if len(kv) > 0 {
t.Error("expected empty map")
}
if err == nil {
t.Error("expected reader error")
}
}

// BenchmarkVars-8 1478143 856 ns/op 4144 B/op 2 allocs/op
func BenchmarkVars(b *testing.B) {
// Benchmark_parseVars-8 1663723 749 ns/op 4096 B/op 1 allocs/op
func Benchmark_parseVars(b *testing.B) {
b.ReportAllocs()

input, _, err := testdata()
if err != nil {
b.Fatal(err)
}

vars := make(map[string]string)
reader := bytes.NewReader(input)
for n := 0; n < b.N; n++ {
_, err := vars(reader)
err := parseVars(reader, vars)
if err != nil {
b.Fatal(err)
}
Expand Down Expand Up @@ -362,6 +371,7 @@ func testdata() ([]byte, Config, error) {
X: 'a',
},
}},
Interpolated: "$B env_1 $ $B \\3 6379 + $",
}

return input, expected, nil
Expand Down
33 changes: 0 additions & 33 deletions loaders.go

This file was deleted.

11 changes: 7 additions & 4 deletions testdata/.env
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# key=value
TIMEOUT=2000000000
ABC=" string\" "
A=1
B=2
ABC =" string\" "
A =1
B =2
C=3# key=value
D =4
E=5
Expand All @@ -11,6 +11,7 @@ UA=1 # key=value
UB=2
# comment
UC=3

UD=4
UE=5
F32=15425.2231
Expand All @@ -22,6 +23,8 @@ STRUCT_FIELD=Value
STRUCTPTR_FIELD="Val\"ue "
MONGO_DATABASE_HOST="mongodb://user:pass==@host.tld:955/?ssl=true&replicaSet=globaldb" # db connection
MONGO_DATABASE_COLLECTION_NAME='us=ers'
MONGO_OTHER=1
MONGO_OTHER=$A
MONGO_X=97
# comment
INTERPOLATED="\$B env_$A $ \$B \\$C ${REDIS_PORT} + $"

Loading

0 comments on commit 7a610b3

Please sign in to comment.