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 }