dapper

package module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2024 License: MIT Imports: 16 Imported by: 1

README

Dapper

Dapper is a pretty-printer for Go values.

Documentation Latest Version Build Status Code Coverage

Dapper is not intended to be used directly as a debugging tool, but as a library for applications that need to describe Go values to humans, such as testing frameworks.

Some features and design goals include:

  • Concise formatting, without type ambiguity
  • Deterministic output, useful for generating diffs using standard tools
  • A filter system for producing customized output on a per-value basis

Example

This example renders a basic tree structure. Note that the output only includes type names where the value's type can not be inferred from the context.

Code
package main

import (
	"fmt"
	"github.com/dogmatiq/dapper"
)

type TreeNode struct {
	Name     string
	Value    any
	Children []*TreeNode
}

type NodeValue struct{}

func main() {
	v := TreeNode{
		Name: "root",
		Children: []*TreeNode{
			{
				Name:  "branch #1",
				Value: 100,
			},
			{
				Name:  "branch #2",
				Value: NodeValue{},
			},
		},
	}

	dapper.Print(v)
}
Output
main.TreeNode{
    Name:     "root"
    Value:    nil
    Children: {
        {
            Name:     "branch #1"
            Value:    int(100)
            Children: nil
        }
        {
            Name:     "branch #2"
            Value:    main.NodeValue{}
            Children: nil
        }
    }
}

Documentation

Overview

Package dapper is a deterministic pretty-printer with minimal output.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AsConcrete added in v0.5.0

func AsConcrete[T any](v Value) (T, bool)

AsConcrete returns a v as type T if its dynamic type is exactly T.

func AsImplementationOf added in v0.5.0

func AsImplementationOf[T any](v Value) (T, bool)

AsImplementationOf returns v as a value of type T if it directly implements T.

func ErrorFilter added in v0.5.0

func ErrorFilter(r Renderer, v Value)

ErrorFilter is a Filter that formats implementations of [error].

func Format

func Format(v any) string

Format returns a pretty-printed representation of v using [DefaultPrinter].

Example
s := Format(123)
fmt.Println(s)
Output:

int(123)

func Is added in v0.5.0

func Is[T any](v Value) bool

Is returns true if v's type is exactly T.

func Print added in v0.2.0

func Print(values ...any)

Print writes a pretty-printed representation of v to os.Stdout using [DefaultPrinter].

Example
Print(123, 456.0)
Output:

int(123)
float64(456)

func ProtoFilter added in v0.5.0

func ProtoFilter(r Renderer, v Value)

ProtoFilter is a Filter that formats implementations of proto.Message.

func ReflectFilter added in v0.5.0

func ReflectFilter(r Renderer, v Value)

ReflectFilter is a Filter that formats various types from the reflect package.

func StringerFilter added in v0.4.4

func StringerFilter(r Renderer, v Value)

StringerFilter is a Filter that formats implementations of dapper.Stringer.

func SyncFilter added in v0.3.4

func SyncFilter(r Renderer, v Value)

SyncFilter is a filter that formats various types from the sync package.

func TimeFilter added in v0.3.4

func TimeFilter(r Renderer, v Value)

TimeFilter is a filter that formats various values from the time package.

func Write

func Write(w io.Writer, v any) (int, error)

Write writes a pretty-printed representation of v to w using [DefaultPrinter].

It returns the number of bytes written.

Example
w := &bytes.Buffer{}

if _, err := Write(w, 123); err != nil {
	panic(err)
}

w.WriteTo(os.Stdout)
Output:

int(123)

Types

type Annotator added in v0.6.0

type Annotator func(Value) string

Annotator is a function that annotates a value with additional information.

If it returns a non-empty string, the string is rendered after the value, regardless of whether the value is rendered by a filter.

type Config added in v0.4.0

type Config struct {
	// Annotators is a set of functions that can annotate values with additional
	// information, regardless of whether the value is rendered by a filter or
	// the default rendering logic.
	Annotators []Annotator

	// Filters is the set of filters to apply when formatting values.
	//
	// Filters are applied in the order they are provided. If any filter renders
	// output all subsequent filters and the default rendering logic are
	// skipped. Any annotations are still applied.
	Filters []Filter

	// RenderPackagePaths, when true, causes the printer to render the
	// fully-qualified package path when rendering type names.
	RenderPackagePaths bool

	// RenderUnexportedStructFields, when true, causes the printer to render
	// unexported struct fields.
	RenderUnexportedStructFields bool
	// contains filtered or unexported fields
}

Config is the configuration for a printer.

type Filter added in v0.3.0

type Filter func(r Renderer, v Value)

Filter is a function that provides custom formatting logic for a specific type or value.

The filter uses r to render v. If r is unused v is rendered using the default formatting logic.

type Option added in v0.6.0

type Option func(*Config)

Option controls the behavior of a printer.

func WithAnnotator added in v0.6.0

func WithAnnotator(a Annotator) Option

WithAnnotator adds an Annotator to the printer.

Annotators are used to add supplementary textual information to the output of the printer. They are applied after the value has been rendered by any filters or the default rendering logic.

func WithDefaultFilters added in v0.6.0

func WithDefaultFilters(enabled bool) Option

WithDefaultFilters enables or disables the default Filter set.

func WithFilter added in v0.6.0

func WithFilter(f Filter) Option

WithFilter adds a Filter to be applied when formatting values.

Filters allow overriding the default rendering logic for specific types or values. They are applied in the order they are provided. If any filter renders output all subsequent filters and the default rendering logic are skipped. Any annotations are still applied.

Filters added by WithFilter take precedence over the default filters, which can be disabled using WithDefaultFilters.

func WithPackagePaths added in v0.6.0

func WithPackagePaths(show bool) Option

WithPackagePaths controls whether the printer renders the fully-qualified package path in type names. This option is enabled by default.

func WithUnexportedStructFields added in v0.6.0

func WithUnexportedStructFields(show bool) Option

WithUnexportedStructFields controls whether the printer renders unexported struct fields. This option is enabled by default.

type Printer

type Printer struct {
	// contains filtered or unexported fields
}

Printer generates human-readable representations of Go values.

The output format is intended to be as minimal as possible, without being ambiguous. To that end, type information is only included where it can not be reliably inferred from the structure of the value.

func NewPrinter added in v0.6.0

func NewPrinter(options ...Option) *Printer

NewPrinter returns a new Printer with the given options applied.

Example
type TreeNode struct {
	Name     string
	Value    any
	Children []*TreeNode
}

type NodeValue struct{}

v := TreeNode{
	Name: "root",
	Children: []*TreeNode{
		{
			Name:  "branch #1",
			Value: 100,
		},
		{
			Name:  "branch #2",
			Value: NodeValue{},
		},
	},
}

p := NewPrinter()
s := p.Format(v)
fmt.Println(s)
Output:

github.com/dogmatiq/dapper_test.TreeNode{
    Name:     "root"
    Value:    nil
    Children: {
        {
            Name:     "branch #1"
            Value:    int(100)
            Children: nil
        }
        {
            Name:     "branch #2"
            Value:    github.com/dogmatiq/dapper_test.NodeValue{}
            Children: nil
        }
    }
}
Example (Options)
type TreeNode struct {
	Name     string
	Value    any
	Children []*TreeNode
}

type NodeValue struct{}

v := TreeNode{
	Name: "root",
	Children: []*TreeNode{
		{
			Name:  "branch #1",
			Value: 100,
		},
		{
			Name:  "branch #2",
			Value: NodeValue{},
		},
	},
}

p := NewPrinter(WithPackagePaths(false))
s := p.Format(v)
fmt.Println(s)
Output:

dapper_test.TreeNode{
    Name:     "root"
    Value:    nil
    Children: {
        {
            Name:     "branch #1"
            Value:    int(100)
            Children: nil
        }
        {
            Name:     "branch #2"
            Value:    dapper_test.NodeValue{}
            Children: nil
        }
    }
}

func (*Printer) Format

func (p *Printer) Format(v any) string

Format returns a pretty-printed representation of v.

func (*Printer) Write

func (p *Printer) Write(w io.Writer, v any) (_ int, err error)

Write writes a pretty-printed representation of v to w.

It returns the number of bytes written.

type Renderer added in v0.5.0

type Renderer interface {
	io.Writer

	Config() Config

	FormatType(Value) string
	WriteType(Value)

	WriteValue(Value)
	FormatValue(Value) string

	Indent()
	Outdent()
	Print(format string, args ...any)

	WithModifiedConfig(func(*Config)) Renderer
}

Renderer is an interface for rendering human-readable representations of arbitrary values.

type Stringer added in v0.4.4

type Stringer interface {
	DapperString() string
}

Stringer is an interface for types that produce their own Dapper representation.

type Value added in v0.3.0

type Value struct {
	// Value is the value to be formatted.
	Value reflect.Value

	// DynamicType is the value's type.
	DynamicType reflect.Type

	// StaticType is the type of the "variable" that the value is stored in,
	// which may not be the same as its dynamic type.
	//
	// For example, when formatting the values within a slice of "any"
	// containing integers, such as []any{1, 2, 3}, the DynamicType will be
	// "int", but the static type will be "any".
	StaticType reflect.Type

	// IsAmbiguousDynamicType is true if the value's dynamic type is not clear from
	// the context of what has already been rendered.
	IsAmbiguousDynamicType bool

	// IsAmbiguousStaticType is true if the value's static type is not clear from
	// the context of what has already been rendered.
	IsAmbiguousStaticType bool

	// IsUnexported is true if this value was obtained from an unexported struct
	// field. If so, it is not possible to extract the underlying value.
	IsUnexported bool
}

Value contains information about a Go value that is to be formatted.

func (*Value) IsAmbiguousType added in v0.3.0

func (v *Value) IsAmbiguousType() bool

IsAmbiguousType returns true if either the dynamic type or the static type is ambiguous.

func (*Value) IsAnonymousType added in v0.3.0

func (v *Value) IsAnonymousType() bool

IsAnonymousType returns true if the value has an anonymous type.

Directories

Path Synopsis
internal
stream
Package stream provides utilities for working with IO streams.
Package stream provides utilities for working with IO streams.

Jump to

Keyboard shortcuts

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