Skip to content

Commit

Permalink
Merge pull request #55 from dogmatiq/see-also
Browse files Browse the repository at this point in the history
See also
  • Loading branch information
jmalloc authored Mar 11, 2023
2 parents 072a3a7 + 3110999 commit 6cebc4d
Show file tree
Hide file tree
Showing 104 changed files with 1,505 additions and 1,073 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ The format is based on [Keep a Changelog], and this project adheres to
[keep a changelog]: https://keepachangelog.com/en/1.0.0/
[semantic versioning]: https://semver.org/spec/v2.0.0.html

### Unreleased

### Added

- Added `SeeAlso()` method to all builders, which links to another variable for documentation purposes

## [0.4.2] - 2023-03-11

### Added
Expand Down
6 changes: 6 additions & 0 deletions bool.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ func (b *BoolBuilder[T]) WithDefault(v T) *BoolBuilder[T] {
return b
}

// SeeAlso creates a relationship between this variable and those used by i.
func (b *BoolBuilder[T]) SeeAlso(i Input, options ...SeeAlsoOption) *BoolBuilder[T] {
seeAlsoInput(&b.spec, i, options...)
return b
}

// Required completes the build process and registers a required variable with
// Ferrite's validation system.
func (b *BoolBuilder[T]) Required(options ...RequiredOption) Required[T] {
Expand Down
9 changes: 3 additions & 6 deletions builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,9 @@ func undefinedError(v variable.Any) error {
}

// isBuilderOf makes a static assertion that B meats
type isBuilderOf[T any, B builderOf[T]] struct{}

// builderOf is an interface and type constriant common to all builders that
// produce a value of type T.
type builderOf[T any] interface {
type isBuilderOf[T any, B interface {
SeeAlso(input Input, options ...SeeAlsoOption) B
Required(options ...RequiredOption) Required[T]
Optional(options ...OptionalOption) Optional[T]
Deprecated(options ...DeprecatedOption) Deprecated[T]
}
}] struct{}
6 changes: 6 additions & 0 deletions duration.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ func (b *DurationBuilder) WithMaximum(v time.Duration) *DurationBuilder {
return b
}

// SeeAlso creates a relationship between this variable and those used by i.
func (b *DurationBuilder) SeeAlso(i Input, options ...SeeAlsoOption) *DurationBuilder {
seeAlsoInput(&b.spec, i, options...)
return b
}

// Required completes the build process and registers a required variable with
// Ferrite's validation system.
func (b *DurationBuilder) Required(options ...RequiredOption) Required[time.Duration] {
Expand Down
6 changes: 6 additions & 0 deletions enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ func (b *EnumBuilder[T]) WithDefault(v T) *EnumBuilder[T] {
return b
}

// SeeAlso creates a relationship between this variable and those used by i.
func (b *EnumBuilder[T]) SeeAlso(i Input, options ...SeeAlsoOption) *EnumBuilder[T] {
seeAlsoInput(&b.spec, i, options...)
return b
}

// Required completes the build process and registers a required variable with
// Ferrite's validation system.
func (b *EnumBuilder[T]) Required(options ...RequiredOption) Required[T] {
Expand Down
6 changes: 6 additions & 0 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ func (b *FileBuilder) WithDefault(v string) *FileBuilder {
return b
}

// SeeAlso creates a relationship between this variable and those used by i.
func (b *FileBuilder) SeeAlso(i Input, options ...SeeAlsoOption) *FileBuilder {
seeAlsoInput(&b.spec, i, options...)
return b
}

// Required completes the build process and registers a required variable with
// Ferrite's validation system.
func (b *FileBuilder) Required(options ...RequiredOption) Required[FileName] {
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ require (
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/kr/pretty v0.1.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.6.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
8 changes: 7 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jmalloc/gomegax v0.0.0-20200507221434-64fca4c0e03a h1:Gk7Gkwl1KUJII/FiAjvBjRgEz/lpvTV8kNYp+9jdpuk=
github.com/jmalloc/gomegax v0.0.0-20200507221434-64fca4c0e03a/go.mod h1:TZpc8ObQEKqTuy1/VXpPRfcMU80QFDU4zK3nchXts/k=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
Expand Down Expand Up @@ -75,8 +80,9 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
Expand Down
15 changes: 15 additions & 0 deletions input.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ferrite

import "github.com/dogmatiq/ferrite/variable"

// An Input is the application-facing interface for obtaining a value from
// environment variables.
//
// Typically each input is sourced from exactly one environment variable,
// however it is possible that a value collates values from multiple variables.
//
// An input is created using one of the various builder-functions, for example
// String().
type Input interface {
variables() []variable.Any
}
29 changes: 18 additions & 11 deletions builderdeprecated.go → inputdeprecated.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import (
"github.com/dogmatiq/ferrite/variable"
)

// Deprecated is the application-facing interface for a value that is sourced
// Deprecated is a specialization of the Input interface for values obtained
// from deprecated environment variables.
//
// It is obtained by calling Deprecated() on a variable builder.
type Deprecated[T any] interface {
// DeprecatedValue returns the parsed and validated value of the environment
// variable, if it is defined.
Input

// DeprecatedValue returns the parsed and validated value built from the
// environment variable(s).
//
// If the environment variable is not defined (and there is no default
// value), ok is false; otherwise, ok is true and v is the value.
// If the constituent environment variable(s) are not defined and there is
// no default value, ok is false; otherwise, ok is true and v is the value.
//
// It panics if the environment variable is defined but invalid.
// It panics if any of one of the constituent environment variable(s) has an
// invalid value.
DeprecatedValue() (T, bool)
}

Expand All @@ -41,6 +42,7 @@ func deprecated[T any, S variable.TypedSchema[T]](

// interface is currently empty so we don't need an implementation
return deprecatedFunc[T]{
[]variable.Any{v},
func() (T, bool, error) {
return v.NativeValue()
},
Expand All @@ -50,13 +52,18 @@ func deprecated[T any, S variable.TypedSchema[T]](
// deprecatedFunc is an implementation of Deprecated[T] that obtains the value
// from an arbitrary function.
type deprecatedFunc[T any] struct {
fn func() (T, bool, error)
vars []variable.Any
fn func() (T, bool, error)
}

func (d deprecatedFunc[T]) DeprecatedValue() (T, bool) {
n, ok, err := d.fn()
func (i deprecatedFunc[T]) DeprecatedValue() (T, bool) {
n, ok, err := i.fn()
if err != nil {
panic(err.Error())
}
return n, ok
}

func (i deprecatedFunc[T]) variables() []variable.Any {
return i.vars
}
35 changes: 21 additions & 14 deletions builderoptional.go → inputoptional.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import (
"github.com/dogmatiq/ferrite/variable"
)

// Optional is the application-facing interface for a value that is sourced
// from optional environment variables.
//
// It is obtained by calling Deprecated() on a variable builder.
// Optional is a specialization of the Input interface for values obtained
// from deprecated environment variables.
type Optional[T any] interface {
// Value returns the parsed and validated value of the environment variable,
// if it is defined.
Input

// Value returns the parsed and validated value built from the environment
// variable(s).
//
// If the environment variable is not defined (and there is no default
// value), ok is false; otherwise, ok is true and v is the value.
// If the constituent environment variable(s) are not defined and there is
// no default value, ok is false; otherwise, ok is true and v is the value.
//
// It panics if the environment variable is defined but invalid.
// It panics if any of one of the constituent environment variable(s) has an
// invalid value.
Value() (T, bool)
}

Expand All @@ -37,22 +38,28 @@ func optional[T any, S variable.TypedSchema[T]](
)

return optionalFunc[T]{
[]variable.Any{v},
func() (T, bool, error) {
return v.NativeValue()
},
}
}

// optionalFunc is an implementation of Optional[T] that obtains the value from an
// arbitrary function.
// optionalFunc is an implementation of Optional[T] that obtains the value from
// an arbitrary function.
type optionalFunc[T any] struct {
fn func() (T, bool, error)
vars []variable.Any
fn func() (T, bool, error)
}

func (d optionalFunc[T]) Value() (T, bool) {
n, ok, err := d.fn()
func (i optionalFunc[T]) Value() (T, bool) {
n, ok, err := i.fn()
if err != nil {
panic(err.Error())
}
return n, ok
}

func (i optionalFunc[T]) variables() []variable.Any {
return i.vars
}
27 changes: 17 additions & 10 deletions builderrequired.go → inputrequired.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import (
"github.com/dogmatiq/ferrite/variable"
)

// Required is the application-facing interface for a value that is sourced
// from required environment variables.
//
// It is obtained by calling Deprecated() on a variable builder.
// Required is a specialization of the Input interface for values obtained
// from required (mandatory) environment variables.
type Required[T any] interface {
Input

// Value returns the parsed and validated value of the environment variable.
//
// It panics if the environment variable is undefined or invalid.
// It panics if any of one of the constituent environment variable(s) is
// undefined or has an invalid value.
Value() T
}

Expand All @@ -35,6 +36,7 @@ func required[T any, S variable.TypedSchema[T]](
)

return requiredFunc[T]{
[]variable.Any{v},
func() (T, error) {
n, ok, err := v.NativeValue()
if ok || err != nil {
Expand All @@ -45,16 +47,21 @@ func required[T any, S variable.TypedSchema[T]](
}
}

// requiredFunc is an implementation of Required[T] that obtains the value
// from an arbitrary function.
// requiredFunc is an implementation of Required[T] that obtains the value from
// an arbitrary function.
type requiredFunc[T any] struct {
fn func() (T, error)
vars []variable.Any
fn func() (T, error)
}

func (d requiredFunc[T]) Value() T {
n, err := d.fn()
func (i requiredFunc[T]) Value() T {
n, err := i.fn()
if err != nil {
panic(err.Error())
}
return n
}

func (i requiredFunc[T]) variables() []variable.Any {
return i.vars
}
59 changes: 59 additions & 0 deletions internal/mode/usage/markdown/complete_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package markdown_test

import (
"github.com/dogmatiq/ferrite"
"github.com/dogmatiq/ferrite/variable"
. "github.com/onsi/ginkgo/v2"
)

var _ = DescribeTable(
"func Run()",
tableTest("complete"),
Entry(
"no variables",
"empty.md",
func(reg *variable.Registry) {},
),
Entry(
"non-normative examples",
"non-normative.md",
func(reg *variable.Registry) {
ferrite.
String("READ_DSN", "database connection string for read-models").
Required(variable.WithRegistry(reg))
},
),
Entry(
"platform examples",
"platform-examples.md",
func(reg *variable.Registry) {
ferrite.
Bool("DEBUG", "enable or disable debugging features").
Optional(variable.WithRegistry(reg))
},
),
Entry(
"platform examples use default values as examples when available",
"platform-examples-use-defaults.md",
func(reg *variable.Registry) {
ferrite.
NetworkPort("PORT", "an environment variable that has a default value").
WithDefault("ftp").
Required(variable.WithRegistry(reg))
},
),
Entry(
"see also",
"see-also.md",
func(reg *variable.Registry) {
verbose := ferrite.
Bool("VERBOSE", "enable verbose logging").
Optional(variable.WithRegistry(reg))

ferrite.
Bool("DEBUG", "enable or disable debugging features").
SeeAlso(verbose).
Optional(variable.WithRegistry(reg))
},
),
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package markdown

import "github.com/dogmatiq/ferrite/variable"

func (r *renderer) renderUsage() {
func (r *renderer) renderPlatformExamples() {
r.line("## Usage Examples")
r.gap()
r.renderKubernetesUsage()
r.renderKubernetesExample()
r.gap()
r.renderDockerUsage()
r.renderDockerExample()
}

func (r *renderer) renderKubernetesUsage() {
func (r *renderer) renderKubernetesExample() {
r.line("<details>")
r.line("<summary>Kubernetes</summary>")

Expand Down Expand Up @@ -82,7 +82,7 @@ func (r *renderer) renderKubernetesUsage() {
r.line("</details>")
}

func (r *renderer) renderDockerUsage() {
func (r *renderer) renderDockerExample() {
r.line("<details>")
r.line("<summary>Docker</summary>")
r.gap()
Expand Down
Loading

0 comments on commit 6cebc4d

Please sign in to comment.