From b2f4697cc4610958e94a607119be7ead4f4393d6 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Sat, 15 May 2021 20:21:25 -0300 Subject: [PATCH] feat: notEmpty tag (#174) Signed-off-by: Carlos A Becker --- README.md | 36 +++++++++++++++++++----------------- env.go | 7 +++++++ env_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 2b952e3..fe1f959 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://img.shields.io/github/workflow/status/caarlos0/env/build?style=for-the-badge)](https://github.com/caarlos0/env/actions?workflow=build) [![Coverage Status](https://img.shields.io/codecov/c/gh/caarlos0/env.svg?logo=codecov&style=for-the-badge)](https://codecov.io/gh/caarlos0/env) -[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](http://godoc.org/github.com/caarlos0/env/v6) +[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://pkg.go.dev/github.com/caarlos0/env/v6) Simple lib to parse envs to structs in Go. @@ -17,11 +17,7 @@ import ( "fmt" "time" - // if using go modules "github.com/caarlos0/env/v6" - - // if using dep/others - "github.com/caarlos0/env" ) type config struct { @@ -142,18 +138,25 @@ And then you can parse `Config` with `env.Parse`. ## Required fields -The `env` tag option `required` (e.g., `env:"tagKey,required"`) can be added -to ensure that some environment variable is set. In the example above, -an error is returned if the `config` struct is changed to: +The `env` tag option `required` (e.g., `env:"tagKey,required"`) can be added to ensure that some environment variable is set. +In the example above, an error is returned if the `config` struct is changed to: + +```go +type config struct { + SecretKey string `env:"SECRET_KEY,required"` +} +``` + +## Not Empty fields +While `required` demands the environment variable to be check, it doesn't check its value. +If you want to make sure the environment is set and not emtpy, you need to use the `notEmpty` tag option instead (`env:"SOME_ENV,notEmpty"`). + +Example: ```go type config struct { - Home string `env:"HOME"` - Port int `env:"PORT" envDefault:"3000"` - IsProduction bool `env:"PRODUCTION"` - Hosts []string `env:"HOSTS" envSeparator:":"` - SecretKey string `env:"SECRET_KEY,required"` + SecretKey string `env:"SECRET_KEY,notEmpty"` } ``` @@ -183,7 +186,7 @@ package main import ( "fmt" "time" - "github.com/caarlos0/env" + "github.com/caarlos0/env/v6" ) type config struct { @@ -213,7 +216,6 @@ $ SECRET=/tmp/secret \ {Secret:qwerty Password:dvorak Certificate:coleman} ``` - ## Options ### Environment @@ -231,7 +233,7 @@ import ( "fmt" "log" - "github.com/caarlos0/env" + "github.com/caarlos0/env/v6" ) type Config struct { @@ -267,7 +269,7 @@ import ( "fmt" "log" - "github.com/caarlos0/env" + "github.com/caarlos0/env/v6" ) type Config struct { diff --git a/env.go b/env.go index f3a98d0..792c855 100644 --- a/env.go +++ b/env.go @@ -219,6 +219,7 @@ func get(field reflect.StructField, opts []Options) (val string, err error) { var exists bool var loadFile bool var unset bool + var notEmpty bool expand := strings.EqualFold(field.Tag.Get("envExpand"), "true") key, tags := parseKeyForOption(field.Tag.Get(getTagName(opts))) @@ -233,6 +234,8 @@ func get(field reflect.StructField, opts []Options) (val string, err error) { required = true case "unset": unset = true + case "notEmpty": + notEmpty = true default: return "", fmt.Errorf("env: tag option %q not supported", tag) } @@ -253,6 +256,10 @@ func get(field reflect.StructField, opts []Options) (val string, err error) { return "", fmt.Errorf(`env: required environment variable %q is not set`, key) } + if notEmpty && val == "" { + return "", fmt.Errorf("env: environment variable %q should not be empty", key) + } + if loadFile && val != "" { filename := val val, err = getFromFile(filename) diff --git a/env_test.go b/env_test.go index 52c7778..e5b5169 100644 --- a/env_test.go +++ b/env_test.go @@ -684,6 +684,44 @@ func TestErrorRequiredNotSet(t *testing.T) { isErrorWithMessage(t, Parse(&config{}), `env: required environment variable "IS_REQUIRED" is not set`) } +func TestNoErrorNotEmptySet(t *testing.T) { + is := is.New(t) + os.Setenv("IS_REQUIRED", "1") + defer os.Clearenv() + type config struct { + IsRequired string `env:"IS_REQUIRED,notEmpty"` + } + is.NoErr(Parse(&config{})) +} + +func TestNoErrorRequiredAndNotEmptySet(t *testing.T) { + is := is.New(t) + os.Setenv("IS_REQUIRED", "1") + defer os.Clearenv() + type config struct { + IsRequired string `env:"IS_REQUIRED,required,notEmpty"` + } + is.NoErr(Parse(&config{})) +} + +func TestErrorNotEmptySet(t *testing.T) { + os.Setenv("IS_REQUIRED", "") + defer os.Clearenv() + type config struct { + IsRequired string `env:"IS_REQUIRED,notEmpty"` + } + isErrorWithMessage(t, Parse(&config{}), `env: environment variable "IS_REQUIRED" should not be empty`) +} + +func TestErrorRequiredAndNotEmptySet(t *testing.T) { + os.Setenv("IS_REQUIRED", "") + defer os.Clearenv() + type config struct { + IsRequired string `env:"IS_REQUIRED,notEmpty,required"` + } + isErrorWithMessage(t, Parse(&config{}), `env: environment variable "IS_REQUIRED" should not be empty`) +} + func TestErrorRequiredNotSetWithDefault(t *testing.T) { is := is.New(t)