diff --git a/CHANGELOG.md b/CHANGELOG.md index aa4381c..8298836 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/bool.go b/bool.go index 47180e1..bd6966d 100644 --- a/bool.go +++ b/bool.go @@ -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] { diff --git a/builder.go b/builder.go index f19cac3..4983a1b 100644 --- a/builder.go +++ b/builder.go @@ -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{} diff --git a/duration.go b/duration.go index 077044b..8a8eb45 100644 --- a/duration.go +++ b/duration.go @@ -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] { diff --git a/enum.go b/enum.go index 673cea9..2fc8955 100644 --- a/enum.go +++ b/enum.go @@ -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] { diff --git a/file.go b/file.go index 5dd074f..11ed534 100644 --- a/file.go +++ b/file.go @@ -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] { diff --git a/go.mod b/go.mod index bf83179..af16369 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 11d4c81..159ac86 100644 --- a/go.sum +++ b/go.sum @@ -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= @@ -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= diff --git a/input.go b/input.go new file mode 100644 index 0000000..5fe4b81 --- /dev/null +++ b/input.go @@ -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 +} diff --git a/builderdeprecated.go b/inputdeprecated.go similarity index 62% rename from builderdeprecated.go rename to inputdeprecated.go index 7376401..b016d24 100644 --- a/builderdeprecated.go +++ b/inputdeprecated.go @@ -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) } @@ -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() }, @@ -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 +} diff --git a/builderoptional.go b/inputoptional.go similarity index 54% rename from builderoptional.go rename to inputoptional.go index 3b80134..9a11a27 100644 --- a/builderoptional.go +++ b/inputoptional.go @@ -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) } @@ -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 +} diff --git a/builderrequired.go b/inputrequired.go similarity index 68% rename from builderrequired.go rename to inputrequired.go index 090ad85..b959fb3 100644 --- a/builderrequired.go +++ b/inputrequired.go @@ -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 } @@ -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 { @@ -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 +} diff --git a/internal/mode/usage/markdown/complete_test.go b/internal/mode/usage/markdown/complete_test.go new file mode 100644 index 0000000..feb32bf --- /dev/null +++ b/internal/mode/usage/markdown/complete_test.go @@ -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)) + }, + ), +) diff --git a/internal/mode/usage/markdown/usage.go b/internal/mode/usage/markdown/platform.go similarity index 92% rename from internal/mode/usage/markdown/usage.go rename to internal/mode/usage/markdown/platform.go index 659feed..b9fb2ac 100644 --- a/internal/mode/usage/markdown/usage.go +++ b/internal/mode/usage/markdown/platform.go @@ -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("
") r.line("Kubernetes") @@ -82,7 +82,7 @@ func (r *renderer) renderKubernetesUsage() { r.line("
") } -func (r *renderer) renderDockerUsage() { +func (r *renderer) renderDockerExample() { r.line("
") r.line("Docker") r.gap() diff --git a/internal/mode/usage/markdown/preamble_test.go b/internal/mode/usage/markdown/preamble_test.go deleted file mode 100644 index e18afac..0000000 --- a/internal/mode/usage/markdown/preamble_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package markdown_test - -import ( - "bytes" - "os" - "path/filepath" - - "github.com/dogmatiq/ferrite" - "github.com/dogmatiq/ferrite/internal/mode" - . "github.com/dogmatiq/ferrite/internal/mode/usage/markdown" - "github.com/dogmatiq/ferrite/variable" - . "github.com/jmalloc/gomegax" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = Describe("func Run()", func() { - var reg *variable.Registry - - BeforeEach(func() { - reg = &variable.Registry{ - Environment: &variable.MemoryEnvironment{}, - } - }) - - DescribeTable( - "it generates the correct preamble", - func( - file string, - setup func(*variable.Registry), - ) { - setup(reg) - - expect, err := os.ReadFile( - filepath.Join( - "testdata", - "markdown", - "preamble", - file, - ), - ) - Expect(err).ShouldNot(HaveOccurred()) - - actual := &bytes.Buffer{} - exited := false - - Run( - mode.Options{ - Registry: reg, - Args: []string{""}, - Out: actual, - Exit: func(code int) { - exited = true - Expect(code).To(Equal(0)) - }, - }, - WithoutUsageExamples(), - ) - ExpectWithOffset(1, actual.String()).To(EqualX(string(expect))) - Expect(exited).To(BeTrue()) - }, - 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)) - }, - ), - ) -}) diff --git a/internal/mode/usage/markdown/relationship.go b/internal/mode/usage/markdown/relationship.go new file mode 100644 index 0000000..46bd268 --- /dev/null +++ b/internal/mode/usage/markdown/relationship.go @@ -0,0 +1,45 @@ +package markdown + +import ( + "github.com/dogmatiq/ferrite/variable" + "golang.org/x/exp/slices" +) + +func (r *specRenderer) renderRelationships() { + c := &relationshipCollator{ + spec: r.spec, + } + + for _, rel := range r.spec.Relationships() { + rel.AcceptVisitor(c) + } + + if len(c.seeAlso) != 0 { + r.ren.gap() + r.ren.line("#### See Also") + r.ren.gap() + + slices.SortFunc( + c.seeAlso, + func(a, b variable.SeeAlso) bool { + return a.To.Name() < b.To.Name() + }, + ) + + for _, rel := range c.seeAlso { + r.ren.renderIndexItem(rel.To) + } + } + +} + +type relationshipCollator struct { + spec variable.Spec + seeAlso []variable.SeeAlso +} + +func (r *relationshipCollator) VisitSeeAlso(rel variable.SeeAlso) { + if r.spec == rel.From { + r.seeAlso = append(r.seeAlso, rel) + } +} diff --git a/internal/mode/usage/markdown/renderer.go b/internal/mode/usage/markdown/renderer.go index e9d30b6..c371407 100644 --- a/internal/mode/usage/markdown/renderer.go +++ b/internal/mode/usage/markdown/renderer.go @@ -45,7 +45,7 @@ func (r *renderer) Render() { if !r.withoutUsageExamples { r.gap() - r.renderUsage() + r.renderPlatformExamples() } } diff --git a/internal/mode/usage/markdown/run_test.go b/internal/mode/usage/markdown/run_test.go new file mode 100644 index 0000000..d62e9b7 --- /dev/null +++ b/internal/mode/usage/markdown/run_test.go @@ -0,0 +1,59 @@ +package markdown_test + +import ( + "bytes" + "os" + "path/filepath" + + "github.com/dogmatiq/ferrite/internal/mode" + . "github.com/dogmatiq/ferrite/internal/mode/usage/markdown" + "github.com/dogmatiq/ferrite/variable" + . "github.com/jmalloc/gomegax" + . "github.com/onsi/gomega" +) + +func tableTest( + path string, + options ...Option, +) func( + file string, + setup func(*variable.Registry), +) { + return func( + file string, + setup func(*variable.Registry), + ) { + reg := &variable.Registry{ + Environment: &variable.MemoryEnvironment{}, + } + + setup(reg) + + expect, err := os.ReadFile( + filepath.Join( + "testdata", + path, + file, + ), + ) + Expect(err).ShouldNot(HaveOccurred()) + + actual := &bytes.Buffer{} + exited := false + + Run( + mode.Options{ + Registry: reg, + Args: []string{""}, + Out: actual, + Exit: func(code int) { + exited = true + Expect(code).To(Equal(0)) + }, + }, + options..., + ) + ExpectWithOffset(1, actual.String()).To(EqualX(string(expect))) + Expect(exited).To(BeTrue()) + } +} diff --git a/internal/mode/usage/markdown/spec.go b/internal/mode/usage/markdown/spec.go index cc8a22f..8f01293 100644 --- a/internal/mode/usage/markdown/spec.go +++ b/internal/mode/usage/markdown/spec.go @@ -41,6 +41,7 @@ func (r *specRenderer) Render() { } r.renderUnimportantDocumentation() + r.renderRelationships() } // VisitNumeric renders the primary requirement for spec that uses the "numeric" diff --git a/internal/mode/usage/markdown/spec_test.go b/internal/mode/usage/markdown/spec_test.go index 872e777..23f23e3 100644 --- a/internal/mode/usage/markdown/spec_test.go +++ b/internal/mode/usage/markdown/spec_test.go @@ -2,13 +2,10 @@ package markdown_test import ( "bytes" - "os" - "path/filepath" "strings" "time" "github.com/dogmatiq/ferrite" - "github.com/dogmatiq/ferrite/internal/mode" . "github.com/dogmatiq/ferrite/internal/mode/usage/markdown" "github.com/dogmatiq/ferrite/variable" . "github.com/jmalloc/gomegax" @@ -16,837 +13,797 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("func Run()", func() { - var reg *variable.Registry +var _ = DescribeTable( + "func Run()", + tableTest( + "spec", + WithoutPreamble(), + WithoutIndex(), + WithoutUsageExamples(), + ), - BeforeEach(func() { - reg = &variable.Registry{ - Environment: &variable.MemoryEnvironment{}, - } - }) + // BOOL - DescribeTable( - "it describes the environment variable", - func( - file string, - setup func(*variable.Registry), - ) { - setup(reg) - - expect, err := os.ReadFile( - filepath.Join( - "testdata", - "markdown", - "spec", - file, - ), - ) - Expect(err).ShouldNot(HaveOccurred()) - - actual := &bytes.Buffer{} - exited := false - - Run( - mode.Options{ - Registry: reg, - Args: []string{""}, - Out: actual, - Exit: func(code int) { - exited = true - Expect(code).To(Equal(0)) - }, - }, - WithoutPreamble(), - WithoutIndex(), - WithoutUsageExamples(), - ) - ExpectWithOffset(1, actual.String()).To(EqualX(string(expect))) - Expect(exited).To(BeTrue()) + Entry( + "bool + optional + default", + "bool/with-default.md", + func(reg *variable.Registry) { + ferrite. + Bool("DEBUG", "enable or disable debugging features"). + WithDefault(false). + Optional(variable.WithRegistry(reg)) }, + ), + Entry( + "bool + optional", + "bool/optional.md", + func(reg *variable.Registry) { + ferrite. + Bool("DEBUG", "enable or disable debugging features"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "bool + required + default", + "bool/with-default.md", + func(reg *variable.Registry) { + ferrite. + Bool("DEBUG", "enable or disable debugging features"). + WithDefault(false). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "bool + required", + "bool/required.md", + func(reg *variable.Registry) { + ferrite. + Bool("DEBUG", "enable or disable debugging features"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "bool + deprecated + default", + "bool/deprecated-with-default.md", + func(reg *variable.Registry) { + ferrite. + Bool("DEBUG", "enable or disable debugging features"). + WithDefault(false). + Deprecated(variable.WithRegistry(reg)) + }, + ), + Entry( + "bool + deprecated", + "bool/deprecated.md", + func(reg *variable.Registry) { + ferrite. + Bool("DEBUG", "enable or disable debugging features"). + Deprecated(variable.WithRegistry(reg)) + }, + ), - // BOOL - - Entry( - "bool + optional + default", - "bool/with-default.md", - func(reg *variable.Registry) { - ferrite. - Bool("DEBUG", "enable or disable debugging features"). - WithDefault(false). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "bool + optional", - "bool/optional.md", - func(reg *variable.Registry) { - ferrite. - Bool("DEBUG", "enable or disable debugging features"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "bool + required + default", - "bool/with-default.md", - func(reg *variable.Registry) { - ferrite. - Bool("DEBUG", "enable or disable debugging features"). - WithDefault(false). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "bool + required", - "bool/required.md", - func(reg *variable.Registry) { - ferrite. - Bool("DEBUG", "enable or disable debugging features"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "bool + deprecated + default", - "bool/deprecated-with-default.md", - func(reg *variable.Registry) { - ferrite. - Bool("DEBUG", "enable or disable debugging features"). - WithDefault(false). - Deprecated(variable.WithRegistry(reg)) - }, - ), - Entry( - "bool + deprecated", - "bool/deprecated.md", - func(reg *variable.Registry) { - ferrite. - Bool("DEBUG", "enable or disable debugging features"). - Deprecated(variable.WithRegistry(reg)) - }, - ), - - // ENUM + // ENUM - Entry( - "enum + optional + default", - "enum/with-default.md", - func(reg *variable.Registry) { - ferrite. - Enum("LOG_LEVEL", "the minimum log level to record"). - WithMember("debug", "show information for developers"). - WithMember("info", "standard log messages"). - WithMember("warn", "important, but don't need individual human review"). - WithMember("error", "a healthy application shouldn't produce any errors"). - WithMember("fatal", "the application cannot proceed"). - WithDefault("error"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "enum + optional", - "enum/optional.md", - func(reg *variable.Registry) { - ferrite. - Enum("LOG_LEVEL", "the minimum log level to record"). - WithMember("debug", "show information for developers"). - WithMember("info", "standard log messages"). - WithMember("warn", "important, but don't need individual human review"). - WithMember("error", "a healthy application shouldn't produce any errors"). - WithMember("fatal", "the application cannot proceed"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "enum + required + default", - "enum/with-default.md", - func(reg *variable.Registry) { - ferrite. - Enum("LOG_LEVEL", "the minimum log level to record"). - WithMember("debug", "show information for developers"). - WithMember("info", "standard log messages"). - WithMember("warn", "important, but don't need individual human review"). - WithMember("error", "a healthy application shouldn't produce any errors"). - WithMember("fatal", "the application cannot proceed"). - WithDefault("error"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "enum + required", - "enum/required.md", - func(reg *variable.Registry) { - ferrite. - Enum("LOG_LEVEL", "the minimum log level to record"). - WithMember("debug", "show information for developers"). - WithMember("info", "standard log messages"). - WithMember("warn", "important, but don't need individual human review"). - WithMember("error", "a healthy application shouldn't produce any errors"). - WithMember("fatal", "the application cannot proceed"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "enum + deprecated + default", - "enum/deprecated-with-default.md", - func(reg *variable.Registry) { - ferrite. - Enum("LOG_LEVEL", "the minimum log level to record"). - WithMember("debug", "show information for developers"). - WithMember("info", "standard log messages"). - WithMember("warn", "important, but don't need individual human review"). - WithMember("error", "a healthy application shouldn't produce any errors"). - WithMember("fatal", "the application cannot proceed"). - WithDefault("error"). - Deprecated(variable.WithRegistry(reg)) - }, - ), - Entry( - "enum + deprecated", - "enum/deprecated.md", - func(reg *variable.Registry) { - ferrite. - Enum("LOG_LEVEL", "the minimum log level to record"). - WithMember("debug", "show information for developers"). - WithMember("info", "standard log messages"). - WithMember("warn", "important, but don't need individual human review"). - WithMember("error", "a healthy application shouldn't produce any errors"). - WithMember("fatal", "the application cannot proceed"). - Deprecated(variable.WithRegistry(reg)) - }, - ), + Entry( + "enum + optional + default", + "enum/with-default.md", + func(reg *variable.Registry) { + ferrite. + Enum("LOG_LEVEL", "the minimum log level to record"). + WithMember("debug", "show information for developers"). + WithMember("info", "standard log messages"). + WithMember("warn", "important, but don't need individual human review"). + WithMember("error", "a healthy application shouldn't produce any errors"). + WithMember("fatal", "the application cannot proceed"). + WithDefault("error"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "enum + optional", + "enum/optional.md", + func(reg *variable.Registry) { + ferrite. + Enum("LOG_LEVEL", "the minimum log level to record"). + WithMember("debug", "show information for developers"). + WithMember("info", "standard log messages"). + WithMember("warn", "important, but don't need individual human review"). + WithMember("error", "a healthy application shouldn't produce any errors"). + WithMember("fatal", "the application cannot proceed"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "enum + required + default", + "enum/with-default.md", + func(reg *variable.Registry) { + ferrite. + Enum("LOG_LEVEL", "the minimum log level to record"). + WithMember("debug", "show information for developers"). + WithMember("info", "standard log messages"). + WithMember("warn", "important, but don't need individual human review"). + WithMember("error", "a healthy application shouldn't produce any errors"). + WithMember("fatal", "the application cannot proceed"). + WithDefault("error"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "enum + required", + "enum/required.md", + func(reg *variable.Registry) { + ferrite. + Enum("LOG_LEVEL", "the minimum log level to record"). + WithMember("debug", "show information for developers"). + WithMember("info", "standard log messages"). + WithMember("warn", "important, but don't need individual human review"). + WithMember("error", "a healthy application shouldn't produce any errors"). + WithMember("fatal", "the application cannot proceed"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "enum + deprecated + default", + "enum/deprecated-with-default.md", + func(reg *variable.Registry) { + ferrite. + Enum("LOG_LEVEL", "the minimum log level to record"). + WithMember("debug", "show information for developers"). + WithMember("info", "standard log messages"). + WithMember("warn", "important, but don't need individual human review"). + WithMember("error", "a healthy application shouldn't produce any errors"). + WithMember("fatal", "the application cannot proceed"). + WithDefault("error"). + Deprecated(variable.WithRegistry(reg)) + }, + ), + Entry( + "enum + deprecated", + "enum/deprecated.md", + func(reg *variable.Registry) { + ferrite. + Enum("LOG_LEVEL", "the minimum log level to record"). + WithMember("debug", "show information for developers"). + WithMember("info", "standard log messages"). + WithMember("warn", "important, but don't need individual human review"). + WithMember("error", "a healthy application shouldn't produce any errors"). + WithMember("fatal", "the application cannot proceed"). + Deprecated(variable.WithRegistry(reg)) + }, + ), - // DURATION + // DURATION - Entry( - "duration + optional + default", - "duration/with-default.md", - func(reg *variable.Registry) { - ferrite. - Duration("GRPC_TIMEOUT", "gRPC request timeout"). - WithDefault(10 * time.Millisecond). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "duration + optional", - "duration/optional.md", - func(reg *variable.Registry) { - ferrite. - Duration("GRPC_TIMEOUT", "gRPC request timeout"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "duration + required + default", - "duration/with-default.md", - func(reg *variable.Registry) { - ferrite. - Duration("GRPC_TIMEOUT", "gRPC request timeout"). - WithDefault(10 * time.Millisecond). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "duration + required", - "duration/required.md", - func(reg *variable.Registry) { - ferrite. - Duration("GRPC_TIMEOUT", "gRPC request timeout"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "duration + maximum", - "duration/with-max.md", - func(reg *variable.Registry) { - ferrite. - Duration("GRPC_TIMEOUT", "gRPC request timeout"). - WithMaximum(24 * time.Hour). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "duration + deprecated + default", - "duration/deprecated-with-default.md", - func(reg *variable.Registry) { - ferrite. - Duration("GRPC_TIMEOUT", "gRPC request timeout"). - WithDefault(10 * time.Millisecond). - Deprecated(variable.WithRegistry(reg)) - }, - ), - Entry( - "duration + deprecated", - "duration/deprecated.md", - func(reg *variable.Registry) { - ferrite. - Duration("GRPC_TIMEOUT", "gRPC request timeout"). - Deprecated(variable.WithRegistry(reg)) - }, - ), + Entry( + "duration + optional + default", + "duration/with-default.md", + func(reg *variable.Registry) { + ferrite. + Duration("GRPC_TIMEOUT", "gRPC request timeout"). + WithDefault(10 * time.Millisecond). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "duration + optional", + "duration/optional.md", + func(reg *variable.Registry) { + ferrite. + Duration("GRPC_TIMEOUT", "gRPC request timeout"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "duration + required + default", + "duration/with-default.md", + func(reg *variable.Registry) { + ferrite. + Duration("GRPC_TIMEOUT", "gRPC request timeout"). + WithDefault(10 * time.Millisecond). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "duration + required", + "duration/required.md", + func(reg *variable.Registry) { + ferrite. + Duration("GRPC_TIMEOUT", "gRPC request timeout"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "duration + maximum", + "duration/with-max.md", + func(reg *variable.Registry) { + ferrite. + Duration("GRPC_TIMEOUT", "gRPC request timeout"). + WithMaximum(24 * time.Hour). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "duration + deprecated + default", + "duration/deprecated-with-default.md", + func(reg *variable.Registry) { + ferrite. + Duration("GRPC_TIMEOUT", "gRPC request timeout"). + WithDefault(10 * time.Millisecond). + Deprecated(variable.WithRegistry(reg)) + }, + ), + Entry( + "duration + deprecated", + "duration/deprecated.md", + func(reg *variable.Registry) { + ferrite. + Duration("GRPC_TIMEOUT", "gRPC request timeout"). + Deprecated(variable.WithRegistry(reg)) + }, + ), - // KUBERNETES SERVICE + // KUBERNETES SERVICE - Entry( - "k8s service + optional + default", - "k8s-service/with-default.md", - func(reg *variable.Registry) { - ferrite. - KubernetesService("redis"). - WithDefault("redis.example.org", "6379"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "k8s service + optional", - "k8s-service/optional.md", - func(reg *variable.Registry) { - ferrite. - KubernetesService("redis"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "k8s service + required + default", - "k8s-service/with-default.md", - func(reg *variable.Registry) { - ferrite. - KubernetesService("redis"). - WithDefault("redis.example.org", "6379"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "k8s service + required", - "k8s-service/required.md", - func(reg *variable.Registry) { - ferrite. - KubernetesService("redis"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "k8s service + deprecated + default", - "k8s-service/deprecated-with-default.md", - func(reg *variable.Registry) { - ferrite. - KubernetesService("redis"). - WithDefault("redis.example.org", "6379"). - Deprecated(variable.WithRegistry(reg)) - }, - ), - Entry( - "k8s service + deprecated", - "k8s-service/deprecated.md", - func(reg *variable.Registry) { - ferrite. - KubernetesService("redis"). - Deprecated(variable.WithRegistry(reg)) - }, - ), - // NETWORK PORT + Entry( + "k8s service + optional + default", + "k8s-service/with-default.md", + func(reg *variable.Registry) { + ferrite. + KubernetesService("redis"). + WithDefault("redis.example.org", "6379"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "k8s service + optional", + "k8s-service/optional.md", + func(reg *variable.Registry) { + ferrite. + KubernetesService("redis"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "k8s service + required + default", + "k8s-service/with-default.md", + func(reg *variable.Registry) { + ferrite. + KubernetesService("redis"). + WithDefault("redis.example.org", "6379"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "k8s service + required", + "k8s-service/required.md", + func(reg *variable.Registry) { + ferrite. + KubernetesService("redis"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "k8s service + deprecated + default", + "k8s-service/deprecated-with-default.md", + func(reg *variable.Registry) { + ferrite. + KubernetesService("redis"). + WithDefault("redis.example.org", "6379"). + Deprecated(variable.WithRegistry(reg)) + }, + ), + Entry( + "k8s service + deprecated", + "k8s-service/deprecated.md", + func(reg *variable.Registry) { + ferrite. + KubernetesService("redis"). + Deprecated(variable.WithRegistry(reg)) + }, + ), + // NETWORK PORT - Entry( - "network port + optional + default", - "network-port/with-default.md", - func(reg *variable.Registry) { - ferrite. - NetworkPort("PORT", "listen port for the HTTP server"). - WithDefault("8080"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "network port + optional", - "network-port/optional.md", - func(reg *variable.Registry) { - ferrite. - NetworkPort("PORT", "listen port for the HTTP server"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "network port + required + default", - "network-port/with-default.md", - func(reg *variable.Registry) { - ferrite. - NetworkPort("PORT", "listen port for the HTTP server"). - WithDefault("8080"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "network port + required", - "network-port/required.md", - func(reg *variable.Registry) { - ferrite. - NetworkPort("PORT", "listen port for the HTTP server"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "network port + deprecated + default", - "network-port/deprecated-with-default.md", - func(reg *variable.Registry) { - ferrite. - NetworkPort("PORT", "listen port for the HTTP server"). - WithDefault("8080"). - Deprecated(variable.WithRegistry(reg)) - }, - ), - Entry( - "network port + deprecated", - "network-port/deprecated.md", - func(reg *variable.Registry) { - ferrite. - NetworkPort("PORT", "listen port for the HTTP server"). - Deprecated(variable.WithRegistry(reg)) - }, - ), + Entry( + "network port + optional + default", + "network-port/with-default.md", + func(reg *variable.Registry) { + ferrite. + NetworkPort("PORT", "listen port for the HTTP server"). + WithDefault("8080"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "network port + optional", + "network-port/optional.md", + func(reg *variable.Registry) { + ferrite. + NetworkPort("PORT", "listen port for the HTTP server"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "network port + required + default", + "network-port/with-default.md", + func(reg *variable.Registry) { + ferrite. + NetworkPort("PORT", "listen port for the HTTP server"). + WithDefault("8080"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "network port + required", + "network-port/required.md", + func(reg *variable.Registry) { + ferrite. + NetworkPort("PORT", "listen port for the HTTP server"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "network port + deprecated + default", + "network-port/deprecated-with-default.md", + func(reg *variable.Registry) { + ferrite. + NetworkPort("PORT", "listen port for the HTTP server"). + WithDefault("8080"). + Deprecated(variable.WithRegistry(reg)) + }, + ), + Entry( + "network port + deprecated", + "network-port/deprecated.md", + func(reg *variable.Registry) { + ferrite. + NetworkPort("PORT", "listen port for the HTTP server"). + Deprecated(variable.WithRegistry(reg)) + }, + ), - // NUMBER - FLOAT + // NUMBER - FLOAT - Entry( - "float + optional + default", - "float/with-default.md", - func(reg *variable.Registry) { - ferrite. - Float[float32]("WEIGHT", "weighting for this node"). - WithDefault(123.5). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "float + optional", - "float/optional.md", - func(reg *variable.Registry) { - ferrite. - Float[float32]("WEIGHT", "weighting for this node"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "float + required + default", - "float/with-default.md", - func(reg *variable.Registry) { - ferrite. - Float[float32]("WEIGHT", "weighting for this node"). - WithDefault(123.5). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "float + required", - "float/required.md", - func(reg *variable.Registry) { - ferrite. - Float[float32]("WEIGHT", "weighting for this node"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "float + min", - "float/with-min.md", - func(reg *variable.Registry) { - ferrite. - Float[float32]("WEIGHT", "weighting for this node"). - WithMinimum(-10.5). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "float + max", - "float/with-max.md", - func(reg *variable.Registry) { - ferrite. - Float[float32]("WEIGHT", "weighting for this node"). - WithMaximum(+20.5). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "float + min/max", - "float/with-minmax.md", - func(reg *variable.Registry) { - ferrite. - Float[float32]("WEIGHT", "weighting for this node"). - WithMinimum(-10.5). - WithMaximum(+20.5). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "float + deprecated + default", - "float/deprecated-with-default.md", - func(reg *variable.Registry) { - ferrite. - Float[float32]("WEIGHT", "weighting for this node"). - WithDefault(123.5). - Deprecated(variable.WithRegistry(reg)) - }, - ), - Entry( - "float + deprecated", - "float/deprecated.md", - func(reg *variable.Registry) { - ferrite. - Float[float32]("WEIGHT", "weighting for this node"). - Deprecated(variable.WithRegistry(reg)) - }, - ), + Entry( + "float + optional + default", + "float/with-default.md", + func(reg *variable.Registry) { + ferrite. + Float[float32]("WEIGHT", "weighting for this node"). + WithDefault(123.5). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "float + optional", + "float/optional.md", + func(reg *variable.Registry) { + ferrite. + Float[float32]("WEIGHT", "weighting for this node"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "float + required + default", + "float/with-default.md", + func(reg *variable.Registry) { + ferrite. + Float[float32]("WEIGHT", "weighting for this node"). + WithDefault(123.5). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "float + required", + "float/required.md", + func(reg *variable.Registry) { + ferrite. + Float[float32]("WEIGHT", "weighting for this node"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "float + min", + "float/with-min.md", + func(reg *variable.Registry) { + ferrite. + Float[float32]("WEIGHT", "weighting for this node"). + WithMinimum(-10.5). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "float + max", + "float/with-max.md", + func(reg *variable.Registry) { + ferrite. + Float[float32]("WEIGHT", "weighting for this node"). + WithMaximum(+20.5). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "float + min/max", + "float/with-minmax.md", + func(reg *variable.Registry) { + ferrite. + Float[float32]("WEIGHT", "weighting for this node"). + WithMinimum(-10.5). + WithMaximum(+20.5). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "float + deprecated + default", + "float/deprecated-with-default.md", + func(reg *variable.Registry) { + ferrite. + Float[float32]("WEIGHT", "weighting for this node"). + WithDefault(123.5). + Deprecated(variable.WithRegistry(reg)) + }, + ), + Entry( + "float + deprecated", + "float/deprecated.md", + func(reg *variable.Registry) { + ferrite. + Float[float32]("WEIGHT", "weighting for this node"). + Deprecated(variable.WithRegistry(reg)) + }, + ), - // NUMBER - SIGNED + // NUMBER - SIGNED - Entry( - "signed + optional + default", - "signed/with-default.md", - func(reg *variable.Registry) { - ferrite. - Signed[int8]("WEIGHT", "weighting for this node"). - WithDefault(100). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "signed + optional", - "signed/optional.md", - func(reg *variable.Registry) { - ferrite. - Signed[int8]("WEIGHT", "weighting for this node"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "signed + required + default", - "signed/with-default.md", - func(reg *variable.Registry) { - ferrite. - Signed[int8]("WEIGHT", "weighting for this node"). - WithDefault(100). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "signed + required", - "signed/required.md", - func(reg *variable.Registry) { - ferrite. - Signed[int8]("WEIGHT", "weighting for this node"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "signed + min", - "signed/with-min.md", - func(reg *variable.Registry) { - ferrite. - Signed[int8]("WEIGHT", "weighting for this node"). - WithMinimum(-10). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "signed + max", - "signed/with-max.md", - func(reg *variable.Registry) { - ferrite. - Signed[int8]("WEIGHT", "weighting for this node"). - WithMaximum(+20). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "signed + min/max", - "signed/with-minmax.md", - func(reg *variable.Registry) { - ferrite. - Signed[int8]("WEIGHT", "weighting for this node"). - WithMinimum(-10). - WithMaximum(+20). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "signed + deprecated + default", - "signed/deprecated-with-default.md", - func(reg *variable.Registry) { - ferrite. - Signed[int8]("WEIGHT", "weighting for this node"). - WithDefault(100). - Deprecated(variable.WithRegistry(reg)) - }, - ), - Entry( - "signed + deprecated", - "signed/deprecated.md", - func(reg *variable.Registry) { - ferrite. - Signed[int8]("WEIGHT", "weighting for this node"). - Deprecated(variable.WithRegistry(reg)) - }, - ), + Entry( + "signed + optional + default", + "signed/with-default.md", + func(reg *variable.Registry) { + ferrite. + Signed[int8]("WEIGHT", "weighting for this node"). + WithDefault(100). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "signed + optional", + "signed/optional.md", + func(reg *variable.Registry) { + ferrite. + Signed[int8]("WEIGHT", "weighting for this node"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "signed + required + default", + "signed/with-default.md", + func(reg *variable.Registry) { + ferrite. + Signed[int8]("WEIGHT", "weighting for this node"). + WithDefault(100). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "signed + required", + "signed/required.md", + func(reg *variable.Registry) { + ferrite. + Signed[int8]("WEIGHT", "weighting for this node"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "signed + min", + "signed/with-min.md", + func(reg *variable.Registry) { + ferrite. + Signed[int8]("WEIGHT", "weighting for this node"). + WithMinimum(-10). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "signed + max", + "signed/with-max.md", + func(reg *variable.Registry) { + ferrite. + Signed[int8]("WEIGHT", "weighting for this node"). + WithMaximum(+20). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "signed + min/max", + "signed/with-minmax.md", + func(reg *variable.Registry) { + ferrite. + Signed[int8]("WEIGHT", "weighting for this node"). + WithMinimum(-10). + WithMaximum(+20). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "signed + deprecated + default", + "signed/deprecated-with-default.md", + func(reg *variable.Registry) { + ferrite. + Signed[int8]("WEIGHT", "weighting for this node"). + WithDefault(100). + Deprecated(variable.WithRegistry(reg)) + }, + ), + Entry( + "signed + deprecated", + "signed/deprecated.md", + func(reg *variable.Registry) { + ferrite. + Signed[int8]("WEIGHT", "weighting for this node"). + Deprecated(variable.WithRegistry(reg)) + }, + ), - // NUMBER - UNSIGNED + // NUMBER - UNSIGNED - Entry( - "unsigned + optional + default", - "unsigned/with-default.md", - func(reg *variable.Registry) { - ferrite. - Unsigned[uint16]("WEIGHT", "weighting for this node"). - WithDefault(900). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "unsigned + optional", - "unsigned/optional.md", - func(reg *variable.Registry) { - ferrite. - Unsigned[uint16]("WEIGHT", "weighting for this node"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "unsigned + required + default", - "unsigned/with-default.md", - func(reg *variable.Registry) { - ferrite. - Unsigned[uint16]("WEIGHT", "weighting for this node"). - WithDefault(900). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "unsigned + required", - "unsigned/required.md", - func(reg *variable.Registry) { - ferrite. - Unsigned[uint16]("WEIGHT", "weighting for this node"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "unsigned + min", - "unsigned/with-min.md", - func(reg *variable.Registry) { - ferrite. - Unsigned[uint16]("WEIGHT", "weighting for this node"). - WithMinimum(10). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "unsigned + max", - "unsigned/with-max.md", - func(reg *variable.Registry) { - ferrite. - Unsigned[uint16]("WEIGHT", "weighting for this node"). - WithMaximum(20). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "unsigned + min/max", - "unsigned/with-minmax.md", - func(reg *variable.Registry) { - ferrite. - Unsigned[uint16]("WEIGHT", "weighting for this node"). - WithMinimum(10). - WithMaximum(20). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "unsigned + deprecated + default", - "unsigned/deprecated-with-default.md", - func(reg *variable.Registry) { - ferrite. - Unsigned[uint16]("WEIGHT", "weighting for this node"). - WithDefault(900). - Deprecated(variable.WithRegistry(reg)) - }, - ), - Entry( - "unsigned + deprecated", - "unsigned/deprecated.md", - func(reg *variable.Registry) { - ferrite. - Unsigned[uint16]("WEIGHT", "weighting for this node"). - Deprecated(variable.WithRegistry(reg)) - }, - ), + Entry( + "unsigned + optional + default", + "unsigned/with-default.md", + func(reg *variable.Registry) { + ferrite. + Unsigned[uint16]("WEIGHT", "weighting for this node"). + WithDefault(900). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "unsigned + optional", + "unsigned/optional.md", + func(reg *variable.Registry) { + ferrite. + Unsigned[uint16]("WEIGHT", "weighting for this node"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "unsigned + required + default", + "unsigned/with-default.md", + func(reg *variable.Registry) { + ferrite. + Unsigned[uint16]("WEIGHT", "weighting for this node"). + WithDefault(900). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "unsigned + required", + "unsigned/required.md", + func(reg *variable.Registry) { + ferrite. + Unsigned[uint16]("WEIGHT", "weighting for this node"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "unsigned + min", + "unsigned/with-min.md", + func(reg *variable.Registry) { + ferrite. + Unsigned[uint16]("WEIGHT", "weighting for this node"). + WithMinimum(10). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "unsigned + max", + "unsigned/with-max.md", + func(reg *variable.Registry) { + ferrite. + Unsigned[uint16]("WEIGHT", "weighting for this node"). + WithMaximum(20). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "unsigned + min/max", + "unsigned/with-minmax.md", + func(reg *variable.Registry) { + ferrite. + Unsigned[uint16]("WEIGHT", "weighting for this node"). + WithMinimum(10). + WithMaximum(20). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "unsigned + deprecated + default", + "unsigned/deprecated-with-default.md", + func(reg *variable.Registry) { + ferrite. + Unsigned[uint16]("WEIGHT", "weighting for this node"). + WithDefault(900). + Deprecated(variable.WithRegistry(reg)) + }, + ), + Entry( + "unsigned + deprecated", + "unsigned/deprecated.md", + func(reg *variable.Registry) { + ferrite. + Unsigned[uint16]("WEIGHT", "weighting for this node"). + Deprecated(variable.WithRegistry(reg)) + }, + ), - // STRING + // STRING - Entry( - "string + optional + default", - "string/with-default.md", - func(reg *variable.Registry) { - ferrite. - String("READ_DSN", "database connection string for read-models"). - WithDefault("host=localhost dbname=readmodels user=projector"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "string + optional", - "string/optional.md", - func(reg *variable.Registry) { - ferrite. - String("READ_DSN", "database connection string for read-models"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "string + required + default", - "string/with-default.md", - func(reg *variable.Registry) { - ferrite. - String("READ_DSN", "database connection string for read-models"). - WithDefault("host=localhost dbname=readmodels user=projector"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "string + required", - "string/required.md", - func(reg *variable.Registry) { - ferrite. - String("READ_DSN", "database connection string for read-models"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "string + deprecated + default", - "string/deprecated-with-default.md", - func(reg *variable.Registry) { - ferrite. - String("READ_DSN", "database connection string for read-models"). - WithDefault("host=localhost dbname=readmodels user=projector"). - Deprecated(variable.WithRegistry(reg)) - }, - ), - Entry( - "string + deprecated", - "string/deprecated.md", - func(reg *variable.Registry) { - ferrite. - String("READ_DSN", "database connection string for read-models"). - Deprecated(variable.WithRegistry(reg)) - }, - ), - Entry( - "string + sensitive + optional + default", - "string/sensitive-with-default.md", - func(reg *variable.Registry) { - ferrite. - String("PASSWORD", "a very secret password"). - WithDefault("hunter2"). - WithSensitiveContent(). - Optional( - variable.WithRegistry(reg), - ) - }, - ), - Entry( - "string + sensitive + optional", - "string/sensitive-optional.md", - func(reg *variable.Registry) { - ferrite. - String("PASSWORD", "a very secret password"). - WithSensitiveContent(). - Optional( - variable.WithRegistry(reg), - ) - }, - ), - Entry( - "string + sensitive + required + default", - "string/sensitive-with-default.md", - func(reg *variable.Registry) { - ferrite. - String("PASSWORD", "a very secret password"). - WithDefault("hunter2"). - WithSensitiveContent(). - Required( - variable.WithRegistry(reg), - ) - }, - ), - Entry( - "string + sensitive + required", - "string/sensitive-required.md", - func(reg *variable.Registry) { - ferrite. - String("PASSWORD", "a very secret password"). - WithSensitiveContent(). - Required( - variable.WithRegistry(reg), - ) - }, - ), + Entry( + "string + optional + default", + "string/with-default.md", + func(reg *variable.Registry) { + ferrite. + String("READ_DSN", "database connection string for read-models"). + WithDefault("host=localhost dbname=readmodels user=projector"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "string + optional", + "string/optional.md", + func(reg *variable.Registry) { + ferrite. + String("READ_DSN", "database connection string for read-models"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "string + required + default", + "string/with-default.md", + func(reg *variable.Registry) { + ferrite. + String("READ_DSN", "database connection string for read-models"). + WithDefault("host=localhost dbname=readmodels user=projector"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "string + required", + "string/required.md", + func(reg *variable.Registry) { + ferrite. + String("READ_DSN", "database connection string for read-models"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "string + deprecated + default", + "string/deprecated-with-default.md", + func(reg *variable.Registry) { + ferrite. + String("READ_DSN", "database connection string for read-models"). + WithDefault("host=localhost dbname=readmodels user=projector"). + Deprecated(variable.WithRegistry(reg)) + }, + ), + Entry( + "string + deprecated", + "string/deprecated.md", + func(reg *variable.Registry) { + ferrite. + String("READ_DSN", "database connection string for read-models"). + Deprecated(variable.WithRegistry(reg)) + }, + ), + Entry( + "string + sensitive + optional + default", + "string/sensitive-with-default.md", + func(reg *variable.Registry) { + ferrite. + String("PASSWORD", "a very secret password"). + WithDefault("hunter2"). + WithSensitiveContent(). + Optional( + variable.WithRegistry(reg), + ) + }, + ), + Entry( + "string + sensitive + optional", + "string/sensitive-optional.md", + func(reg *variable.Registry) { + ferrite. + String("PASSWORD", "a very secret password"). + WithSensitiveContent(). + Optional( + variable.WithRegistry(reg), + ) + }, + ), + Entry( + "string + sensitive + required + default", + "string/sensitive-with-default.md", + func(reg *variable.Registry) { + ferrite. + String("PASSWORD", "a very secret password"). + WithDefault("hunter2"). + WithSensitiveContent(). + Required( + variable.WithRegistry(reg), + ) + }, + ), + Entry( + "string + sensitive + required", + "string/sensitive-required.md", + func(reg *variable.Registry) { + ferrite. + String("PASSWORD", "a very secret password"). + WithSensitiveContent(). + Required( + variable.WithRegistry(reg), + ) + }, + ), - // URL + // URL - Entry( - "url + optional + default", - "url/with-default.md", - func(reg *variable.Registry) { - ferrite. - URL("API_URL", "the URL of the REST API"). - WithDefault("http://localhost:8080"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "url + optional", - "url/optional.md", - func(reg *variable.Registry) { - ferrite. - URL("API_URL", "the URL of the REST API"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "url + required + default", - "url/with-default.md", - func(reg *variable.Registry) { - ferrite. - URL("API_URL", "the URL of the REST API"). - WithDefault("http://localhost:8080"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "url + required", - "url/required.md", - func(reg *variable.Registry) { - ferrite. - URL("API_URL", "the URL of the REST API"). - Required(variable.WithRegistry(reg)) - }, - ), - Entry( - "url + deprecated + default", - "url/deprecated-with-default.md", - func(reg *variable.Registry) { - ferrite. - URL("API_URL", "the URL of the REST API"). - WithDefault("http://localhost:8080"). - Deprecated(variable.WithRegistry(reg)) - }, - ), - Entry( - "url + deprecated", - "url/deprecated.md", - func(reg *variable.Registry) { - ferrite. - URL("API_URL", "the URL of the REST API"). - Deprecated(variable.WithRegistry(reg)) - }, - ), - ) -}) + Entry( + "url + optional + default", + "url/with-default.md", + func(reg *variable.Registry) { + ferrite. + URL("API_URL", "the URL of the REST API"). + WithDefault("http://localhost:8080"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "url + optional", + "url/optional.md", + func(reg *variable.Registry) { + ferrite. + URL("API_URL", "the URL of the REST API"). + Optional(variable.WithRegistry(reg)) + }, + ), + Entry( + "url + required + default", + "url/with-default.md", + func(reg *variable.Registry) { + ferrite. + URL("API_URL", "the URL of the REST API"). + WithDefault("http://localhost:8080"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "url + required", + "url/required.md", + func(reg *variable.Registry) { + ferrite. + URL("API_URL", "the URL of the REST API"). + Required(variable.WithRegistry(reg)) + }, + ), + Entry( + "url + deprecated + default", + "url/deprecated-with-default.md", + func(reg *variable.Registry) { + ferrite. + URL("API_URL", "the URL of the REST API"). + WithDefault("http://localhost:8080"). + Deprecated(variable.WithRegistry(reg)) + }, + ), + Entry( + "url + deprecated", + "url/deprecated.md", + func(reg *variable.Registry) { + ferrite. + URL("API_URL", "the URL of the REST API"). + Deprecated(variable.WithRegistry(reg)) + }, + ), +) // expectLines verifies that text consists of the given lines. func expectLines(buf *bytes.Buffer, lines ...string) { diff --git a/internal/mode/usage/markdown/syntax.go b/internal/mode/usage/markdown/syntax.go deleted file mode 100644 index b868feb..0000000 --- a/internal/mode/usage/markdown/syntax.go +++ /dev/null @@ -1 +0,0 @@ -package markdown diff --git a/internal/mode/usage/markdown/testdata/markdown/preamble/empty.md b/internal/mode/usage/markdown/testdata/complete/empty.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/preamble/empty.md rename to internal/mode/usage/markdown/testdata/complete/empty.md diff --git a/internal/mode/usage/markdown/testdata/complete/non-normative.md b/internal/mode/usage/markdown/testdata/complete/non-normative.md new file mode 100644 index 0000000..859dea4 --- /dev/null +++ b/internal/mode/usage/markdown/testdata/complete/non-normative.md @@ -0,0 +1,107 @@ +# Environment Variables + +This document describes the environment variables used by ``. + +If any of the environment variable values do not meet the requirements herein, +the application will print usage information to `STDERR` then exit with a +non-zero exit code. Please note that **undefined** variables and **empty** +values are considered equivalent. + +⚠️ This document includes **non-normative** example values. While these values +are syntactically correct, they may not be meaningful to this application. + +⚠️ The application may consume other undocumented environment variables; this +document only shows those variables declared using [Ferrite]. + +The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, +**SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this +document are to be interpreted as described in [RFC 2119]. + +## Index + +- [`READ_DSN`](#READ_DSN) — database connection string for read-models + +## Specification + +### `READ_DSN` + +> database connection string for read-models + +The `READ_DSN` variable **MUST NOT** be left undefined. + +```bash +export READ_DSN=foo # (non-normative) +``` + +## Usage Examples + +
+Kubernetes + +This example shows how to define the environment variables needed by `` +on a [Kubernetes container] within a Kubenetes deployment manifest. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: example-deployment +spec: + template: + spec: + containers: + - name: example-container + env: + - name: READ_DSN # database connection string for read-models + value: foo +``` + +Alternatively, the environment variables can be defined within a [config map][kubernetes config map] +then referenced a deployment manifest using `configMapRef`. + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: example-config-map +data: + READ_DSN: foo # database connection string for read-models +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: example-deployment +spec: + template: + spec: + containers: + - name: example-container + envFrom: + - configMapRef: + name: example-config-map +``` + +
+ +
+Docker + +This example shows how to define the environment variables needed by `` +when running as a [Docker service] defined in a Docker compose file. + +```yaml +service: + example-service: + environment: + READ_DSN: foo # database connection string for read-models +``` + +
+ + + +[docker service]: https://docs.docker.com/compose/environment-variables/#set-environment-variables-in-containers +[ferrite]: https://github.com/dogmatiq/ferrite +[kubernetes config map]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#configure-all-key-value-pairs-in-a-configmap-as-container-environment-variables +[kubernetes container]: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container +[rfc 2119]: https://www.rfc-editor.org/rfc/rfc2119.html diff --git a/internal/mode/usage/markdown/testdata/markdown/usage/usage-shows-default.md b/internal/mode/usage/markdown/testdata/complete/platform-examples-use-defaults.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/usage/usage-shows-default.md rename to internal/mode/usage/markdown/testdata/complete/platform-examples-use-defaults.md diff --git a/internal/mode/usage/markdown/testdata/markdown/usage/usage.md b/internal/mode/usage/markdown/testdata/complete/platform-examples.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/usage/usage.md rename to internal/mode/usage/markdown/testdata/complete/platform-examples.md diff --git a/internal/mode/usage/markdown/testdata/complete/see-also.md b/internal/mode/usage/markdown/testdata/complete/see-also.md new file mode 100644 index 0000000..c3493db --- /dev/null +++ b/internal/mode/usage/markdown/testdata/complete/see-also.md @@ -0,0 +1,127 @@ +# Environment Variables + +This document describes the environment variables used by ``. + +If any of the environment variable values do not meet the requirements herein, +the application will print usage information to `STDERR` then exit with a +non-zero exit code. Please note that **undefined** variables and **empty** +values are considered equivalent. + +⚠️ The application may consume other undocumented environment variables; this +document only shows those variables declared using [Ferrite]. + +The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, +**SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this +document are to be interpreted as described in [RFC 2119]. + +## Index + +- [`DEBUG`](#DEBUG) — enable or disable debugging features +- [`VERBOSE`](#VERBOSE) — enable verbose logging + +## Specification + +### `DEBUG` + +> enable or disable debugging features + +The `DEBUG` variable **MAY** be left undefined. Otherwise, the value **MUST** be +either `true` or `false`. + +```bash +export DEBUG=true +export DEBUG=false +``` + +#### See Also + +- [`VERBOSE`](#VERBOSE) — enable verbose logging + +### `VERBOSE` + +> enable verbose logging + +The `VERBOSE` variable **MAY** be left undefined. Otherwise, the value **MUST** +be either `true` or `false`. + +```bash +export VERBOSE=true +export VERBOSE=false +``` + +## Usage Examples + +
+Kubernetes + +This example shows how to define the environment variables needed by `` +on a [Kubernetes container] within a Kubenetes deployment manifest. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: example-deployment +spec: + template: + spec: + containers: + - name: example-container + env: + - name: DEBUG # enable or disable debugging features + value: "false" + - name: VERBOSE # enable verbose logging + value: "false" +``` + +Alternatively, the environment variables can be defined within a [config map][kubernetes config map] +then referenced a deployment manifest using `configMapRef`. + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: example-config-map +data: + DEBUG: "false" # enable or disable debugging features + VERBOSE: "false" # enable verbose logging +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: example-deployment +spec: + template: + spec: + containers: + - name: example-container + envFrom: + - configMapRef: + name: example-config-map +``` + +
+ +
+Docker + +This example shows how to define the environment variables needed by `` +when running as a [Docker service] defined in a Docker compose file. + +```yaml +service: + example-service: + environment: + DEBUG: "false" # enable or disable debugging features + VERBOSE: "false" # enable verbose logging +``` + +
+ + + +[docker service]: https://docs.docker.com/compose/environment-variables/#set-environment-variables-in-containers +[ferrite]: https://github.com/dogmatiq/ferrite +[kubernetes config map]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#configure-all-key-value-pairs-in-a-configmap-as-container-environment-variables +[kubernetes container]: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container +[rfc 2119]: https://www.rfc-editor.org/rfc/rfc2119.html diff --git a/internal/mode/usage/markdown/testdata/markdown/preamble/non-normative.md b/internal/mode/usage/markdown/testdata/markdown/preamble/non-normative.md deleted file mode 100644 index 4e7ea94..0000000 --- a/internal/mode/usage/markdown/testdata/markdown/preamble/non-normative.md +++ /dev/null @@ -1,39 +0,0 @@ -# Environment Variables - -This document describes the environment variables used by ``. - -If any of the environment variable values do not meet the requirements herein, -the application will print usage information to `STDERR` then exit with a -non-zero exit code. Please note that **undefined** variables and **empty** -values are considered equivalent. - -⚠️ This document includes **non-normative** example values. While these values -are syntactically correct, they may not be meaningful to this application. - -⚠️ The application may consume other undocumented environment variables; this -document only shows those variables declared using [Ferrite]. - -The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, -**SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this -document are to be interpreted as described in [RFC 2119]. - -## Index - -- [`READ_DSN`](#READ_DSN) — database connection string for read-models - -## Specification - -### `READ_DSN` - -> database connection string for read-models - -The `READ_DSN` variable **MUST NOT** be left undefined. - -```bash -export READ_DSN=foo # (non-normative) -``` - - - -[ferrite]: https://github.com/dogmatiq/ferrite -[rfc 2119]: https://www.rfc-editor.org/rfc/rfc2119.html diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/bool/deprecated-with-default.md b/internal/mode/usage/markdown/testdata/spec/bool/deprecated-with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/bool/deprecated-with-default.md rename to internal/mode/usage/markdown/testdata/spec/bool/deprecated-with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/bool/deprecated.md b/internal/mode/usage/markdown/testdata/spec/bool/deprecated.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/bool/deprecated.md rename to internal/mode/usage/markdown/testdata/spec/bool/deprecated.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/bool/optional.md b/internal/mode/usage/markdown/testdata/spec/bool/optional.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/bool/optional.md rename to internal/mode/usage/markdown/testdata/spec/bool/optional.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/bool/required.md b/internal/mode/usage/markdown/testdata/spec/bool/required.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/bool/required.md rename to internal/mode/usage/markdown/testdata/spec/bool/required.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/bool/with-default.md b/internal/mode/usage/markdown/testdata/spec/bool/with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/bool/with-default.md rename to internal/mode/usage/markdown/testdata/spec/bool/with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/duration/deprecated-with-default.md b/internal/mode/usage/markdown/testdata/spec/duration/deprecated-with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/duration/deprecated-with-default.md rename to internal/mode/usage/markdown/testdata/spec/duration/deprecated-with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/duration/deprecated.md b/internal/mode/usage/markdown/testdata/spec/duration/deprecated.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/duration/deprecated.md rename to internal/mode/usage/markdown/testdata/spec/duration/deprecated.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/duration/optional.md b/internal/mode/usage/markdown/testdata/spec/duration/optional.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/duration/optional.md rename to internal/mode/usage/markdown/testdata/spec/duration/optional.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/duration/required.md b/internal/mode/usage/markdown/testdata/spec/duration/required.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/duration/required.md rename to internal/mode/usage/markdown/testdata/spec/duration/required.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/duration/with-default.md b/internal/mode/usage/markdown/testdata/spec/duration/with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/duration/with-default.md rename to internal/mode/usage/markdown/testdata/spec/duration/with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/duration/with-max.md b/internal/mode/usage/markdown/testdata/spec/duration/with-max.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/duration/with-max.md rename to internal/mode/usage/markdown/testdata/spec/duration/with-max.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/enum/deprecated-with-default.md b/internal/mode/usage/markdown/testdata/spec/enum/deprecated-with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/enum/deprecated-with-default.md rename to internal/mode/usage/markdown/testdata/spec/enum/deprecated-with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/enum/deprecated.md b/internal/mode/usage/markdown/testdata/spec/enum/deprecated.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/enum/deprecated.md rename to internal/mode/usage/markdown/testdata/spec/enum/deprecated.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/enum/optional.md b/internal/mode/usage/markdown/testdata/spec/enum/optional.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/enum/optional.md rename to internal/mode/usage/markdown/testdata/spec/enum/optional.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/enum/required.md b/internal/mode/usage/markdown/testdata/spec/enum/required.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/enum/required.md rename to internal/mode/usage/markdown/testdata/spec/enum/required.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/enum/with-default.md b/internal/mode/usage/markdown/testdata/spec/enum/with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/enum/with-default.md rename to internal/mode/usage/markdown/testdata/spec/enum/with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/float/deprecated-with-default.md b/internal/mode/usage/markdown/testdata/spec/float/deprecated-with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/float/deprecated-with-default.md rename to internal/mode/usage/markdown/testdata/spec/float/deprecated-with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/float/deprecated.md b/internal/mode/usage/markdown/testdata/spec/float/deprecated.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/float/deprecated.md rename to internal/mode/usage/markdown/testdata/spec/float/deprecated.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/float/optional.md b/internal/mode/usage/markdown/testdata/spec/float/optional.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/float/optional.md rename to internal/mode/usage/markdown/testdata/spec/float/optional.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/float/required.md b/internal/mode/usage/markdown/testdata/spec/float/required.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/float/required.md rename to internal/mode/usage/markdown/testdata/spec/float/required.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/float/with-default.md b/internal/mode/usage/markdown/testdata/spec/float/with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/float/with-default.md rename to internal/mode/usage/markdown/testdata/spec/float/with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/float/with-max.md b/internal/mode/usage/markdown/testdata/spec/float/with-max.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/float/with-max.md rename to internal/mode/usage/markdown/testdata/spec/float/with-max.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/float/with-min.md b/internal/mode/usage/markdown/testdata/spec/float/with-min.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/float/with-min.md rename to internal/mode/usage/markdown/testdata/spec/float/with-min.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/float/with-minmax.md b/internal/mode/usage/markdown/testdata/spec/float/with-minmax.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/float/with-minmax.md rename to internal/mode/usage/markdown/testdata/spec/float/with-minmax.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/deprecated-with-default.md b/internal/mode/usage/markdown/testdata/spec/k8s-service/deprecated-with-default.md similarity index 88% rename from internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/deprecated-with-default.md rename to internal/mode/usage/markdown/testdata/spec/k8s-service/deprecated-with-default.md index 15d18ad..4b5cbbb 100644 --- a/internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/deprecated-with-default.md +++ b/internal/mode/usage/markdown/testdata/spec/k8s-service/deprecated-with-default.md @@ -20,6 +20,10 @@ in a future version. export REDIS_SERVICE_HOST=redis.example.org # (default) ``` +#### See Also + +- [`REDIS_SERVICE_PORT`](#REDIS_SERVICE_PORT) — kubernetes "redis" service port + ### `REDIS_SERVICE_PORT` > kubernetes "redis" service port @@ -47,3 +51,7 @@ the system's service database, typically located in the `/etc/service` file on UNIX-like systems. Standard service names are published by IANA.
+ +#### See Also + +- [`REDIS_SERVICE_HOST`](#REDIS_SERVICE_HOST) — kubernetes "redis" service host diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/deprecated.md b/internal/mode/usage/markdown/testdata/spec/k8s-service/deprecated.md similarity index 87% rename from internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/deprecated.md rename to internal/mode/usage/markdown/testdata/spec/k8s-service/deprecated.md index 120343e..e6293d5 100644 --- a/internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/deprecated.md +++ b/internal/mode/usage/markdown/testdata/spec/k8s-service/deprecated.md @@ -19,6 +19,10 @@ in a future version. export REDIS_SERVICE_HOST=foo # (non-normative) ``` +#### See Also + +- [`REDIS_SERVICE_PORT`](#REDIS_SERVICE_PORT) — kubernetes "redis" service port + ### `REDIS_SERVICE_PORT` > kubernetes "redis" service port @@ -45,3 +49,7 @@ the system's service database, typically located in the `/etc/service` file on UNIX-like systems. Standard service names are published by IANA. + +#### See Also + +- [`REDIS_SERVICE_HOST`](#REDIS_SERVICE_HOST) — kubernetes "redis" service host diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/optional.md b/internal/mode/usage/markdown/testdata/spec/k8s-service/optional.md similarity index 85% rename from internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/optional.md rename to internal/mode/usage/markdown/testdata/spec/k8s-service/optional.md index 7478aff..b7bf45e 100644 --- a/internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/optional.md +++ b/internal/mode/usage/markdown/testdata/spec/k8s-service/optional.md @@ -16,6 +16,10 @@ typically does not need to be specified in the pod manifest. export REDIS_SERVICE_HOST=foo # (non-normative) ``` +#### See Also + +- [`REDIS_SERVICE_PORT`](#REDIS_SERVICE_PORT) — kubernetes "redis" service port + ### `REDIS_SERVICE_PORT` > kubernetes "redis" service port @@ -39,3 +43,7 @@ the system's service database, typically located in the `/etc/service` file on UNIX-like systems. Standard service names are published by IANA. + +#### See Also + +- [`REDIS_SERVICE_HOST`](#REDIS_SERVICE_HOST) — kubernetes "redis" service host diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/required.md b/internal/mode/usage/markdown/testdata/spec/k8s-service/required.md similarity index 84% rename from internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/required.md rename to internal/mode/usage/markdown/testdata/spec/k8s-service/required.md index f8f02d6..8f1bc6a 100644 --- a/internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/required.md +++ b/internal/mode/usage/markdown/testdata/spec/k8s-service/required.md @@ -15,6 +15,10 @@ typically does not need to be specified in the pod manifest. export REDIS_SERVICE_HOST=foo # (non-normative) ``` +#### See Also + +- [`REDIS_SERVICE_PORT`](#REDIS_SERVICE_PORT) — kubernetes "redis" service port + ### `REDIS_SERVICE_PORT` > kubernetes "redis" service port @@ -37,3 +41,7 @@ the system's service database, typically located in the `/etc/service` file on UNIX-like systems. Standard service names are published by IANA. + +#### See Also + +- [`REDIS_SERVICE_HOST`](#REDIS_SERVICE_HOST) — kubernetes "redis" service host diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/with-default.md b/internal/mode/usage/markdown/testdata/spec/k8s-service/with-default.md similarity index 86% rename from internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/with-default.md rename to internal/mode/usage/markdown/testdata/spec/k8s-service/with-default.md index e4290c0..0820de4 100644 --- a/internal/mode/usage/markdown/testdata/markdown/spec/k8s-service/with-default.md +++ b/internal/mode/usage/markdown/testdata/spec/k8s-service/with-default.md @@ -17,6 +17,10 @@ typically does not need to be specified in the pod manifest. export REDIS_SERVICE_HOST=redis.example.org # (default) ``` +#### See Also + +- [`REDIS_SERVICE_PORT`](#REDIS_SERVICE_PORT) — kubernetes "redis" service port + ### `REDIS_SERVICE_PORT` > kubernetes "redis" service port @@ -41,3 +45,7 @@ the system's service database, typically located in the `/etc/service` file on UNIX-like systems. Standard service names are published by IANA. + +#### See Also + +- [`REDIS_SERVICE_HOST`](#REDIS_SERVICE_HOST) — kubernetes "redis" service host diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/network-port/deprecated-with-default.md b/internal/mode/usage/markdown/testdata/spec/network-port/deprecated-with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/network-port/deprecated-with-default.md rename to internal/mode/usage/markdown/testdata/spec/network-port/deprecated-with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/network-port/deprecated.md b/internal/mode/usage/markdown/testdata/spec/network-port/deprecated.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/network-port/deprecated.md rename to internal/mode/usage/markdown/testdata/spec/network-port/deprecated.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/network-port/optional.md b/internal/mode/usage/markdown/testdata/spec/network-port/optional.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/network-port/optional.md rename to internal/mode/usage/markdown/testdata/spec/network-port/optional.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/network-port/required.md b/internal/mode/usage/markdown/testdata/spec/network-port/required.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/network-port/required.md rename to internal/mode/usage/markdown/testdata/spec/network-port/required.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/network-port/with-default.md b/internal/mode/usage/markdown/testdata/spec/network-port/with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/network-port/with-default.md rename to internal/mode/usage/markdown/testdata/spec/network-port/with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/signed/deprecated-with-default.md b/internal/mode/usage/markdown/testdata/spec/signed/deprecated-with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/signed/deprecated-with-default.md rename to internal/mode/usage/markdown/testdata/spec/signed/deprecated-with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/signed/deprecated.md b/internal/mode/usage/markdown/testdata/spec/signed/deprecated.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/signed/deprecated.md rename to internal/mode/usage/markdown/testdata/spec/signed/deprecated.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/signed/optional.md b/internal/mode/usage/markdown/testdata/spec/signed/optional.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/signed/optional.md rename to internal/mode/usage/markdown/testdata/spec/signed/optional.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/signed/required.md b/internal/mode/usage/markdown/testdata/spec/signed/required.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/signed/required.md rename to internal/mode/usage/markdown/testdata/spec/signed/required.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/signed/with-default.md b/internal/mode/usage/markdown/testdata/spec/signed/with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/signed/with-default.md rename to internal/mode/usage/markdown/testdata/spec/signed/with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/signed/with-max.md b/internal/mode/usage/markdown/testdata/spec/signed/with-max.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/signed/with-max.md rename to internal/mode/usage/markdown/testdata/spec/signed/with-max.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/signed/with-min.md b/internal/mode/usage/markdown/testdata/spec/signed/with-min.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/signed/with-min.md rename to internal/mode/usage/markdown/testdata/spec/signed/with-min.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/signed/with-minmax.md b/internal/mode/usage/markdown/testdata/spec/signed/with-minmax.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/signed/with-minmax.md rename to internal/mode/usage/markdown/testdata/spec/signed/with-minmax.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/string/deprecated-with-default.md b/internal/mode/usage/markdown/testdata/spec/string/deprecated-with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/string/deprecated-with-default.md rename to internal/mode/usage/markdown/testdata/spec/string/deprecated-with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/string/deprecated.md b/internal/mode/usage/markdown/testdata/spec/string/deprecated.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/string/deprecated.md rename to internal/mode/usage/markdown/testdata/spec/string/deprecated.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/string/optional.md b/internal/mode/usage/markdown/testdata/spec/string/optional.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/string/optional.md rename to internal/mode/usage/markdown/testdata/spec/string/optional.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/string/required.md b/internal/mode/usage/markdown/testdata/spec/string/required.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/string/required.md rename to internal/mode/usage/markdown/testdata/spec/string/required.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/string/sensitive-optional.md b/internal/mode/usage/markdown/testdata/spec/string/sensitive-optional.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/string/sensitive-optional.md rename to internal/mode/usage/markdown/testdata/spec/string/sensitive-optional.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/string/sensitive-required.md b/internal/mode/usage/markdown/testdata/spec/string/sensitive-required.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/string/sensitive-required.md rename to internal/mode/usage/markdown/testdata/spec/string/sensitive-required.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/string/sensitive-with-default.md b/internal/mode/usage/markdown/testdata/spec/string/sensitive-with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/string/sensitive-with-default.md rename to internal/mode/usage/markdown/testdata/spec/string/sensitive-with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/string/with-default.md b/internal/mode/usage/markdown/testdata/spec/string/with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/string/with-default.md rename to internal/mode/usage/markdown/testdata/spec/string/with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/unsigned/deprecated-with-default.md b/internal/mode/usage/markdown/testdata/spec/unsigned/deprecated-with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/unsigned/deprecated-with-default.md rename to internal/mode/usage/markdown/testdata/spec/unsigned/deprecated-with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/unsigned/deprecated.md b/internal/mode/usage/markdown/testdata/spec/unsigned/deprecated.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/unsigned/deprecated.md rename to internal/mode/usage/markdown/testdata/spec/unsigned/deprecated.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/unsigned/optional.md b/internal/mode/usage/markdown/testdata/spec/unsigned/optional.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/unsigned/optional.md rename to internal/mode/usage/markdown/testdata/spec/unsigned/optional.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/unsigned/required.md b/internal/mode/usage/markdown/testdata/spec/unsigned/required.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/unsigned/required.md rename to internal/mode/usage/markdown/testdata/spec/unsigned/required.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/unsigned/with-default.md b/internal/mode/usage/markdown/testdata/spec/unsigned/with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/unsigned/with-default.md rename to internal/mode/usage/markdown/testdata/spec/unsigned/with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/unsigned/with-max.md b/internal/mode/usage/markdown/testdata/spec/unsigned/with-max.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/unsigned/with-max.md rename to internal/mode/usage/markdown/testdata/spec/unsigned/with-max.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/unsigned/with-min.md b/internal/mode/usage/markdown/testdata/spec/unsigned/with-min.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/unsigned/with-min.md rename to internal/mode/usage/markdown/testdata/spec/unsigned/with-min.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/unsigned/with-minmax.md b/internal/mode/usage/markdown/testdata/spec/unsigned/with-minmax.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/unsigned/with-minmax.md rename to internal/mode/usage/markdown/testdata/spec/unsigned/with-minmax.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/url/deprecated-with-default.md b/internal/mode/usage/markdown/testdata/spec/url/deprecated-with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/url/deprecated-with-default.md rename to internal/mode/usage/markdown/testdata/spec/url/deprecated-with-default.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/url/deprecated.md b/internal/mode/usage/markdown/testdata/spec/url/deprecated.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/url/deprecated.md rename to internal/mode/usage/markdown/testdata/spec/url/deprecated.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/url/optional.md b/internal/mode/usage/markdown/testdata/spec/url/optional.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/url/optional.md rename to internal/mode/usage/markdown/testdata/spec/url/optional.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/url/required.md b/internal/mode/usage/markdown/testdata/spec/url/required.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/url/required.md rename to internal/mode/usage/markdown/testdata/spec/url/required.md diff --git a/internal/mode/usage/markdown/testdata/markdown/spec/url/with-default.md b/internal/mode/usage/markdown/testdata/spec/url/with-default.md similarity index 100% rename from internal/mode/usage/markdown/testdata/markdown/spec/url/with-default.md rename to internal/mode/usage/markdown/testdata/spec/url/with-default.md diff --git a/internal/mode/usage/markdown/usage_test.go b/internal/mode/usage/markdown/usage_test.go deleted file mode 100644 index dab76ab..0000000 --- a/internal/mode/usage/markdown/usage_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package markdown_test - -import ( - "bytes" - "os" - "path/filepath" - - "github.com/dogmatiq/ferrite" - "github.com/dogmatiq/ferrite/internal/mode" - . "github.com/dogmatiq/ferrite/internal/mode/usage/markdown" - "github.com/dogmatiq/ferrite/variable" - . "github.com/jmalloc/gomegax" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = Describe("func Run()", func() { - var reg *variable.Registry - - BeforeEach(func() { - reg = &variable.Registry{ - Environment: &variable.MemoryEnvironment{}, - } - }) - - DescribeTable( - "it describes the environment variable", - func( - file string, - setup func(*variable.Registry), - ) { - setup(reg) - - expect, err := os.ReadFile( - filepath.Join( - "testdata", - "markdown", - "usage", - file, - ), - ) - Expect(err).ShouldNot(HaveOccurred()) - - actual := &bytes.Buffer{} - exited := false - - Run( - mode.Options{ - Registry: reg, - Args: []string{""}, - Out: actual, - Exit: func(code int) { - exited = true - Expect(code).To(Equal(0)) - }, - }, - ) - ExpectWithOffset(1, actual.String()).To(EqualX(string(expect))) - Expect(exited).To(BeTrue()) - }, - Entry( - "usage", - "usage.md", - func(reg *variable.Registry) { - ferrite. - Bool("DEBUG", "enable or disable debugging features"). - Optional(variable.WithRegistry(reg)) - }, - ), - Entry( - "usage shows the default value in examples if available", - "usage-shows-default.md", - func(reg *variable.Registry) { - ferrite. - NetworkPort("PORT", "an environment variable that has a default value"). - WithDefault("ftp"). - Required(variable.WithRegistry(reg)) - }, - ), - ) -}) diff --git a/kubernetes.go b/kubernetes.go index 127a019..659fac7 100644 --- a/kubernetes.go +++ b/kubernetes.go @@ -92,6 +92,9 @@ func KubernetesService(svc string) *KubernetesServiceBuilder { buildNetworkPortSyntaxDocumentation(b.portSpec.Documentation()) + seeAlso(b.hostSpec.Peek(), b.portSpec.Peek()) + seeAlso(b.portSpec.Peek(), b.hostSpec.Peek()) + return b } @@ -149,6 +152,13 @@ func (b *KubernetesServiceBuilder) WithDefault(host, port string) *KubernetesSer return b } +// SeeAlso creates a relationship between this variable and those used by i. +func (b *KubernetesServiceBuilder) SeeAlso(i Input, options ...SeeAlsoOption) *KubernetesServiceBuilder { + seeAlsoInput(&b.hostSpec, i, options...) + seeAlsoInput(&b.portSpec, i, options...) + return b +} + // Required completes the build process and registers a required variable with // Ferrite's validation system. func (b *KubernetesServiceBuilder) Required(options ...RequiredOption) Required[KubernetesAddress] { @@ -166,6 +176,7 @@ func (b *KubernetesServiceBuilder) Required(options ...RequiredOption) Required[ ) return requiredFunc[KubernetesAddress]{ + []variable.Any{host, port}, func() (KubernetesAddress, error) { h, ok, err := host.NativeValue() if err != nil { @@ -204,6 +215,7 @@ func (b *KubernetesServiceBuilder) Optional(options ...OptionalOption) Optional[ ) return optionalFunc[KubernetesAddress]{ + []variable.Any{host, port}, b.optionalResolver(host, port), } } @@ -225,6 +237,7 @@ func (b *KubernetesServiceBuilder) Deprecated(options ...DeprecatedOption) Depre ) return deprecatedFunc[KubernetesAddress]{ + []variable.Any{host, port}, b.optionalResolver(host, port), } } diff --git a/network.go b/network.go index 3452a8c..ae9d002 100644 --- a/network.go +++ b/network.go @@ -45,6 +45,12 @@ func (b *NetworkPortBuilder) WithDefault(v string) *NetworkPortBuilder { return b } +// SeeAlso creates a relationship between this variable and those used by i. +func (b *NetworkPortBuilder) SeeAlso(i Input, options ...SeeAlsoOption) *NetworkPortBuilder { + seeAlsoInput(&b.spec, i, options...) + return b +} + // Required completes the build process and registers a required variable with // Ferrite's validation system. func (b *NetworkPortBuilder) Required(options ...RequiredOption) Required[string] { diff --git a/numfloat.go b/numfloat.go index f8147fd..4d47696 100644 --- a/numfloat.go +++ b/numfloat.go @@ -73,6 +73,12 @@ func (b *FloatBuilder[T]) WithMaximum(v T) *FloatBuilder[T] { return b } +// SeeAlso creates a relationship between this variable and those used by i. +func (b *FloatBuilder[T]) SeeAlso(i Input, options ...SeeAlsoOption) *FloatBuilder[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 *FloatBuilder[T]) Required(options ...RequiredOption) Required[T] { diff --git a/numsigned.go b/numsigned.go index 6cd385a..128a886 100644 --- a/numsigned.go +++ b/numsigned.go @@ -73,6 +73,12 @@ func (b *SignedBuilder[T]) WithMaximum(v T) *SignedBuilder[T] { return b } +// SeeAlso creates a relationship between this variable and those used by i. +func (b *SignedBuilder[T]) SeeAlso(i Input, options ...SeeAlsoOption) *SignedBuilder[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 *SignedBuilder[T]) Required(options ...RequiredOption) Required[T] { diff --git a/numunsigned.go b/numunsigned.go index 283db85..065b794 100644 --- a/numunsigned.go +++ b/numunsigned.go @@ -72,6 +72,12 @@ func (b *UnsignedBuilder[T]) WithMaximum(v T) *UnsignedBuilder[T] { return b } +// SeeAlso creates a relationship between this variable and those used by i. +func (b *UnsignedBuilder[T]) SeeAlso(i Input, options ...SeeAlsoOption) *UnsignedBuilder[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 *UnsignedBuilder[T]) Required(options ...RequiredOption) Required[T] { diff --git a/relationship.go b/relationship.go new file mode 100644 index 0000000..ecef4d6 --- /dev/null +++ b/relationship.go @@ -0,0 +1,36 @@ +package ferrite + +import "github.com/dogmatiq/ferrite/variable" + +// SeeAlsoOption changes the behavior of a call the SeeAlso() method of the +// various builder implementations. +type SeeAlsoOption interface { + applySeeAlsoOption(*variable.SeeAlso) +} + +func seeAlsoInput( + from variable.SpecBuilder, + to Input, + options ...SeeAlsoOption, +) { + for _, v := range to.variables() { + seeAlso(from.Peek(), v.Spec(), options...) + } +} + +func seeAlso( + from, to variable.Spec, + options ...SeeAlsoOption, +) { + r := variable.SeeAlso{ + From: from, + To: to, + } + + for _, opt := range options { + opt.applySeeAlsoOption(&r) + } + + from.AddRelationship(r) + to.AddRelationship(r) +} diff --git a/relationshipexample_test.go b/relationshipexample_test.go new file mode 100644 index 0000000..f85bb6e --- /dev/null +++ b/relationshipexample_test.go @@ -0,0 +1,18 @@ +package ferrite_test + +import ( + "github.com/dogmatiq/ferrite" +) + +func Example_seeAlso() { + defer example()() + + verbose := ferrite. + Bool("VERBOSE", "enable verbose logging"). + Optional() + + ferrite. + Bool("DEBUG", "enable or disable debugging features"). + SeeAlso(verbose). + Optional() +} diff --git a/string.go b/string.go index f73ea2c..a868192 100644 --- a/string.go +++ b/string.go @@ -66,6 +66,12 @@ func (b *StringBuilder[T]) WithSensitiveContent() *StringBuilder[T] { return b } +// SeeAlso creates a relationship between this variable and those used by i. +func (b *StringBuilder[T]) SeeAlso(i Input, options ...SeeAlsoOption) *StringBuilder[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 *StringBuilder[T]) Required(options ...RequiredOption) Required[T] { diff --git a/url.go b/url.go index 4c66c55..5a6821e 100644 --- a/url.go +++ b/url.go @@ -69,6 +69,12 @@ func (b *URLBuilder) WithDefault(v string) *URLBuilder { return b } +// SeeAlso creates a relationship between this variable and those used by i. +func (b *URLBuilder) SeeAlso(i Input, options ...SeeAlsoOption) *URLBuilder { + seeAlsoInput(&b.spec, i, options...) + return b +} + // Required completes the build process and registers a required variable with // Ferrite's validation system. func (b *URLBuilder) Required(options ...RequiredOption) Required[*url.URL] { diff --git a/variable/relationship.go b/variable/relationship.go new file mode 100644 index 0000000..81f17a3 --- /dev/null +++ b/variable/relationship.go @@ -0,0 +1,32 @@ +package variable + +// Relationship represents a relationship between two variables. +type Relationship interface { + // Specs returns the specs for the variables that are involved in the + // relationship. + Specs() []Spec + + // AcceptVisitor passes the schema to the appropriate method of v. + AcceptVisitor(v RelationshipVisitor) +} + +// RelationshipVisitor dispatches based on a relationship's type. +type RelationshipVisitor interface { + VisitSeeAlso(SeeAlso) +} + +// SeeAlso is a relationship that refers the user from one variable to another. +type SeeAlso struct { + From, To Spec +} + +// Specs returns the specs for the variables that are involved in the +// relationship. +func (r SeeAlso) Specs() []Spec { + return []Spec{r.From, r.To} +} + +// AcceptVisitor passes r to the appropriate method of v. +func (r SeeAlso) AcceptVisitor(v RelationshipVisitor) { + v.VisitSeeAlso(r) +} diff --git a/variable/schema.go b/variable/schema.go index 11eca28..926a8be 100644 --- a/variable/schema.go +++ b/variable/schema.go @@ -15,7 +15,7 @@ type Schema interface { Finalize() error // AcceptVisitor passes the schema to the appropriate method of v. - AcceptVisitor(SchemaVisitor) + AcceptVisitor(v SchemaVisitor) } // SchemaError indicates that a value is invalid because it violates its schema. @@ -26,7 +26,7 @@ type SchemaError interface { Schema() Schema // AcceptVisitor passes the error to the appropriate method of v. - AcceptVisitor(SchemaErrorVisitor) + AcceptVisitor(v SchemaErrorVisitor) } // SchemaVisitor dispatches based on a variable's schema. diff --git a/variable/spec.go b/variable/spec.go index 001370c..e59df0c 100644 --- a/variable/spec.go +++ b/variable/spec.go @@ -42,6 +42,12 @@ type Spec interface { // Documentation returns a list of chunks of documentation text. Documentation() []Documentation + + // Relationships returns a list of relationships that involve this variable. + Relationships() []Relationship + + // AddRelationship adds a relationship that involves this variable. + AddRelationship(r Relationship) } // IsDefault returns true if v is the default value of the given spec. @@ -55,16 +61,17 @@ func IsDefault(s Spec, v Literal) bool { // TypedSpec builds a specification for a variable depicted by type T. type TypedSpec[T any] struct { - name string - desc string - def maybe.Value[valueOf[T]] - required bool - sensitive bool - deprecated bool - schema TypedSchema[T] - examples []Example - docs []Documentation - constraints []TypedConstraint[T] + name string + desc string + def maybe.Value[valueOf[T]] + required bool + sensitive bool + deprecated bool + schema TypedSchema[T] + examples []Example + docs []Documentation + constraints []TypedConstraint[T] + relationships []Relationship } // Name returns the name of the variable. @@ -124,6 +131,16 @@ func (s *TypedSpec[T]) Documentation() []Documentation { return s.docs } +// Relationships returns a list of relationships that involve this variable. +func (s TypedSpec[T]) Relationships() []Relationship { + return s.relationships +} + +// AddRelationship adds a relationship that involves this variable. +func (s *TypedSpec[T]) AddRelationship(r Relationship) { + s.relationships = append(s.relationships, r) +} + // CheckConstraints returns an error if v does not satisfy any one of the // specification's constraints. func (s *TypedSpec[T]) CheckConstraints(v T) ConstraintError { diff --git a/variable/specbuilder.go b/variable/specbuilder.go index ff68e55..7cc3a93 100644 --- a/variable/specbuilder.go +++ b/variable/specbuilder.go @@ -12,9 +12,11 @@ type SpecBuilder interface { Name(string) Description(string) MarkRequired() - MarkDeprecated(reason string) + MarkDeprecated() MarkSensitive() Documentation() DocumentationBuilder + Relationship(Relationship) + Peek() Spec } // TypedSpecBuilder builds a specification for a variable of type T. @@ -108,6 +110,16 @@ func (b *TypedSpecBuilder[T]) Documentation() DocumentationBuilder { } } +// Relationship adds a relationship that involves this variable. +func (b *TypedSpecBuilder[T]) Relationship(rel Relationship) { + b.spec.AddRelationship(rel) +} + +// Peek returns the (potentially invalid) spec that is being built. +func (b *TypedSpecBuilder[T]) Peek() Spec { + return &b.spec +} + // Done builds the specification and registers the variable. func (b *TypedSpecBuilder[T]) Done(schema TypedSchema[T]) *TypedSpec[T] { b.spec.schema = schema