Skip to content

Commit

Permalink
feat: Add support of url.URL (#80)
Browse files Browse the repository at this point in the history
  • Loading branch information
obalunenko authored Mar 26, 2023
1 parent 607a9e0 commit 4f9dea7
Show file tree
Hide file tree
Showing 18 changed files with 3,782 additions and 3 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/obalunenko/getenv.svg)](https://pkg.go.dev/github.com/obalunenko/getenv)
[![Go Report Card](https://goreportcard.com/badge/github.com/obalunenko/getenv)](https://goreportcard.com/report/github.com/obalunenko/getenv)
[![codecov](https://codecov.io/gh/obalunenko/getenv/branch/master/graph/badge.svg)](https://codecov.io/gh/obalunenko/getenv)
![coverbadger-tag-do-not-edit](https://img.shields.io/badge/coverage-97.03%25-brightgreen?longCache=true&style=flat)
![coverbadger-tag-do-not-edit](https://img.shields.io/badge/coverage-97.56%25-brightgreen?longCache=true&style=flat)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=obalunenko_getenv&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=obalunenko_getenv)

# getenv

Package getenv provides functionality for loading environment variables and parse them into go builtin types.
Expand Down Expand Up @@ -37,6 +36,7 @@ Types supported:
- []time.Time
- time.Duration
- bool
- url.URL

## Examples

Expand Down
1 change: 1 addition & 0 deletions getenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
// - []time.Time
// - time.Duration
// - bool
// - url.URL
package getenv

import (
Expand Down
10 changes: 10 additions & 0 deletions getenv_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package getenv_test

import (
"fmt"
"net/url"
"os"
"time"

Expand Down Expand Up @@ -65,10 +66,19 @@ func ExampleEnvOrDefault() {
val = getenv.EnvOrDefault(key, time.Second)
fmt.Printf("[%T]: %v\n", val, val)

// url.URL
if err := os.Setenv(key, "https://test:[email protected]:8000/tutorials/intro?type=advance&compact=false#history"); err != nil {
panic(err)
}

val = getenv.EnvOrDefault(key, url.URL{})
fmt.Printf("[%T]: %v\n", val, val)

// Output:
// [string]: golly
// [int]: 123
// [time.Time]: 2022-01-20 00:00:00 +0000 UTC
// [[]float64]: [26.89 0.67]
// [time.Duration]: 2h35m0s
// [url.URL]: {https test:abcd123 golangbyexample.com:8000 /tutorials/intro false false type=advance&compact=false history }
}
87 changes: 87 additions & 0 deletions getenv_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package getenv_test

import (
"net/url"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/obalunenko/getenv"
"github.com/obalunenko/getenv/option"
Expand Down Expand Up @@ -2454,3 +2456,88 @@ func TestInt16SliceOrDefault(t *testing.T) {
})
}
}

func getURL(tb testing.TB, rawURL string) url.URL {
val, err := url.Parse(rawURL)
require.NoError(tb, err)

return *val
}

func TestURLOrDefault(t *testing.T) {
const rawDefault = "https://test:[email protected]:8000/tutorials/intro?type=advance&compact=false#history"

type args struct {
key string
defaultVal url.URL
}

type expected struct {
val url.URL
}

var tests = []struct {
name string
precond precondition
args args
expected expected
}{
{
name: "env not set - default returned",
precond: precondition{
setenv: setenv{
isSet: false,
val: "postgres://user:[email protected]:5432/path?k=v#f",
},
},
args: args{
key: testEnvKey,
defaultVal: getURL(t, rawDefault),
},
expected: expected{
val: getURL(t, rawDefault),
},
},
{
name: "env set - env value returned",
precond: precondition{
setenv: setenv{
isSet: true,
val: "postgres://user:[email protected]:5432/path?k=v#f",
},
},
args: args{
key: testEnvKey,
defaultVal: getURL(t, rawDefault),
},
expected: expected{
val: getURL(t, "postgres://user:[email protected]:5432/path?k=v#f"),
},
},
{
name: "empty env value set - default returned",
precond: precondition{
setenv: setenv{
isSet: true,
val: "",
},
},
args: args{
key: testEnvKey,
defaultVal: getURL(t, rawDefault),
},
expected: expected{
val: getURL(t, rawDefault),
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.precond.maybeSetEnv(t, tt.args.key)

got := getenv.EnvOrDefault(tt.args.key, tt.args.defaultVal)
assert.Equal(t, tt.expected.val, got)
})
}
}
9 changes: 9 additions & 0 deletions internal/common_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package internal

import (
"net/url"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

type panicAssertionFunc func(t assert.TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) bool
Expand All @@ -24,3 +26,10 @@ func (p precondition) maybeSetEnv(tb testing.TB, key string) {
tb.Setenv(key, p.setenv.val)
}
}

func getURL(tb testing.TB, rawURL string) url.URL {
val, err := url.Parse(rawURL)
require.NoError(tb, err)

return *val
}
3 changes: 2 additions & 1 deletion internal/constraint.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package internal

import (
"net/url"
"time"
)

type (
// EnvParsable is a constraint for supported environment variable types parsers.
EnvParsable interface {
String | Int | Uint | Float | Time | bool
String | Int | Uint | Float | Time | bool | url.URL
}

// String is a constraint for strings and slice of strings.
Expand Down
11 changes: 11 additions & 0 deletions internal/iface.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package internal

import (
"fmt"
"net/url"
"time"
)

Expand All @@ -27,6 +28,8 @@ func NewEnvParser(v any) EnvParser {
p = timeSliceParser(t)
case time.Duration:
p = durationParser(t)
case url.URL:
p = urlParser(t)
default:
p = nil
}
Expand Down Expand Up @@ -357,3 +360,11 @@ func (d uint32Parser) ParseEnv(key string, defaltVal any, _ Parameters) any {

return val
}

type urlParser url.URL

func (t urlParser) ParseEnv(key string, defaltVal any, _ Parameters) any {
val := urlOrDefault(key, defaltVal.(url.URL))

return val
}
28 changes: 28 additions & 0 deletions internal/iface_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package internal

import (
"net/url"
"testing"
"time"

Expand Down Expand Up @@ -238,6 +239,14 @@ func TestNewEnvParser(t *testing.T) {
want: durationParser(time.Minute),
wantPanic: assert.NotPanics,
},
{
name: "url.URL",
args: args{
v: url.URL{},
},
want: urlParser(url.URL{}),
wantPanic: assert.NotPanics,
},
{
name: "not supported - panics",
args: args{
Expand Down Expand Up @@ -783,6 +792,25 @@ func Test_ParseEnv(t *testing.T) {
time.Date(2023, time.March, 24, 0, 0, 0, 0, time.UTC),
},
},
{
name: "urlParser",
s: urlParser(url.URL{}),
precond: precondition{
setenv: setenv{
isSet: true,
val: "https.google.com",
},
},
args: args{
key: testEnvKey,
defaltVal: url.URL{},
in2: Parameters{
Separator: "",
Layout: time.DateOnly,
},
},
want: getURL(t, "https.google.com"),
},
}

for _, tt := range tests {
Expand Down
18 changes: 18 additions & 0 deletions internal/parsers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package internal

import (
"net/url"
"os"
"strconv"
"strings"
Expand Down Expand Up @@ -613,3 +614,20 @@ func uint32OrDefault(key string, defaultVal uint32) uint32 {

return uint32(val)
}

// urlOrDefault retrieves the url.URL value of the environment variable named
// by the key represented by layout.
// If variable not set or value is empty - defaultVal will be returned.
func urlOrDefault(key string, defaultVal url.URL) url.URL {
env := stringOrDefault(key, "")
if env == "" {
return defaultVal
}

val, err := url.Parse(env)
if err != nil {
return defaultVal
}

return *val
}
79 changes: 79 additions & 0 deletions internal/parsers_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package internal

import (
"net/url"
"testing"
"time"

Expand Down Expand Up @@ -1652,6 +1653,84 @@ func Test_timeOrDefault(t *testing.T) {
}
}

func TestURLOrDefault(t *testing.T) {
const rawDefault = "https://test:[email protected]:8000/tutorials/intro?type=advance&compact=false#history"

type args struct {
key string
defaultVal url.URL
}

type expected struct {
val url.URL
}

var tests = []struct {
name string
precond precondition
args args
expected expected
}{
{
name: "env not set - default returned",
precond: precondition{
setenv: setenv{
isSet: false,
val: "postgres://user:[email protected]:5432/path?k=v#f",
},
},
args: args{
key: testEnvKey,
defaultVal: getURL(t, rawDefault),
},
expected: expected{
val: getURL(t, rawDefault),
},
},
{
name: "env set - env value returned",
precond: precondition{
setenv: setenv{
isSet: true,
val: "postgres://user:[email protected]:5432/path?k=v#f",
},
},
args: args{
key: testEnvKey,
defaultVal: getURL(t, rawDefault),
},
expected: expected{
val: getURL(t, "postgres://user:[email protected]:5432/path?k=v#f"),
},
},
{
name: "empty env value set - default returned",
precond: precondition{
setenv: setenv{
isSet: true,
val: "",
},
},
args: args{
key: testEnvKey,
defaultVal: getURL(t, rawDefault),
},
expected: expected{
val: getURL(t, rawDefault),
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.precond.maybeSetEnv(t, tt.args.key)

got := urlOrDefault(tt.args.key, tt.args.defaultVal)
assert.Equal(t, tt.expected.val, got)
})
}
}

func Test_timeSliceOrDefault(t *testing.T) {
const layout = "2006/02/01 15:04"

Expand Down
Loading

0 comments on commit 4f9dea7

Please sign in to comment.