notice

package
v0.44.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 14, 2026 License: MIT Imports: 4 Imported by: 2

README

Notice Package

The notice package offers a suite of utilities for crafting clear, structured assertion messages. It simplifies the creation of readable error messages, featuring a header and contextual rows. With a fluent interface, it enables seamless message construction and includes helper functions for formatting and unwrapping errors.

Basic Usage

Create a Message
msg := notice.New("expected values to be equal").
    Want("%s", "abc").
    Have("%s", "xyz")

fmt.Println(msg)
// Output:
// expected values to be equal:
//   want: abc
//   have: xyz
Wrap Errors
ErrMy := errors.New("my error")

msg := notice.New("expected values to be equal").
    Want("%s", "abc").
    Have("%s", "xyz").
    Wrap(ErrMy)

is := errors.Is(msg, ErrMy)
fmt.Println(is)
// Output: true
Add Metadata
msg := notice.New("expected values to be equal").
    Want("%s", "abc").
    Have("%s", "xyz").
    MetaSet("key", 123)

fmt.Println(msg.MetaLookup("key"))
// Output: value true
}

For more examples see the examples_test.go file.

Indenting Lines

lines := notice.Indent(4, ' ', "line1\nline2\nline3")

fmt.Println(lines)
// Output:
//     line1
//     line2
//     line3

Documentation

Overview

Package notice simplifies building structured assertion messages.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrNotice = errors.New("notice error")

ErrNotice is a sentinel error automatically wrapped by all instances of Notice unless changed with the Notice.Wrap method.

Functions

func Indent

func Indent(n int, r rune, lns string) string

Indent indents lines with n number of runes. Lines are indented only if there are more than one line.

Example
package main

import (
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	lines := notice.Indent(4, ' ', "line1\nline2\nline3")

	fmt.Println(lines)
}
Output:

    line1
    line2
    line3

func Join added in v0.6.0

func Join(ers ...error) error

Join joins multiple notices into one. Returns the last not nil joined notice.

func Pad added in v0.11.0

func Pad(str string, length int) string

Pad left pads the string with spaces to the given length.

func TrialCmp added in v0.11.0

func TrialCmp(x, y *Notice) int

TrialCmp is a comparison function for sorting Notice instances by their Trail values. It returns:

-1 if x is less than y,
 0 if x equals y,
+1 if x is greater than y.

Types

type Notice

type Notice struct {
	Header string // Header message.

	// Is a trail to the field, element or key the notice message is about.
	Trail string

	Rows []Row          // Context rows.
	Meta map[string]any // Useful metadata.
	// contains filtered or unexported fields
}

Notice represents a structured expectation violation message consisting of a header, trail and multiple named rows with context.

nolint: errname

func From

func From(err error, prefix ...string) *Notice

From returns instance of Notice if it is in err's tree. If the prefix is not empty, the header will be prefixed with the first element in the slice. If "err" is not an instance of Notice, it will create a new one and wrap the "err".

Example
package main

import (
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	var err error
	err = notice.New("expected values to be equal").
		Want("%s", "abc").
		Have("%s", "xyz")

	msg := notice.From(err, "optional prefix").
		Append("my", "%s", "value")

	fmt.Println(msg)
}
Output:

[optional prefix] expected values to be equal:
  want: abc
  have: xyz
    my: value

func New

func New(header string, args ...any) *Notice

New creates a new Notice with a header formatted using fmt.Sprintf from the provided format string and optional arguments. The resulting Notice has its base error set to ErrNotice. The header is set by calling. If no arguments are provided, the format string is used as-is.

Example:

n := New("header: %s", "name") // Header: "header: name"
n := New("generic error")      // Header: "generic error"
Example
package main

import (
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	msg := notice.New("expected values to be equal").
		Want("%s", "abc").
		Have("%s", "xyz")

	fmt.Println(msg)
}
Output:

expected values to be equal:
  want: abc
  have: xyz
Example (FormatedHeader)
package main

import (
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	msg := notice.New("expected %s to be equal", "values").
		Want("%s", "abc").
		Have("%s", "xyz")

	fmt.Println(msg)
}
Output:

expected values to be equal:
  want: abc
  have: xyz

func SortNotices added in v0.11.0

func SortNotices(head *Notice, cmp func(a, b *Notice) int) *Notice

SortNotices sorts a doubly linked list of Notice instances starting at head, ordering nodes by their values in ascending order. It modifies the list in-place by updating prev and next pointers. The "cmp" function takes two Notice instances and returns -1 if "a" should come before "b", 0 if equal, or 1 if "a" should come after "b". If the head is nil or the list has one node, it returns the unchanged head. Returns the tail of the sorted list so it can be used directly with Join or [Node.Chain] to add more nodes.

func (*Notice) Append

func (msg *Notice) Append(name, format string, args ...any) *Notice

Append appends a new row with the specified name and value build using fmt.Sprintf from format and args. Implements fluent interface.

Example
package main

import (
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	msg := notice.New("expected values to be equal").
		Want("%s", "abc").
		Have("%s", "xyz").
		Append("name", "%d", 5)

	fmt.Println(msg)
}
Output:

expected values to be equal:
  want: abc
  have: xyz
  name: 5
Example (ForceNexLine)
package main

import (
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	msg := notice.New("expected values to be equal").
		Want("%s", "abc").
		Have("\n%s", "xyz").
		Append("name", "%d", 5)

	fmt.Println(msg)
}
Output:

expected values to be equal:
  want: abc
  have:
        xyz
  name: 5
Example (MultiLine)
package main

import (
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	msg := notice.New("expected values to be equal").
		Want("%s", "abc").
		Have("%s", "x\ny\nz").
		Append("name", "%d", 5)

	fmt.Println(msg)
}
Output:

expected values to be equal:
  want: abc
  have:
        x
        y
        z
  name: 5

func (*Notice) AppendRow

func (msg *Notice) AppendRow(desc ...Row) *Notice

AppendRow appends description rows to the message.

Example
package main

import (
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	row0 := notice.NewRow("number", "%d", 5)
	row1 := notice.NewRow("string", "%s", "abc")

	msg := notice.New("expected values to be equal").
		Want("%s", "abc").
		Have("%s", "xyz").
		AppendRow(row0, row1)

	fmt.Println(msg)
}
Output:

expected values to be equal:
    want: abc
    have: xyz
  number: 5
  string: abc

func (*Notice) Chain added in v0.11.0

func (msg *Notice) Chain(prev *Notice) *Notice

Chain adds the current Notice as next in the chain after "prev" and returns the current instance.

func (*Notice) Error

func (msg *Notice) Error() string

Notice returns a formatted string representation of the Notice.

nolint: gocognit, cyclop

func (*Notice) Have

func (msg *Notice) Have(format string, args ...any) *Notice

Have uses the Append method to append a row with the "have" name. If the "have" row already exists, it will just replace its value.

func (*Notice) Head added in v0.11.0

func (msg *Notice) Head() *Notice

Head returns the head of the notice chain, if the current notice instance is the head, it returns self.

func (*Notice) Is

func (msg *Notice) Is(target error) bool

func (*Notice) MetaLookup added in v0.11.0

func (msg *Notice) MetaLookup(key string) (any, bool)

MetaLookup returns the data set by Notice.MetaSet. Returns nil and false if the key was never set.

func (*Notice) MetaSet added in v0.11.0

func (msg *Notice) MetaSet(key string, val any) *Notice

MetaSet sets data. To get it back, use the Notice.MetaLookup method.

Example
package main

import (
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	msg := notice.New("expected values to be equal").
		Want("%s", "abc").
		Have("%s", "xyz").
		MetaSet("key", 123)

	fmt.Println(msg.MetaLookup("key"))
}
Output:

123 true

func (*Notice) Next added in v0.11.0

func (msg *Notice) Next() *Notice

Next returns the next Notice in the chain or nil.

func (*Notice) Prepend

func (msg *Notice) Prepend(name, format string, args ...any) *Notice

Prepend prepends a new row with the specified name and value built using fmt.Sprintf from format and args. Implements fluent interface.

Example
package main

import (
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	msg := notice.New("expected values to be equal").
		SetTrail("type.field").
		Want("%s", "abc").
		Have("%s", "xyz").
		Prepend("name", "%d", 5)

	fmt.Println(msg)
}
Output:

expected values to be equal:
  trail: type.field
   name: 5
   want: abc
   have: xyz

func (*Notice) Prev added in v0.11.0

func (msg *Notice) Prev() *Notice

Prev returns the previous Notice in the chain or nil.

func (*Notice) Remove

func (msg *Notice) Remove(name string) *Notice

Remove removes named row.

func (*Notice) SetHeader

func (msg *Notice) SetHeader(header string, args ...any) *Notice

SetHeader sets the header message. Implements fluent interface.

Example
package main

import (
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	msg := notice.New("expected values to be equal").
		Want("%s", "abc").
		Have("%s", "xyz")

	_ = msg.SetHeader("some other %s", "header")

	fmt.Println(msg)
}
Output:

some other header:
  want: abc
  have: xyz

func (*Notice) SetTrail added in v0.11.0

func (msg *Notice) SetTrail(trail string) *Notice

SetTrail adds trail row if "tr" is not an empty string. If the trail row already exists, it overwrites it. Implements fluent interface.

SetTrail examples:

  • Type
  • Type[1].Field
  • Type["key"].Field
Example
package main

import (
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	msg := notice.New("expected values to be equal").
		SetTrail("type.field").
		Want("%s", "abc").
		Have("%s", "xyz")

	fmt.Println(msg)
}
Output:

expected values to be equal:
  trail: type.field
   want: abc
   have: xyz

func (*Notice) Unwrap added in v0.6.0

func (msg *Notice) Unwrap() error

Unwrap returns wrapped error. By default, it returns ErrNotice unless a different error was specified using Notice.Wrap.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	ErrMy := errors.New("my error")

	msg := notice.New("expected values to be equal").
		Want("%s", "abc").
		Have("%s", "xyz").
		Wrap(ErrMy)

	fmt.Println(msg.Unwrap())
}
Output:

my error

func (*Notice) Want

func (msg *Notice) Want(format string, args ...any) *Notice

Want uses the Append method to append a row with the "want" name. If the "want" row already exists, it will just replace its value.

func (*Notice) Wrap

func (msg *Notice) Wrap(err error) *Notice

Wrap sets base error with provided one.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/ctx42/testing/pkg/notice"
)

func main() {
	ErrMy := errors.New("my error")

	msg := notice.New("expected values to be equal").
		Want("%s", "abc").
		Have("%s", "xyz").
		Wrap(ErrMy)

	is := errors.Is(msg, ErrMy)
	fmt.Println(is)
}
Output:

true

type Position added in v0.35.0

type Position string
const (
	PositionBefore Position = "before"
	PositionAfter  Position = "after"
)

type PositionedRow added in v0.35.0

type PositionedRow struct {
	Row
	Position string
}

type Row

type Row struct {
	Name   string
	Format string
	Args   []any
}

Row represents Notice row.

func NewRow

func NewRow(name, format string, args ...any) Row

NewRow is constructor function for Row.

func (Row) PadName added in v0.6.0

func (r Row) PadName(length int) string

PadName left pads row name with spaces to be requested length.

func (Row) String added in v0.6.0

func (r Row) String() string

String returns formated string value.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL