diff options
| -rw-r--r-- | assetembed/assetembed.go | 6 | ||||
| -rw-r--r-- | jsonmatch/interface.go | 43 | ||||
| -rw-r--r-- | option/option.go | 23 |
3 files changed, 41 insertions, 31 deletions
diff --git a/assetembed/assetembed.go b/assetembed/assetembed.go index f8dcaf8..fc25f11 100644 --- a/assetembed/assetembed.go +++ b/assetembed/assetembed.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Package assetembed provides a HTTP handler for serving asset files embedded in a Go binary through the embed.FS type. -// It is similar in purpose to http.FileServerFS() from the standard library, +// It is similar in purpose to [http.FileServerFS] from the standard library, // but instead of serving files directly with their names as found in the filesystem, // it inserts a cryptographic digest of the file contents into the filename that the handler serves. // @@ -34,7 +34,7 @@ type Handler struct { contents map[string][]byte // e.g. "res/app-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css" -> [contents of res/app.css] } -// NewHandler builds a new Handler instance. +// NewHandler builds a new [Handler] instance. // This will read all files in assetFS and store a copy of their contents inside the Handler instance. // // For filesystems backed by actual disk or network storage, this can be a very expensive operation. @@ -120,7 +120,7 @@ var cacheControlHeader = fmt.Sprintf( int64((14 * 24 * time.Hour).Seconds()), // max-age = 2 weeks ) -// ServeHTTP implements the http.Handler interface. +// ServeHTTP implements the [http.Handler] interface. func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { buf, exists := h.contents[strings.TrimPrefix(r.URL.Path, "/")] if !exists { diff --git a/jsonmatch/interface.go b/jsonmatch/interface.go index 69319cb..d7b6b5b 100644 --- a/jsonmatch/interface.go +++ b/jsonmatch/interface.go @@ -96,12 +96,12 @@ // In this example, we have made a mistake in the implementation. // The field "name" has been misspelled, so it will be marshalled as "naem" instead. // Because the test unmarshals into the same type as the implementation, it will not be able to uncover this error. -// This example might be a bit contrived, but keeping test logic separate from implementation logic is especially important for types using advanced marshalling logic through custom implementations of the json.Marshaler and json.Unmarshaler interfaces. +// This example might be a bit contrived, but keeping test logic separate from implementation logic is especially important for types using advanced marshalling logic through custom implementations of the [json.Marshaler] and [json.Unmarshaler] interfaces. // // # Capturing nondeterministic data // // Sometimes, JSON payloads may contain randomly-generated fields like UUIDs or non-deterministic data like timestamps that cannot be predicted when writing the test code. -// For these situations, package jsonmatch provides the CaptureField function. +// For these situations, package jsonmatch provides the [CaptureField] function. // The example below shows a test exercising a PUT endpoint to create an object, capturing the object's ID while asserting on the rest of the response, and then using that ID to exercise a GET endpoint that displays the created object. // // req1 := httptest.NewRequest(http.MethodPut, "/v1/things/new", strings.NewReader(`{"name":"hello"}`) @@ -136,7 +136,7 @@ import ( "fmt" ) -// Diffable is the common interface of types Object, Array, Scalar and Null from this package. +// Diffable is the common interface of types [Object], [Array], [Scalar] and [Null] from this package. // The DiffAgainst function compares the value contained in the Diffable against an encoded JSON payload. // // The implementation will try to generate diffs as granularly as possible. @@ -174,7 +174,7 @@ var ( // Please refer to the package documentation for how to use this type. type Array []any -// DiffAgainst implements the Diffable interface. +// DiffAgainst implements the [Diffable] interface. func (a Array) DiffAgainst(buf []byte) []Diff { return diffAgainst([]any(a), buf) } @@ -183,21 +183,21 @@ func (a Array) DiffAgainst(buf []byte) []Diff { // Please refer to the package documentation for how to use this type. type Object map[string]any -// DiffAgainst implements the Diffable interface. +// DiffAgainst implements the [Diffable] interface. func (o Object) DiffAgainst(buf []byte) []Diff { return diffAgainst(map[string]any(o), buf) } // Null implements diffing against an encoded JSON payload that is expected just the value `null`. // This type is only used on the top level of the JSON payload. -// Within type Object or type Array, put a `nil` directly. +// Within type [Object] or type [Array], put a `nil` directly. func Null() Diffable { return scalar{nil} } // Scalar implements diffing against an encoded JSON payload that is expected to contain just a scalar value (a number, string or boolean). // This type is only used on the top level of the JSON payload. -// Within type Object or type Array, put the value directly. +// Within type [Object] or type [Array,] put the value directly. func Scalar[S ScalarValue](value S) Diffable { return scalar{value} } @@ -206,12 +206,12 @@ type scalar struct { Value any } -// DiffAgainst implements the Diffable interface. +// DiffAgainst implements the [Diffable] interface. func (s scalar) DiffAgainst(buf []byte) []Diff { return diffAgainst(s.Value, buf) } -// ScalarValue is an interface containing every type that can be given to func Scalar. +// ScalarValue is an interface containing every type that can be given to func [Scalar]. type ScalarValue interface { ~bool | ~int | ~int8 | ~int16 | ~int32 | ~int64 | @@ -221,7 +221,7 @@ type ScalarValue interface { } // Diff is a difference between the actual encoded JSON payload given to a DiffAgainst() call, and the expectation encoded in the object that DiffAgainst() was called on. -// See type Diffable for details on how diffing works. +// See type [Diffable] for details on how diffing works. type Diff struct { // Kind explains the type of difference. // No stability guarantee is given for the values that can occur in this field. @@ -246,16 +246,16 @@ func (d Diff) String() string { } // Pointer is a JSON pointer (RFC 6901) that references a particular JSON value relative to the root of the encoded JSON payload that was given to DiffAgainst(). -// It appears in type Diff. +// It appears in type [Diff]. // // This type is intended to become synonymous with encoding/json/jsontext.Pointer once that type is stabilized. type Pointer string -// CaptureField returns a capture slot that can be placed in a jsonmatch.Object or jsonmatch.Array instance to capture individual non-deterministic values during an assertion. +// CaptureField returns a capture slot that can be placed in an [Object] or [Array] instance to capture individual non-deterministic values during an assertion. // Please refer to the package documentation for details and usage examples. // // Capture slots only work inside data structures that DiffAgainst() knows how to recurse into. -// Please refer to the documentation on type Diffable for details. +// Please refer to the documentation on type [Diffable] for details. func CaptureField[T any](target *T) any { // NOTE: The public interface is using generics because that allows enforcing // that `target` is passed as pointer. But the internal representation holds @@ -268,7 +268,7 @@ type capturedField struct { PointerToTarget any } -// MarshalJSON implements the json.Marshaler interface by transparently marshaling the contained value. +// MarshalJSON implements the [json.Marshaler] interface by transparently marshaling the contained value. // // This implementation ensures that `capturedField` looks like its payload // when serialized for a "type mismatch" or "value mismatch" error message. @@ -276,7 +276,7 @@ func (f capturedField) MarshalJSON() ([]byte, error) { return json.Marshal(f.PointerToTarget) } -// UnmarshalJSON implements the json.Unmarshaler interface by always throwing an error. +// UnmarshalJSON implements the [json.Unmarshaler] interface by always throwing an error. // // This implementation ensures that `capturedField` is not placed into a // container that DiffAgainst() does not know how to recurse into. @@ -286,19 +286,26 @@ func (f capturedField) UnmarshalJSON(buf []byte) error { type irrelevant struct{} -// Irrelevant returns a slot that can be placed in a jsonmatch.Object or jsonmatch.Array instance +// Irrelevant returns a slot that can be placed in an [Object] or [Array] instance // to ignore the contents of certain fields or array elements during an assertion. // // Irrelevant() slots only work inside data structures that DiffAgainst() knows how to recurse into. -// Please refer to the documentation on type Diffable for details. +// Please refer to the documentation on type [Diffable] for details. func Irrelevant() any { return irrelevant{} } -// MarshalJSON implements the json.Marshaler interface. +// MarshalJSON implements the [json.Marshaler] interface. // // This implementation ensures that `irrelevant` renders in a readable way // when a larger value containing it is serialized for a "type mismatch" or "value mismatch" error message. func (irrelevant) MarshalJSON() ([]byte, error) { return []byte(`"<irrelevant>"`), nil } + +var ( + // static assertion that the documented interfaces are indeed implemented + _ json.Marshaler = capturedField{} + _ json.Unmarshaler = capturedField{} + _ json.Marshaler = irrelevant{} +) diff --git a/option/option.go b/option/option.go index 8bd614c..c41dd61 100644 --- a/option/option.go +++ b/option/option.go @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2025 Stefan Majewsky <majewsky@gmx.net> // SPDX-License-Identifier: Apache-2.0 -// Package optional provides an Option type for Go. +// Package option provides an [Option] type for Go. // A value of the Option type will be in one of two states: "Some" (containing a value) or "None" (containing no value). // // The purpose of the Option type is to more clearly distinguish the two situations that standard Go uses pointer types for: @@ -306,7 +306,7 @@ var ( _ json.Unmarshaler = &Option[bool]{} ) -// Format implements the fmt.Formatter interface. +// Format implements the [fmt.Formatter] interface. // // If there is a contained value, it will be formatted as if it was given directly. // Otherwise, the string "<none>" will be formatted according to the specified width and flags. @@ -318,7 +318,7 @@ func (o Option[T]) Format(f fmt.State, verb rune) { } } -// IsZero implements the IsZeroer interface as understood by encoding/json and github.com/go-yaml/yaml. +// IsZero implements the IsZeroer interface as understood by encoding/json, github.com/go-yaml/yaml and github.com/yaml/go-yaml. // It is an alias of IsNone(). func (o Option[T]) IsZero() bool { return !o.isSome @@ -328,7 +328,7 @@ type yamlMarshaler interface { MarshalYAML() (any, error) } -// Scan implements the database/sql.Scanner interface. +// Scan implements the [sql.Scanner] interface. func (o *Option[T]) Scan(src any) error { var data sql.Null[T] err := data.Scan(src) @@ -344,7 +344,7 @@ func (o *Option[T]) Scan(src any) error { return nil } -// Value implements the database/sql/driver.Valuer interface. +// Value implements the [driver.Valuer] interface. func (o Option[T]) Value() (driver.Value, error) { if o.isSome { return driver.DefaultParameterConverter.ConvertValue(o.value) @@ -353,7 +353,7 @@ func (o Option[T]) Value() (driver.Value, error) { } } -// MarshalJSON implements the encoding/json.Marshaler interface. +// MarshalJSON implements the [json.Marshaler] interface. func (o Option[T]) MarshalJSON() ([]byte, error) { if o.isSome { return json.Marshal(o.value) @@ -362,7 +362,7 @@ func (o Option[T]) MarshalJSON() ([]byte, error) { } } -// UnmarshalJSON implements the encoding/json.Unmarshaler interface. +// UnmarshalJSON implements the [json.Unmarshaler] interface. func (o *Option[T]) UnmarshalJSON(buf []byte) error { var data *T err := json.Unmarshal(buf, &data) @@ -373,7 +373,7 @@ func (o *Option[T]) UnmarshalJSON(buf []byte) error { return nil } -// MarshalYAML implements the yaml.Marshaler interface from gopkg.in/yaml.v2 and v3. +// MarshalYAML implements the yaml.Marshaler interface from gopkg.in/yaml.v2 and v3 as well as go.yaml.in/yaml/v3. func (o Option[T]) MarshalYAML() (any, error) { if o.isSome { // If we just return o.value directly here, MarshalYAML will not be called @@ -391,8 +391,11 @@ func (o Option[T]) MarshalYAML() (any, error) { // UnmarshalYAML implements the yaml.Unmarshaler interface from gopkg.in/yaml.v2. // -// gopkg.in/yaml.v3 supports this interface via backwards-compatibility, -// so we intentionally do not use the v3-only signature that refers to the yaml.Node type. +// gopkg.in/yaml.v3 and go.yaml.in/yaml/v3 support this interface via backwards-compatibility. +// We cannot use the v3-only signature that refers to the yaml.Node type, +// because that would require us to choose only one of either v3 libraries to support. +// +// Ref: https://github.com/yaml/go-yaml/issues/56 func (o *Option[T]) UnmarshalYAML(unmarshal func(any) error) error { var data *T err := unmarshal(&data) |
