Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,62 @@ If you do not want confire to perform any validation at all, use the `NoValidate
confire.Process(&conf, confire.NoValidate)
```

### Validation Errors

If you write your own config that implements the `Validator` interface, then you can use Confire's built in `InvalidConfig` errors as follows:

```go
import "go.rtnl.ai/confire"

type MyConfig struct {
Interval time.Duration
Port int
Window TimeWindow
}

type TimeWindow struct {
Date TimeDecoder
Length time.Duration
}

type TimeDecoder time.Time

func (c MyConfig) Validate() (err error) {
if t.Interval < 1 {
// A more extensive required check that also takes into account negative values
err = confire.Join(err, confire.Required("", "interval"))
}

if t.Port < 2000 || t.Port > 65334 {
// A type specific invalid configuration error
err = confire.Join(err, confire.Invalid("", "port", "port must be in the integer range [2000, 65334]"))
}

// Returns all configuration errors, not just a single configuration error.
return err
}

func (c TimeWindow) Validate() (err error) {
if verr := CheckWindowLength(c.Length); verr != nil {
// Wrap another error as the invalid configuration error
// Note that you can use nesting to show that this is window.length that is erroring.
err = confire.Join(err, confire.Wrap("window", "length", "bad window length", verr))
}

return err
}

func (t *TimeDecoder) Decode(s string) error {
if e := time.Parse(s, layout); e != nil {
// Return a special parsing error error
return confire.Parse("", "date", e)
}
return nil
}
```

This error handling allows you greater flexibility in returning invalid config typed errors that are the same types as the validator built in to confire. It also allows you to report all invalid fields all at once rather than one error at a time, using the `Join` functionality.

## Parsing

Environment variables and default values in struct tags are all strings that must be parsed into more complex types such as `bool`, `uint64`, `[]string`, `map[int]string` and others, therefore some parsing is required.
Expand Down
3 changes: 2 additions & 1 deletion usage/testdata/custom.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ CONFIRE_RATE=value·between·0·and·1
CONFIRE_CANCEL=
CONFIRE_LOG_LEVEL=
CONFIRE_TIMEOUT=amount·of·time·to·wait·for·a·respone
DATABASE_URL=database·connection·DSN
CONFIRE_COLORS_PRIMARY=
CONFIRE_COLORS_SECONDARY=
CONFIRE_SENDGRID_API_KEY=
SENDGRID_API_KEY=
7 changes: 6 additions & 1 deletion usage/testdata/default_list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ CONFIRE_TIMEOUT
··[type]········Duration
··[default]·····30s
··[required]····
DATABASE_URL
··[description]·database·connection·DSN
··[type]········String
··[default]·····
··[required]····true
CONFIRE_COLORS_PRIMARY
··[description]·
··[type]········String
Expand All @@ -61,7 +66,7 @@ CONFIRE_COLORS_SECONDARY
··[type]········String
··[default]·····#eeffee
··[required]····
CONFIRE_SENDGRID_API_KEY
SENDGRID_API_KEY
··[description]·
··[type]········String
··[default]·····
Expand Down
3 changes: 2 additions & 1 deletion usage/testdata/default_table.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ CONFIRE_RATE················Float···············
CONFIRE_CANCEL··············Unsigned·Integer·······························16····························
CONFIRE_LOG_LEVEL···········LogLevel·······································info··························
CONFIRE_TIMEOUT·············Duration·······································30s···························amount·of·time·to·wait·for·a·respone
DATABASE_URL················String···························································true········database·connection·DSN
CONFIRE_COLORS_PRIMARY······String·········································#cc6699·······················
CONFIRE_COLORS_SECONDARY····String·········································#eeffee·······················
CONFIRE_SENDGRID_API_KEY····String·······································································
SENDGRID_API_KEY············String·······································································
1 change: 1 addition & 0 deletions usage/testdata/fault.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
{.Key}
{.Key}
{.Key}
{.Key}
7 changes: 6 additions & 1 deletion usage/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ func Usagef(prefix string, spec interface{}, out io.Writer, format string) error

// Specify the default usage template functions
functions := template.FuncMap{
"usage_key": func(v env.Info) string { return v.Key },
"usage_key": func(v env.Info) string {
if v.Alt != "" {
return v.Alt
}
return v.Key
},
"usage_description": func(v env.Info) string { return v.Field.Tag("desc") },
"usage_type": func(v env.Info) string { return toTypeDescription(v.Field.Type()) },
"usage_default": func(v env.Info) string { return v.Field.Tag("default") },
Expand Down
6 changes: 5 additions & 1 deletion usage/usage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ type Specification struct {
Cancel uint16 `default:"16"`
LogLevel LogLevel `default:"info" split_words:"true"`
Timeout time.Duration `default:"30s" desc:"amount of time to wait for a respone"`
Colors struct {
Database struct {
URL string `required:"true" env:"DATABASE_URL" desc:"database connection DSN"`
}
Colors struct {
Primary Color `default:"#cc6699"`
Secondary Color `default:"#eeffee"`
}
Expand Down Expand Up @@ -183,6 +186,7 @@ func (e *CustomURL) Decode(value string) (err error) {
}

func compareUsage(t *testing.T, path, actual string) {
t.Helper()
data, err := os.ReadFile(path)
assert.Ok(t, err)
expected := string(data)
Expand Down
Loading