columns

package
v0.28.0 Latest Latest
Warning

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

Go to latest
Published: May 6, 2024 License: Apache-2.0 Imports: 11 Imported by: 4

Documentation

Overview

Package columns is a library that helps to carry data structs in a more generic way using a combination of reflection and generics. It can work with any type of struct (and arrays of those), providing useful functions like sorting, grouping, filtering and printing. How columns are handled can be configured using tags along the struct definition. This keeps metadata and type data in the same place, avoiding extra and/or duplicated code and thereby the likelihood of typos/errors.

Columns just has to be initialized by passing a prototype of the struct. Afterwards you can use all helper functions (sorting, grouping, etc.) on it.

How does it work?

You simply add a "column" tag to members of the struct you want to handle, like so:

type Event struct {
	Node  string `json:"node" column:"node,width:12,ellipsis:middle"`
	PID   int    `json:"pid" column:"PID,hide,align:right,width:6"`
	Comm  string `json:"comm" column:"Comm"`
	Name  string `json:"name" column:"Name"`
	Dummy string `json:"dummy"`
	Time  int64  `json:"time" column:"Time,align:right,width:24,group:sum"`
}

Let's briefly analyze:

  • All fields that have a column tag will be considered, that means the `Dummy` field will not be considered.
  • Each column tag starts with the name of the field. This name is case-insensitive (and an error will be thrown if multiple fields share the same name.)
  • Additional information on the fields are added as a comma separated list after the name of the column; key and value (if applicable) are separated by a colon (see `attributes` below)

You initialize `Columns` by passing it the type of the struct you want to use, like so:

cols, err := columns.NewColumns[Event]()

The parameter could be used for specific options, passing nil will use the defaults.

Attributes

| Attribute | Value(s)               | Description                                                                                                          |
|-----------|------------------------|----------------------------------------------------------------------------------------------------------------------|
| align     | left,right             | defines the alignment of the column (whitespace before or after the value)                                           |
| ellipsis  | none,left,right,middle | defines how situations of content exceeding the given space should be handled, eg: where to place the ellipsis ("…") |
| fixed     | none                   | defines that this column will have a fixed width, even when auto-scaling is enabled                                  |
| group     | sum                    | defines what should happen with the field whenever entries are grouped (see grouping)                                |
| hide      | none                   | specifies that this column is not to be considered by default                                                        |
| precision | int                    | specifies the precision of floats (number of decimals)                                                               |
| width     | int                    | defines the space allocated for the column                                                                           |

Virtual Columns or Custom Extractors

Sometimes it's necessary to add columns on the fly or have special treatment when extracting values. This can be achieved by specifying a virtual column or custom extractors.

Say that for example you have a struct with a (by default) not printable member `Foo` that you still want to output.

type Event struct {
	Node  string `json:"node" column:"node,width:12,ellipsis:middle"`
	Foo   []byte
}

You can do that by adding a virtual column:

cols.AddColumn(columns.Attributes{
	Name:  "foo",
	Width: 14,
}, func(e *Event) any {
	return string(e.Foo)
})

This will convert the []byte to a string before printing it.

You can also just override the default extractor like so:

cols.SetExtractor("node", func(a *Event) any {
	return "Foobar"
})
Example
package main

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/inspektor-gadget/inspektor-gadget/pkg/columns"
	"github.com/inspektor-gadget/inspektor-gadget/pkg/columns/filter"
	"github.com/inspektor-gadget/inspektor-gadget/pkg/columns/formatter/textcolumns"
	"github.com/inspektor-gadget/inspektor-gadget/pkg/columns/sort"
)

type Employee struct {
	Name       string `column:"name" columnTags:"sensitive"`
	Age        int    `column:"age" columnTags:"sensitive"`
	Department string `column:"department"`
}

var Employees = []*Employee{
	{"Alice", 32, "Security"},
	{"Bob", 26, "Security"},
	{"Eve", 99, "Security also"},
}

// Defining the column helper here lets the program crash on start if there are
// errors in the syntax
var employeeColumns = columns.MustCreateColumns[Employee]()

func main() {
	out := bytes.NewBuffer(nil)

	// Get columnMap
	cmap := employeeColumns.GetColumnMap()

	// Get a new formatter
	formatter := textcolumns.NewFormatter(cmap, textcolumns.WithRowDivider(textcolumns.DividerDash))

	// Output the whole table
	formatter.WriteTable(out, Employees)

	out.WriteString("\n")

	// Reverse the order by name and output again
	sort.SortEntries[Employee](cmap, Employees, []string{"-name"})
	formatter.WriteTable(out, Employees)

	out.WriteString("\n")

	// Now only get security personell
	securityOnly, err := filter.FilterEntries[Employee](cmap, Employees, []string{"department:Security"})
	if err != nil {
		panic(err)
	}
	formatter.WriteTable(out, securityOnly)

	out.WriteString("\n")

	// Confidentiality!
	formatter = textcolumns.NewFormatter(employeeColumns.GetColumnMap(columns.WithoutTag("sensitive")), textcolumns.WithRowDivider(textcolumns.DividerDash))
	formatter.WriteTable(out, securityOnly)

	// Split output by newline and remove whitespace at the end of each line to match test output below
	outLines := strings.Split(out.String(), "\n")
	for _, line := range outLines {
		fmt.Println(strings.TrimRight(line, " "))
	}

}
Output:

NAME             AGE              DEPARTMENT
——————————————————————————————————————————————————
Alice            32               Security
Bob              26               Security
Eve              99               Security also

NAME             AGE              DEPARTMENT
——————————————————————————————————————————————————
Eve              99               Security also
Bob              26               Security
Alice            32               Security

NAME             AGE              DEPARTMENT
——————————————————————————————————————————————————
Bob              26               Security
Alice            32               Security

DEPARTMENT
————————————————
Security
Security

Index

Examples

Constants

View Source
const (
	MaxCharsUint8  = 3  // 255
	MaxCharsInt8   = 4  // -128
	MaxCharsUint16 = 5  // 65535
	MaxCharsInt16  = 6  // -32768
	MaxCharsUint32 = 10 // 4294967295
	MaxCharsInt32  = 11 // -2147483648
	MaxCharsUint64 = 20 // 18446744073709551615
	MaxCharsInt64  = 20 // −9223372036854775808
	MaxCharsBool   = 5  // false
	MaxCharsChar   = 1  // 1 character
)

Variables

This section is empty.

Functions

func GetFieldAsArrayFunc added in v0.19.0

func GetFieldAsArrayFunc[OT any, T any](column ColumnInternals) func(entry *T) []OT

GetFieldAsArrayFunc returns a helper function to access an array of type OT of a struct T without using reflection. It does not differentiate between direct members of the struct and members of embedded structs.

func GetFieldAsNumberFunc added in v0.17.0

func GetFieldAsNumberFunc[OT constraints.Integer | constraints.Float, T any](column ColumnInternals) func(entry *T) OT

GetFieldAsNumberFunc returns a helper function to access a field of struct T as a number.

func GetFieldAsString added in v0.18.0

func GetFieldAsString[T any](column ColumnInternals) func(entry *T) string

func GetFieldAsStringExt added in v0.18.0

func GetFieldAsStringExt[T any](column ColumnInternals, floatFormat byte, floatPrecision int) func(entry *T) string

func GetFieldFunc added in v0.17.0

func GetFieldFunc[OT any, T any](column ColumnInternals) func(entry *T) OT

GetFieldFunc returns a helper function to access the value of type OT of a struct T without using reflection. It differentiates between direct members of the struct and members of embedded structs. If any of the embedded structs being accessed is a nil-pointer, the default value of OT will be returned. Custom extractors will be preferred.

func GetFieldFuncExt added in v0.18.0

func GetFieldFuncExt[OT any, T any](column ColumnInternals, raw bool) func(entry *T) OT

GetFieldFuncExt returns a helper function to access the value of type OT of a struct T without using reflection. It differentiates between direct members of the struct and members of embedded structs. If any of the embedded structs being accessed is a nil-pointer, the default value of OT will be returned. If raw is set, even if a custom extractor has been set, the returned func will access the underlying values.

func GetWidthFromType added in v0.26.0

func GetWidthFromType(kind reflect.Kind) int

func MustRegisterTemplate

func MustRegisterTemplate(name, value string)

MustRegisterTemplate calls RegisterTemplate and will panic if an error occurs.

func RegisterTemplate

func RegisterTemplate(name, value string) error

RegisterTemplate registers the column settings template in value to name. Whenever a column has "template:name" set, this template will be used. Plausibility and syntax checks will only be applied when the template gets used.

func SetFieldAsNumberFunc added in v0.18.0

func SetFieldAsNumberFunc[OT constraints.Integer | constraints.Float, T any](column ColumnInternals) func(entry *T, value OT)

SetFieldAsNumberFunc returns a helper function to set a field of struct T to a number.

func SetFieldFunc added in v0.18.0

func SetFieldFunc[OT any, T any](column ColumnInternals) func(entry *T, val OT)

SetFieldFunc returns a helper function to set the value of type OT to a member of struct T without using reflection. It differentiates between direct members of the struct and members of embedded structs. If any of the embedded structs being accessed is a nil-pointer, no value will be set

func ToLowerStrings

func ToLowerStrings(in []string) []string

ToLowerStrings transforms the elements of an array of strings into lowercase.

Types

type Alignment

type Alignment int

Alignment defines whether text should be aligned to the left or right inside a column

const (
	AlignLeft Alignment = iota
	AlignRight
)

type Attributes added in v0.17.0

type Attributes struct {
	// Name of the column; case-insensitive for most use cases; includes inherited prefixes
	Name string `yaml:"name"`
	// Name of the columns without inherited prefixes
	RawName string `yaml:"raw_name"`
	// Width to reserve for this column
	Width int `yaml:"width"`
	// MinWidth will be the minimum width this column will be scaled to when using auto-scaling
	MinWidth int `yaml:"min_width"`
	// MaxWidth will be the maximum width this column will be scaled to when using auto-scaling
	MaxWidth int `yaml:"max_width"`
	// Alignment of this column (left or right)
	Alignment Alignment `yaml:"alignment"`
	// Visible defines whether a column is to be shown by default
	Visible bool `yaml:"visible"`
	// GroupType defines the aggregation method used when grouping this column
	GroupType GroupType `yaml:"group_type"`
	// EllipsisType defines how to abbreviate this column if the value needs more space than is available
	EllipsisType ellipsis.EllipsisType `yaml:"ellipsis_type"`
	// FixedWidth forces the Width even when using Auto-Scaling
	FixedWidth bool `yaml:"fixed_width"`
	// Precision defines how many decimals should be shown on float values, default: 2
	Precision int `yaml:"precision"`
	// Description can hold a short description of the field that can be used to aid the user
	Description string `yaml:"description"`
	// Order defines the default order in which columns are shown
	Order int `yaml:"order"`
	// Tags can be used to dynamically include or exclude columns
	Tags []string `yaml:"tags"`
	// Template defines the template that will be used. Non-typed templates will be applied first.
	Template string `yaml:"template"`
}

type Column

type Column[T any] struct {
	Attributes
	Extractor func(*T) any // Extractor to be used; this can be defined to transform the output before retrieving the actual value
	// contains filtered or unexported fields
}

func (*Column[T]) Get

func (ci *Column[T]) Get(entry *T) reflect.Value

Get returns the reflected value of an entry for the current column; if given nil, it will return the zero value of the underlying type

func (*Column[T]) GetAttributes added in v0.17.0

func (ci *Column[T]) GetAttributes() *Attributes

func (*Column[T]) GetRaw

func (ci *Column[T]) GetRaw(entry *T) reflect.Value

GetRaw returns the reflected value of an entry for the current column without evaluating the extractor func; if given nil or run on a virtual or manually added column, it will return the zero value of the underlying type. If using embedded structs via pointers and the embedded value is nil, it will also return the zero value of the underlying type.

func (*Column[T]) GetRef

func (ci *Column[T]) GetRef(v reflect.Value) reflect.Value

GetRef returns the reflected value of an already reflected entry for the current column; expects v to be valid or will panic

func (*Column[T]) HasCustomExtractor added in v0.11.0

func (ci *Column[T]) HasCustomExtractor() bool

HasCustomExtractor returns true, if the column has a user defined extractor set

func (*Column[T]) HasNoTags

func (ci *Column[T]) HasNoTags() bool

func (*Column[T]) HasTag

func (ci *Column[T]) HasTag(tag string) bool

func (*Column[T]) IsEmbedded

func (ci *Column[T]) IsEmbedded() bool

IsEmbedded returns true, if the current column is a member of an embedded struct

func (*Column[T]) IsVirtual added in v0.11.0

func (ci *Column[T]) IsVirtual() bool

IsVirtual returns true, if the column has direct reference to a field

func (*Column[T]) Kind

func (ci *Column[T]) Kind() reflect.Kind

Kind returns the underlying kind of the column (always reflect.String in case of virtual columns)

func (*Column[T]) RawType added in v0.17.0

func (ci *Column[T]) RawType() reflect.Type

RawType returns the underlying type of the column

func (*Column[T]) Type

func (ci *Column[T]) Type() reflect.Type

Type returns the underlying type of the column (reflect.String, if a custom extractor is used)

type ColumnFilter

type ColumnFilter func(matcher ColumnMatcher) bool

func And

func And(filters ...ColumnFilter) ColumnFilter

And combines several ColumnFilter and matches if all filters match

func Or

func Or(filters ...ColumnFilter) ColumnFilter

Or combines several ColumnFilter and matches if one filter matches

func WithAnyTag added in v0.21.0

func WithAnyTag(tags []string) ColumnFilter

WithAnyTag makes sure that all returned columns contain at least one of the given tags

func WithEmbedded

func WithEmbedded(embedded bool) ColumnFilter

WithEmbedded checks whether a column matches the embedded criteria

func WithNoTags

func WithNoTags() ColumnFilter

WithNoTags makes sure that all returned columns contain no tags

func WithTag

func WithTag(tag string) ColumnFilter

WithTag makes sure that all returned columns contain all the given tag

func WithTags

func WithTags(tags []string) ColumnFilter

WithTags makes sure that all returned columns contain all the given tags

func WithoutExceptTag added in v0.17.0

func WithoutExceptTag(tag, exception string) ColumnFilter

WithoutExceptTag makes sure that all returned columns don't contain the given tag, except when it also includes the exception given

func WithoutTag

func WithoutTag(tag string) ColumnFilter

WithoutTag makes sure that all returned columns don't contain the given tag

func WithoutTags

func WithoutTags(tags []string) ColumnFilter

WithoutTags makes sure that all returned columns contain none of the given tags

type ColumnInterface

type ColumnInterface[T any] interface {
	GetColumn(columnName string) (*Column[T], bool)
	GetColumnMap(filters ...ColumnFilter) ColumnMap[T]
	GetOrderedColumns(filters ...ColumnFilter) []*Column[T]
	GetColumnNames(filters ...ColumnFilter) []string
}

ColumnInterface is an interface that is valid for Columns and ColumnMap

type ColumnInternals added in v0.17.0

type ColumnInternals interface {
	IsVirtual() bool
	HasCustomExtractor() bool
	// contains filtered or unexported methods
}

ColumnInternals is a non-generic interface to return internal values of columns like offsets for faster access.

type ColumnMap

type ColumnMap[T any] map[string]*Column[T]

func (ColumnMap[T]) GetColumn

func (c ColumnMap[T]) GetColumn(columnName string) (*Column[T], bool)

GetColumn returns a specific column by its name

func (ColumnMap[T]) GetColumnMap

func (c ColumnMap[T]) GetColumnMap(filters ...ColumnFilter) ColumnMap[T]

GetColumnMap returns a map of column names to their Column, filtered by filters

func (ColumnMap[T]) GetColumnNames

func (c ColumnMap[T]) GetColumnNames(filters ...ColumnFilter) []string

GetColumnNames returns a list of column names, ordered by the column order values

func (ColumnMap[T]) GetOrderedColumns

func (c ColumnMap[T]) GetOrderedColumns(filters ...ColumnFilter) []*Column[T]

GetOrderedColumns returns an ordered list of columns according to their order values, filtered by filters

func (ColumnMap[T]) VerifyColumnNames

func (c ColumnMap[T]) VerifyColumnNames(columnNames []string) (valid []string, invalid []string)

VerifyColumnNames takes a list of column names and returns two lists, one containing the valid column names and another containing the invalid column names. Prefixes like "-" for descending sorting will be ignored.

type ColumnMatcher

type ColumnMatcher interface {
	HasTag(string) bool
	HasNoTags() bool
	IsEmbedded() bool
}

type Columns

type Columns[T any] struct {
	// columns map[string]*Column[T]
	ColumnMap[T]
	// contains filtered or unexported fields
}

func MustCreateColumns

func MustCreateColumns[T any](options ...Option) *Columns[T]

MustCreateColumns creates a new column helper and panics if it cannot successfully be created; useful if you want to initialize Columns as a global variable inside a package (similar to regexp.MustCompile)

func NewColumns

func NewColumns[T any](options ...Option) (*Columns[T], error)

NewColumns creates a new column helper. T must be of type struct and its fields must have a column tag if they should be considered. Struct and pointer to struct fields will be recursively traversed by default unless a column tag with parameter "noembed" is present. Options can be passed to change the default behavior.

func (*Columns[T]) AddColumn

func (c *Columns[T]) AddColumn(attributes Attributes, extractor func(*T) any) error

AddColumn adds a virtual column to the table. This virtual column requires at least a name and an Extractor

func (*Columns[T]) AddFields added in v0.18.0

func (c *Columns[T]) AddFields(fields []DynamicField, base func(*T) unsafe.Pointer) error

func (*Columns[T]) MustAddColumn

func (c *Columns[T]) MustAddColumn(attributes Attributes, extractor func(*T) any)

MustAddColumn adds a new column and panics if it cannot successfully do so

func (*Columns[T]) MustSetExtractor

func (c *Columns[T]) MustSetExtractor(columnName string, extractor func(*T) any)

MustSetExtractor adds a new extractor to a column and panics if it cannot successfully do so

func (*Columns[T]) SetExtractor

func (c *Columns[T]) SetExtractor(columnName string, extractor func(*T) any) error

SetExtractor sets the extractor function for a specific column

type DynamicField added in v0.18.0

type DynamicField struct {
	Attributes *Attributes
	Tag        string
	Template   string
	Type       reflect.Type
	Offset     uintptr
}

DynamicField manually describes fields this library should be able to access. Combined with the DynamicPtr interface, this can be used to access fields for example in a byte slice. Either Attributes or a Tag has to be set - if both are set, Tag takes precedence over the Attributes.

type GroupType

type GroupType int

GroupType defines how columns should be aggregated in case of grouping

const (
	GroupTypeNone GroupType = iota // GroupTypeNone uses the first occurrence of a value in a group to represent its group
	GroupTypeSum                   // GroupTypeSum adds values of this column up for its group
)

type Option

type Option func(*Options)

func WithAlignment

func WithAlignment(a Alignment) Option

WithAlignment sets the default alignment

func WithEllipsis

func WithEllipsis(e ellipsis.EllipsisType) Option

WithEllipsis sets the default ellipsis type

func WithRequireColumnDefinition

func WithRequireColumnDefinition(require bool) Option

WithRequireColumnDefinition sets whether the library should handle struct members without a column tag

func WithWidth

func WithWidth(w int) Option

WithWidth sets the default column width

type Options

type Options struct {
	DefaultAlignment        Alignment             // default text alignment to use; default AlignLeft
	DefaultEllipsis         ellipsis.EllipsisType // default type of ellipsis to use for overflowing text; default: ellipsis.End
	DefaultWidth            int                   // width to be used when no width is specified for a column; default: 16
	RequireColumnDefinition bool                  // if set to false, Columns will consider all struct members, regardless of the column tag (in backticks behind the struct field, like `column:"columnName"`) being present; default: true
}

func GetDefault

func GetDefault() *Options

type Order

type Order bool

Order defines the sorting order of columns

const (
	OrderAsc  Order = true  // OrderAsc sorts in ascending alphanumerical order
	OrderDesc Order = false // OrderDesc sorts in descending alphanumerical order
)

Directories

Path Synopsis
Package ellipsis helps to truncate text at a specific width and adding an optional ellipsis ("…") to indicate that the text has been truncated.
Package ellipsis helps to truncate text at a specific width and adding an optional ellipsis ("…") to indicate that the text has been truncated.
examples
Package filter helps filtering an array of structs that were analyzed by the columns library.
Package filter helps filtering an array of structs that were analyzed by the columns library.
formatter
textcolumns
Package textcolumns helps to output structs (and events of structs) using metadata from a `Columns` instance in a tabular way suitable for consoles or other frontends using fixed-width characters / fonts.
Package textcolumns helps to output structs (and events of structs) using metadata from a `Columns` instance in a tabular way suitable for consoles or other frontends using fixed-width characters / fonts.
Package group can group the entries of an array by one or more columns.
Package group can group the entries of an array by one or more columns.
Package sort can be used to sort an array by their columns in either ascending or descending order.
Package sort can be used to sort an array by their columns in either ascending or descending order.

Jump to

Keyboard shortcuts

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