github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/columns/columns.go (about)

     1  // Copyright 2022-2024 The Inspektor Gadget authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package columns
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"fmt"
    21  	"reflect"
    22  	"sort"
    23  	"strconv"
    24  	"strings"
    25  	"unsafe"
    26  
    27  	"golang.org/x/exp/constraints"
    28  )
    29  
    30  type ColumnMap[T any] map[string]*Column[T]
    31  
    32  type Columns[T any] struct {
    33  	// columns map[string]*Column[T]
    34  	ColumnMap[T]
    35  	options *Options
    36  }
    37  
    38  const (
    39  	virtualIndex = -1
    40  	manualIndex  = -2
    41  )
    42  
    43  var stringType = reflect.TypeOf("") // used for virtual columns and columns with a custom extractor
    44  
    45  // MustCreateColumns creates a new column helper and panics if it cannot successfully be created; useful if you
    46  // want to initialize Columns as a global variable inside a package (similar to regexp.MustCompile)
    47  func MustCreateColumns[T any](options ...Option) *Columns[T] {
    48  	cols, err := NewColumns[T](options...)
    49  	if err != nil {
    50  		panic(err)
    51  	}
    52  	return cols
    53  }
    54  
    55  // NewColumns creates a new column helper. T must be of type struct and its fields must have a column tag if they
    56  // should be considered. Struct and pointer to struct fields will be recursively traversed by default unless a column
    57  // tag with parameter "noembed" is present. Options can be passed to change the default behavior.
    58  func NewColumns[T any](options ...Option) (*Columns[T], error) {
    59  	opts := GetDefault()
    60  	for _, o := range options {
    61  		o(opts)
    62  	}
    63  
    64  	entryPrototype := new(T)
    65  
    66  	t := reflect.TypeOf(entryPrototype)
    67  	if t.Kind() == reflect.Pointer {
    68  		t = t.Elem()
    69  	}
    70  
    71  	// Generics sadly don't provide a way to constraint to a type like struct{}, so we need to check here
    72  	if t.Kind() != reflect.Struct {
    73  		return nil, fmt.Errorf("NewColumns works only on structs")
    74  	}
    75  
    76  	columns := &Columns[T]{
    77  		ColumnMap: make(ColumnMap[T]),
    78  		options:   opts,
    79  	}
    80  
    81  	err := columns.iterateFields(t, nil, 0, "", nil)
    82  	if err != nil {
    83  		return nil, fmt.Errorf("trying to initialize columns on type %s: %w", t.String(), err)
    84  	}
    85  
    86  	return columns, nil
    87  }
    88  
    89  func (c *Columns[T]) AddFields(fields []DynamicField, base func(*T) unsafe.Pointer) error {
    90  	newCols := make(map[string]*Column[T])
    91  	for _, f := range fields {
    92  		column := &Column[T]{
    93  			explicitName:  true,
    94  			offset:        f.Offset,
    95  			fieldIndex:    manualIndex,
    96  			kind:          f.Type.Kind(),
    97  			columnType:    f.Type,
    98  			rawColumnType: f.Type,
    99  			getStart:      base,
   100  		}
   101  
   102  		// Copy attributes, if present
   103  		if f.Attributes != nil {
   104  			column.Attributes = *f.Attributes
   105  		} else {
   106  			// Set defaults
   107  			column.Attributes = Attributes{
   108  				EllipsisType: c.options.DefaultEllipsis,
   109  				Alignment:    c.options.DefaultAlignment,
   110  				Visible:      true,
   111  				Precision:    2,
   112  				Order:        len(c.ColumnMap) * 10,
   113  			}
   114  		}
   115  
   116  		// Apply tag
   117  		err := column.fromTag(f.Tag)
   118  		if err != nil {
   119  			return fmt.Errorf("applying tag: %w", err)
   120  		}
   121  
   122  		// After applying attributes and tag, we should have a name
   123  		if column.Name == "" {
   124  			return fmt.Errorf("missing name")
   125  		}
   126  
   127  		column.applyTemplate()
   128  
   129  		lowerName := strings.ToLower(column.Name)
   130  
   131  		if _, ok := c.ColumnMap[lowerName]; ok {
   132  			return fmt.Errorf("duplicate column name %q", column.Name)
   133  		}
   134  
   135  		if _, ok := newCols[lowerName]; ok {
   136  			return fmt.Errorf("duplicate column name %q", column.Name)
   137  		}
   138  
   139  		newCols[strings.ToLower(column.Name)] = column
   140  	}
   141  
   142  	for colName, col := range newCols {
   143  		c.ColumnMap[colName] = col
   144  	}
   145  	return nil
   146  }
   147  
   148  // GetColumn returns a specific column by its name
   149  func (c ColumnMap[T]) GetColumn(columnName string) (*Column[T], bool) {
   150  	column, ok := c[strings.ToLower(columnName)]
   151  	return column, ok
   152  }
   153  
   154  // GetColumnMap returns a map of column names to their Column, filtered by filters
   155  func (c ColumnMap[T]) GetColumnMap(filters ...ColumnFilter) ColumnMap[T] {
   156  	if len(filters) == 0 {
   157  		return c
   158  	}
   159  	// return a new copy
   160  	res := make(map[string]*Column[T])
   161  
   162  filter:
   163  	for columnName, column := range c {
   164  		for _, f := range filters {
   165  			if !f(column) {
   166  				continue filter
   167  			}
   168  		}
   169  		res[columnName] = column
   170  	}
   171  	return res
   172  }
   173  
   174  // GetOrderedColumns returns an ordered list of columns according to their order values, filtered by filters
   175  func (c ColumnMap[T]) GetOrderedColumns(filters ...ColumnFilter) []*Column[T] {
   176  	columns := make([]*Column[T], 0, len(c))
   177  
   178  filter:
   179  	for _, column := range c {
   180  		for _, f := range filters {
   181  			if !f(column) {
   182  				continue filter
   183  			}
   184  		}
   185  		columns = append(columns, column)
   186  	}
   187  	sort.Slice(columns, func(i, j int) bool {
   188  		return columns[i].Order < columns[j].Order
   189  	})
   190  	return columns
   191  }
   192  
   193  // GetColumnNames returns a list of column names, ordered by the column order values
   194  func (c ColumnMap[T]) GetColumnNames(filters ...ColumnFilter) []string {
   195  	columns := make([]string, 0, len(c))
   196  	sorted := c.GetOrderedColumns(filters...)
   197  	for _, column := range sorted {
   198  		columns = append(columns, column.Name)
   199  	}
   200  	return columns
   201  }
   202  
   203  // VerifyColumnNames takes a list of column names and returns two lists, one containing the valid column names
   204  // and another containing the invalid column names. Prefixes like "-" for descending sorting will be ignored.
   205  func (c ColumnMap[T]) VerifyColumnNames(columnNames []string) (valid []string, invalid []string) {
   206  	for _, cname := range columnNames {
   207  		cname = strings.ToLower(cname)
   208  
   209  		// Strip prefixes
   210  		cname = strings.TrimPrefix(cname, "-")
   211  
   212  		if _, ok := c[cname]; ok {
   213  			valid = append(valid, cname)
   214  			continue
   215  		}
   216  		invalid = append(invalid, cname)
   217  	}
   218  	return
   219  }
   220  
   221  func (c *Columns[T]) iterateFields(t reflect.Type, sub []subField, offset uintptr, prefix string, tags []string) error {
   222  	isPtr := false
   223  	if t.Kind() == reflect.Pointer {
   224  		if t.Elem().Kind() != reflect.Struct {
   225  			return errors.New("unsupported pointer type")
   226  		}
   227  		isPtr = true
   228  		t = t.Elem()
   229  	}
   230  	for i := 0; i < t.NumField(); i++ {
   231  		f := t.Field(i)
   232  
   233  		tag := f.Tag.Get("column")
   234  		// tagSet := len(tag) > 0
   235  
   236  		column := &Column[T]{
   237  			Attributes: Attributes{
   238  				EllipsisType: c.options.DefaultEllipsis,
   239  				Alignment:    c.options.DefaultAlignment,
   240  				Visible:      true,
   241  				Precision:    2,
   242  				Order:        len(c.ColumnMap) * 10,
   243  			},
   244  			offset: offset + f.Offset,
   245  		}
   246  
   247  		// store kind for faster lookups if required
   248  		column.kind = f.Type.Kind()
   249  		column.columnType = f.Type
   250  		column.rawColumnType = f.Type
   251  
   252  		// read information from tag
   253  		err := column.fromTag(tag)
   254  		if err != nil {
   255  			return fmt.Errorf("parsing tag for %q on field %q: %w", t.Name(), f.Name, err)
   256  		}
   257  
   258  		// add optional tags
   259  		if tags := f.Tag.Get("columnTags"); tags != "" {
   260  			column.Tags = strings.Split(strings.ToLower(tags), ",")
   261  		}
   262  		column.Tags = append(column.Tags, tags...)
   263  
   264  		// Apply prefixes to name
   265  		column.Name = prefix + column.Name
   266  
   267  		// If this field is a pointer to a struct or a struct, try to embed it unless a "noembed" tag is set
   268  		if f.Type.Kind() == reflect.Struct || (f.Type.Kind() == reflect.Pointer && f.Type.Elem().Kind() == reflect.Struct) {
   269  			if !strings.Contains(tag, ",noembed") {
   270  				newOffset := offset + f.Offset
   271  				if f.Type.Kind() == reflect.Pointer {
   272  					newOffset = 0 // offset of the struct pointed to will begin at zero again
   273  				}
   274  				newPrefix := prefix
   275  
   276  				// If not explicit name was set for this field, don't inherit a new prefix
   277  				if column.explicitName {
   278  					newPrefix = column.Name + "."
   279  				}
   280  				err := c.iterateFields(
   281  					f.Type,
   282  					append(append([]subField{}, sub...), subField{
   283  						index:       i,
   284  						offset:      offset + f.Offset,
   285  						parentIsPtr: isPtr,
   286  						isPtr:       f.Type.Kind() == reflect.Pointer,
   287  					}),
   288  					newOffset,
   289  					newPrefix,
   290  					append(tags, column.Tags...),
   291  				)
   292  				if err != nil {
   293  					return err
   294  				}
   295  				continue
   296  			}
   297  		}
   298  
   299  		if tag == "" && c.options.RequireColumnDefinition {
   300  			continue
   301  		}
   302  
   303  		if tag == "" {
   304  			// set the name, so it will get picked up
   305  			tag = f.Name
   306  		}
   307  
   308  		if sub == nil {
   309  			column.fieldIndex = i
   310  		} else {
   311  			// Nested structs
   312  			column.subFieldIndex = append(append([]subField{}, sub...), subField{i, offset + f.Offset, isPtr, false})
   313  		}
   314  
   315  		if column.useTemplate {
   316  			err := column.applyTemplate()
   317  			if err != nil {
   318  				return err
   319  			}
   320  			// re-apply information from field tag to overwrite template settings
   321  			err = column.fromTag(tag)
   322  			if err != nil {
   323  				return fmt.Errorf("parsing tag for %q on field %q: %w", t.Name(), f.Name, err)
   324  			}
   325  		}
   326  
   327  		// fall back to struct field name if column name is empty
   328  		if column.Name == "" {
   329  			column.Name = f.Name
   330  		}
   331  
   332  		if column.Width > 0 && column.MinWidth > column.Width {
   333  			return fmt.Errorf("minWidth should not be greater than width on field %q", t.Name())
   334  		}
   335  		if column.MaxWidth > 0 {
   336  			if column.MaxWidth < column.Width {
   337  				return fmt.Errorf("maxWidth should not be less than width on field %q", t.Name())
   338  			}
   339  			if column.MaxWidth < column.MinWidth {
   340  				return fmt.Errorf("maxWidth must be greater than minWidth %q", t.Name())
   341  			}
   342  		}
   343  
   344  		// check if we can default to a maxWidth for this field
   345  		if column.MaxWidth == 0 {
   346  			column.MaxWidth = column.getWidthFromType()
   347  		}
   348  
   349  		if column.Width == 0 {
   350  			column.Width = c.options.DefaultWidth
   351  		}
   352  		if column.MinWidth > column.Width {
   353  			column.Width = column.MinWidth
   354  		}
   355  
   356  		// add optional description
   357  		column.Description = f.Tag.Get("columnDesc")
   358  
   359  		lowerName := strings.ToLower(column.Name)
   360  		if _, ok := c.ColumnMap[lowerName]; ok {
   361  			return fmt.Errorf("duplicate column %q for %q", lowerName, t.Name())
   362  		}
   363  
   364  		c.ColumnMap[lowerName] = column
   365  	}
   366  
   367  	return nil
   368  }
   369  
   370  // AddColumn adds a virtual column to the table. This virtual column requires at least a
   371  // name and an Extractor
   372  func (c *Columns[T]) AddColumn(attributes Attributes, extractor func(*T) any) error {
   373  	if attributes.Name == "" {
   374  		return errors.New("no name set for column")
   375  	}
   376  	if extractor == nil {
   377  		return fmt.Errorf("no extractor set for column %q", attributes.Name)
   378  	}
   379  
   380  	var temp T
   381  	typ := reflect.TypeOf(extractor(&temp))
   382  
   383  	column := Column[T]{
   384  		Attributes:    attributes,
   385  		Extractor:     extractor,
   386  		fieldIndex:    virtualIndex,
   387  		kind:          typ.Kind(),
   388  		columnType:    typ,
   389  		rawColumnType: typ,
   390  	}
   391  
   392  	column.applyTemplate()
   393  
   394  	columnName := strings.ToLower(column.Name)
   395  	if _, ok := c.ColumnMap[columnName]; ok {
   396  		return fmt.Errorf("duplicate column name %q", column.Name)
   397  	}
   398  
   399  	if column.Width == 0 {
   400  		column.Width = c.options.DefaultWidth
   401  	}
   402  
   403  	c.ColumnMap[columnName] = &column
   404  	return nil
   405  }
   406  
   407  // MustAddColumn adds a new column and panics if it cannot successfully do so
   408  func (c *Columns[T]) MustAddColumn(attributes Attributes, extractor func(*T) any) {
   409  	err := c.AddColumn(attributes, extractor)
   410  	if err != nil {
   411  		panic(err)
   412  	}
   413  }
   414  
   415  // SetExtractor sets the extractor function for a specific column
   416  func (c *Columns[T]) SetExtractor(columnName string, extractor func(*T) any) error {
   417  	if extractor == nil {
   418  		return fmt.Errorf("extractor func must be non-nil")
   419  	}
   420  	column, ok := c.ColumnMap[strings.ToLower(columnName)]
   421  	if !ok {
   422  		return fmt.Errorf("field %q not found", columnName)
   423  	}
   424  
   425  	var temp T
   426  	typ := reflect.TypeOf(extractor(&temp))
   427  
   428  	column.kind = typ.Kind()
   429  	column.Extractor = extractor
   430  	column.columnType = typ
   431  	return nil
   432  }
   433  
   434  // MustSetExtractor adds a new extractor to a column and panics if it cannot successfully do so
   435  func (c *Columns[T]) MustSetExtractor(columnName string, extractor func(*T) any) {
   436  	err := c.SetExtractor(columnName, extractor)
   437  	if err != nil {
   438  		panic(fmt.Errorf("setting extractor for %q column: %w", columnName, err))
   439  	}
   440  }
   441  
   442  // ColumnInternals is a non-generic interface to return internal values of columns like offsets
   443  // for faster access.
   444  type ColumnInternals interface {
   445  	getOffset() uintptr
   446  	getSubFields() []subField
   447  	IsVirtual() bool
   448  	HasCustomExtractor() bool
   449  }
   450  
   451  // GetFieldFunc returns a helper function to access the value of type OT of a struct T
   452  // without using reflection. It differentiates between direct members of the struct and
   453  // members of embedded structs. If any of the embedded structs being accessed is a nil-pointer,
   454  // the default value of OT will be returned. Custom extractors will be preferred.
   455  func GetFieldFunc[OT any, T any](column ColumnInternals) func(entry *T) OT {
   456  	return GetFieldFuncExt[OT, T](column, false)
   457  }
   458  
   459  // GetFieldFuncExt returns a helper function to access the value of type OT of a struct T
   460  // without using reflection. It differentiates between direct members of the struct and
   461  // members of embedded structs. If any of the embedded structs being accessed is a nil-pointer,
   462  // the default value of OT will be returned. If raw is set, even if a custom extractor has been
   463  // set, the returned func will access the underlying values.
   464  func GetFieldFuncExt[OT any, T any](column ColumnInternals, raw bool) func(entry *T) OT {
   465  	if column.IsVirtual() || (column.HasCustomExtractor() && !raw) {
   466  		return func(entry *T) OT {
   467  			return column.(*Column[T]).Extractor(entry).(OT)
   468  		}
   469  	}
   470  	sub := column.getSubFields()
   471  	offset := column.getOffset()
   472  	subLen := len(sub)
   473  	if subLen == 0 {
   474  		return func(entry *T) OT {
   475  			start := unsafe.Pointer(entry)
   476  			if column.(*Column[T]).getStart != nil {
   477  				start = column.(*Column[T]).getStart(entry)
   478  				if start == nil {
   479  					return *new(OT)
   480  				}
   481  			}
   482  			// Previous note was outdated since we weren't using uintptr here
   483  			return *(*OT)(unsafe.Add(start, offset))
   484  		}
   485  	}
   486  
   487  	return func(entry *T) OT {
   488  		start := unsafe.Pointer(entry)
   489  		if column.(*Column[T]).getStart != nil {
   490  			start = column.(*Column[T]).getStart(entry)
   491  			if start == nil {
   492  				return *new(OT)
   493  			}
   494  		}
   495  		for i := 0; i < subLen-1; i++ {
   496  			if sub[i].isPtr {
   497  				start = unsafe.Add(start, sub[i].offset) // now pointing at the pointer
   498  				start = unsafe.Pointer(*(*uintptr)(start))
   499  				if start == nil {
   500  					// If we at any time hit a nil-pointer, we return the default
   501  					// value of type OT
   502  					var defaultValue OT
   503  					return defaultValue
   504  				}
   505  			}
   506  		}
   507  		return *(*OT)(unsafe.Add(start, (sub)[subLen-1].offset))
   508  	}
   509  }
   510  
   511  // GetFieldAsArrayFunc returns a helper function to access an array of type OT of a struct T
   512  // without using reflection. It does not differentiate between direct members of the struct and
   513  // members of embedded structs.
   514  func GetFieldAsArrayFunc[OT any, T any](column ColumnInternals) func(entry *T) []OT {
   515  	l := column.(*Column[T]).RawType().Len()
   516  
   517  	return func(entry *T) []OT {
   518  		entryStart := unsafe.Pointer(entry)
   519  		if column.(*Column[T]).getStart != nil {
   520  			entryStart = column.(*Column[T]).getStart(entry)
   521  		}
   522  
   523  		fieldStart := unsafe.Add(entryStart, column.getOffset())
   524  		srcSlice := unsafe.Slice((*OT)(fieldStart), l)
   525  		r := make([]OT, l)
   526  		copy(r, srcSlice)
   527  		return r
   528  	}
   529  }
   530  
   531  // SetFieldFunc returns a helper function to set the value of type OT to a member of struct T
   532  // without using reflection. It differentiates between direct members of the struct and
   533  // members of embedded structs. If any of the embedded structs being accessed is a nil-pointer,
   534  // no value will be set
   535  func SetFieldFunc[OT any, T any](column ColumnInternals) func(entry *T, val OT) {
   536  	// We cannot write to virtual columns
   537  	if column.IsVirtual() {
   538  		return func(entry *T, val OT) {
   539  		}
   540  	}
   541  	sub := column.getSubFields()
   542  	offset := column.getOffset()
   543  	subLen := len(sub)
   544  	if subLen == 0 {
   545  		return func(entry *T, val OT) {
   546  			start := unsafe.Pointer(entry)
   547  			if column.(*Column[T]).getStart != nil {
   548  				start = column.(*Column[T]).getStart(entry)
   549  			}
   550  			// Previous note was outdated since we weren't using uintptr here
   551  			*(*OT)(unsafe.Add(start, offset)) = val
   552  		}
   553  	}
   554  
   555  	return func(entry *T, val OT) {
   556  		start := unsafe.Pointer(entry)
   557  		if column.(*Column[T]).getStart != nil {
   558  			start = column.(*Column[T]).getStart(entry)
   559  		}
   560  		for i := 0; i < subLen-1; i++ {
   561  			if sub[i].isPtr {
   562  				start = unsafe.Add(start, sub[i].offset) // now pointing at the pointer
   563  				start = unsafe.Pointer(*(*uintptr)(start))
   564  				if start == nil {
   565  					// If we at any time hit a nil-pointer, we cannot set the value
   566  					return
   567  				}
   568  			}
   569  		}
   570  		*(*OT)(unsafe.Add(start, (sub)[subLen-1].offset)) = val
   571  	}
   572  }
   573  
   574  func GetFieldAsStringExt[T any](column ColumnInternals, floatFormat byte, floatPrecision int) func(entry *T) string {
   575  	switch column.(*Column[T]).Kind() {
   576  	case reflect.Int,
   577  		reflect.Int8,
   578  		reflect.Int16,
   579  		reflect.Int32,
   580  		reflect.Int64:
   581  		ff := GetFieldAsNumberFunc[int64, T](column)
   582  		return func(entry *T) string {
   583  			return strconv.FormatInt(ff(entry), 10)
   584  		}
   585  	case reflect.Uint,
   586  		reflect.Uint8,
   587  		reflect.Uint16,
   588  		reflect.Uint32,
   589  		reflect.Uint64:
   590  		ff := GetFieldAsNumberFunc[uint64, T](column)
   591  		return func(entry *T) string {
   592  			return strconv.FormatUint(ff(entry), 10)
   593  		}
   594  	case reflect.Float32, reflect.Float64:
   595  		ff := GetFieldAsNumberFunc[float64, T](column)
   596  		return func(entry *T) string {
   597  			return strconv.FormatFloat(ff(entry), floatFormat, floatPrecision, 64)
   598  		}
   599  	case reflect.Bool:
   600  		ff := GetFieldFunc[bool, T](column)
   601  		return func(entry *T) string {
   602  			if ff(entry) {
   603  				return "true"
   604  			}
   605  			return "false"
   606  		}
   607  	case reflect.Array:
   608  		s := column.(*Column[T]).Type().Elem().Size()
   609  		// c strings: []char null terminated
   610  		if s == 1 {
   611  			return func(entry *T) string {
   612  				arr := GetFieldAsArrayFunc[byte, T](column)(entry)
   613  				i := bytes.IndexByte(arr, 0)
   614  				if i != -1 {
   615  					arr = arr[:i]
   616  				}
   617  				return string(arr)
   618  			}
   619  		}
   620  
   621  		return func(entry *T) string {
   622  			return "TODO"
   623  		}
   624  	case reflect.Slice:
   625  		s := column.(*Column[T]).Type().Elem().Size()
   626  		if s == 1 {
   627  			ff := GetFieldFunc[[]byte, T](column)
   628  			return func(entry *T) string {
   629  				return string(ff(entry))
   630  			}
   631  		}
   632  
   633  		return func(entry *T) string {
   634  			return "TODO"
   635  		}
   636  	case reflect.Map:
   637  		keyType := column.(*Column[T]).Type().Key()
   638  		valueType := column.(*Column[T]).Type().Elem()
   639  
   640  		if keyType.Kind() == reflect.String && valueType.Kind() == reflect.String {
   641  			ff := GetFieldFunc[map[string]string, T](column)
   642  			return func(entry *T) string {
   643  				m := ff(entry)
   644  				kvPairs := make([]string, 0, len(m))
   645  				for k, v := range m {
   646  					kvPairs = append(kvPairs, fmt.Sprintf("%s=%s", k, v))
   647  				}
   648  				sort.Strings(kvPairs)
   649  				return strings.Join(kvPairs, ",")
   650  			}
   651  		}
   652  
   653  		return func(entry *T) string {
   654  			return "TODO"
   655  		}
   656  	case reflect.String:
   657  		return GetFieldFunc[string, T](column)
   658  	}
   659  	return func(entry *T) string {
   660  		return ""
   661  	}
   662  }
   663  
   664  func GetFieldAsString[T any](column ColumnInternals) func(entry *T) string {
   665  	return GetFieldAsStringExt[T](column, 'E', -1)
   666  }
   667  
   668  // GetFieldAsNumberFunc returns a helper function to access a field of struct T as a number.
   669  func GetFieldAsNumberFunc[OT constraints.Integer | constraints.Float, T any](column ColumnInternals) func(entry *T) OT {
   670  	switch column.(*Column[T]).Kind() {
   671  	default:
   672  		var defaultValue OT
   673  		return func(entry *T) OT {
   674  			return defaultValue
   675  		}
   676  	case reflect.Int:
   677  		ff := GetFieldFunc[int, T](column)
   678  		return func(entry *T) OT {
   679  			return OT(ff(entry))
   680  		}
   681  	case reflect.Int8:
   682  		ff := GetFieldFunc[int8, T](column)
   683  		return func(entry *T) OT {
   684  			return OT(ff(entry))
   685  		}
   686  	case reflect.Int16:
   687  		ff := GetFieldFunc[int16, T](column)
   688  		return func(entry *T) OT {
   689  			return OT(ff(entry))
   690  		}
   691  	case reflect.Int32:
   692  		ff := GetFieldFunc[int32, T](column)
   693  		return func(entry *T) OT {
   694  			return OT(ff(entry))
   695  		}
   696  	case reflect.Int64:
   697  		ff := GetFieldFunc[int64, T](column)
   698  		return func(entry *T) OT {
   699  			return OT(ff(entry))
   700  		}
   701  	case reflect.Uint:
   702  		ff := GetFieldFunc[uint, T](column)
   703  		return func(entry *T) OT {
   704  			return OT(ff(entry))
   705  		}
   706  	case reflect.Uint8:
   707  		ff := GetFieldFunc[uint8, T](column)
   708  		return func(entry *T) OT {
   709  			return OT(ff(entry))
   710  		}
   711  	case reflect.Uint16:
   712  		ff := GetFieldFunc[uint16, T](column)
   713  		return func(entry *T) OT {
   714  			return OT(ff(entry))
   715  		}
   716  	case reflect.Uint32:
   717  		ff := GetFieldFunc[uint32, T](column)
   718  		return func(entry *T) OT {
   719  			return OT(ff(entry))
   720  		}
   721  	case reflect.Uint64:
   722  		ff := GetFieldFunc[uint64, T](column)
   723  		return func(entry *T) OT {
   724  			return OT(ff(entry))
   725  		}
   726  	case reflect.Float32:
   727  		ff := GetFieldFunc[float32, T](column)
   728  		return func(entry *T) OT {
   729  			return OT(ff(entry))
   730  		}
   731  	case reflect.Float64:
   732  		ff := GetFieldFunc[float64, T](column)
   733  		return func(entry *T) OT {
   734  			return OT(ff(entry))
   735  		}
   736  	}
   737  }
   738  
   739  // SetFieldAsNumberFunc returns a helper function to set a field of struct T to a number.
   740  func SetFieldAsNumberFunc[OT constraints.Integer | constraints.Float, T any](column ColumnInternals) func(entry *T, value OT) {
   741  	switch column.(*Column[T]).Kind() {
   742  	case reflect.Int:
   743  		ff := SetFieldFunc[int, T](column)
   744  		return func(entry *T, value OT) {
   745  			ff(entry, int(value))
   746  		}
   747  	case reflect.Int8:
   748  		ff := SetFieldFunc[int8, T](column)
   749  		return func(entry *T, value OT) {
   750  			ff(entry, int8(value))
   751  		}
   752  	case reflect.Int16:
   753  		ff := SetFieldFunc[int16, T](column)
   754  		return func(entry *T, value OT) {
   755  			ff(entry, int16(value))
   756  		}
   757  	case reflect.Int32:
   758  		ff := SetFieldFunc[int32, T](column)
   759  		return func(entry *T, value OT) {
   760  			ff(entry, int32(value))
   761  		}
   762  	case reflect.Int64:
   763  		ff := SetFieldFunc[int64, T](column)
   764  		return func(entry *T, value OT) {
   765  			ff(entry, int64(value))
   766  		}
   767  	case reflect.Uint:
   768  		ff := SetFieldFunc[uint, T](column)
   769  		return func(entry *T, value OT) {
   770  			ff(entry, uint(value))
   771  		}
   772  	case reflect.Uint8:
   773  		ff := SetFieldFunc[uint8, T](column)
   774  		return func(entry *T, value OT) {
   775  			ff(entry, uint8(value))
   776  		}
   777  	case reflect.Uint16:
   778  		ff := SetFieldFunc[uint16, T](column)
   779  		return func(entry *T, value OT) {
   780  			ff(entry, uint16(value))
   781  		}
   782  	case reflect.Uint32:
   783  		ff := SetFieldFunc[uint32, T](column)
   784  		return func(entry *T, value OT) {
   785  			ff(entry, uint32(value))
   786  		}
   787  	case reflect.Uint64:
   788  		ff := SetFieldFunc[uint64, T](column)
   789  		return func(entry *T, value OT) {
   790  			ff(entry, uint64(value))
   791  		}
   792  	case reflect.Float32:
   793  		ff := SetFieldFunc[float32, T](column)
   794  		return func(entry *T, value OT) {
   795  			ff(entry, float32(value))
   796  		}
   797  	case reflect.Float64:
   798  		ff := SetFieldFunc[float64, T](column)
   799  		return func(entry *T, value OT) {
   800  			ff(entry, float64(value))
   801  		}
   802  	}
   803  	return func(entry *T, value OT) {}
   804  }