github.com/fluhus/gostuff@v0.4.1-0.20240331134726-be71864f2b5d/csvdec/fill.go (about)

     1  package csvdec
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strconv"
     7  )
     8  
     9  //go:generate go run github.com/fluhus/goat -i fillslice.got -o fillslice.go
    10  
    11  // TODO(amit): Support bool slices.
    12  
    13  // Populates a value's fields with the values in slice s.
    14  // Value is assumed to be a struct.
    15  func fillStruct(value reflect.Value, s []string) error {
    16  	// Check number of fields.
    17  	expectedLength := value.NumField()
    18  	if value.Field(value.NumField()-1).Kind() == reflect.Slice {
    19  		expectedLength--
    20  	}
    21  	if len(s) < expectedLength {
    22  		return fmt.Errorf("not enough values to populate all fields (%d/%d)",
    23  			len(s), value.NumField())
    24  	}
    25  
    26  	// Go over fields.
    27  	for i := 0; i < value.NumField(); i++ {
    28  		field := value.Field(i)
    29  		kind := field.Kind()
    30  
    31  		if !field.CanSet() {
    32  			panic(fmt.Errorf("Field %d cannot be set. Is it unexported?", i))
    33  		}
    34  
    35  		// Assign value according to type.
    36  		switch {
    37  		case kind == reflect.String:
    38  			field.SetString(s[i])
    39  
    40  		case kind >= reflect.Int && kind <= reflect.Int64:
    41  			v, err := strconv.ParseInt(s[i], 0, 64)
    42  			if err != nil {
    43  				return fmt.Errorf("field %d: %v", i, err)
    44  			}
    45  			field.SetInt(v)
    46  
    47  		case kind >= reflect.Uint && kind <= reflect.Uint64:
    48  			v, err := strconv.ParseUint(s[i], 0, 64)
    49  			if err != nil {
    50  				return fmt.Errorf("field %d: %v", i, err)
    51  			}
    52  			field.SetUint(v)
    53  
    54  		case kind == reflect.Float64 || kind == reflect.Float32:
    55  			v, err := strconv.ParseFloat(s[i], 64)
    56  			if err != nil {
    57  				return fmt.Errorf("field %d: %v", i, err)
    58  			}
    59  			field.SetFloat(v)
    60  
    61  		case kind == reflect.Bool:
    62  			v, err := strconv.ParseBool(s[i])
    63  			if err != nil {
    64  				return fmt.Errorf("field %d: %v", i, err)
    65  			}
    66  			field.SetBool(v)
    67  
    68  		case kind == reflect.Slice:
    69  			if i != value.NumField()-1 {
    70  				panic(fmt.Sprintf("Field %v is a slice. A slice may only be"+
    71  					" the last field.", i))
    72  			}
    73  			if err := fillSlice(field, s[i:]); err != nil {
    74  				return fmt.Errorf("field %v: %v", i, err)
    75  			}
    76  
    77  		default:
    78  			panic(fmt.Sprintf("Field %d is of an unsupported type: %v",
    79  				i, kind))
    80  		}
    81  	}
    82  
    83  	return nil
    84  }