github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/ginx/cast/cast.go (about)

     1  package cast
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/bingoohuang/gg/pkg/strcase"
    12  )
    13  
    14  // TryFind tries to find value by field name or tag value.
    15  func TryFind(filedName, tagValue string, getter func(string) (interface{}, bool)) (interface{}, bool) {
    16  	if tagValue != "" {
    17  		return getter(tagValue)
    18  	}
    19  
    20  	return TryAnyCase(filedName, getter)
    21  }
    22  
    23  // TryAnyCase tries to find value by name in any-case.
    24  func TryAnyCase(name string, getter func(string) (interface{}, bool)) (interface{}, bool) {
    25  	if value, ok := getter(name); ok {
    26  		return value, true
    27  	}
    28  
    29  	if value, ok := getter(strcase.ToCamelLower(name)); ok {
    30  		return value, true
    31  	}
    32  
    33  	if value, ok := getter(strcase.ToSnake(name)); ok {
    34  		return value, true
    35  	}
    36  
    37  	if value, ok := getter(strcase.ToSnakeUpper(name)); ok {
    38  		return value, true
    39  	}
    40  
    41  	if value, ok := getter(strcase.ToKebab(name)); ok {
    42  		return value, true
    43  	}
    44  
    45  	if value, ok := getter(strcase.ToKebabUpper(name)); ok {
    46  		return value, true
    47  	}
    48  
    49  	return "", false
    50  }
    51  
    52  // ToStruct populates the properties to the structure's field.
    53  func ToStruct(b interface{}, tagName string, getter func(filedName, tagValue string) (interface{}, bool)) error {
    54  	v := reflect.ValueOf(b)
    55  	vt := v.Type()
    56  
    57  	if vt.Kind() != reflect.Ptr {
    58  		return errors.New("only argument of pointer of structure  supported")
    59  	}
    60  
    61  	v = v.Elem()
    62  	if vt = v.Type(); vt.Kind() != reflect.Struct {
    63  		return errors.New("only argument of pointer of structure  supported")
    64  	}
    65  
    66  	for i := 0; i < vt.NumField(); i++ {
    67  		structField := vt.Field(i)
    68  		if structField.PkgPath != "" { // bypass non-exported fields
    69  			continue
    70  		}
    71  
    72  		fieldType := structField.Type
    73  		fieldPtr := fieldType.Kind() == reflect.Ptr
    74  
    75  		if fieldPtr {
    76  			fieldType = fieldType.Elem()
    77  		}
    78  
    79  		field := v.Field(i)
    80  
    81  		if fieldType.Kind() == reflect.Struct {
    82  			if err := parseStruct(fieldType, tagName, fieldPtr, field, getter); err != nil {
    83  				return err
    84  			}
    85  
    86  			continue
    87  		}
    88  
    89  		v, ok := getter(structField.Name, structField.Tag.Get(tagName))
    90  		if !ok {
    91  			continue
    92  		}
    93  
    94  		if vs, ok := v.(string); ok {
    95  			vv, err := To(vs, structField.Type)
    96  			if err != nil {
    97  				return err
    98  			}
    99  
   100  			field.Set(vv)
   101  
   102  			continue
   103  		}
   104  
   105  		if reflect.TypeOf(v) == structField.Type {
   106  			field.Set(reflect.ValueOf(v))
   107  			continue
   108  		}
   109  
   110  		return fmt.Errorf("unable to convert %v to %v", v, structField.Type)
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  func parseStruct(fieldType reflect.Type, tag string, ptr bool, field reflect.Value,
   117  	getter func(name string, tagValue string) (interface{}, bool),
   118  ) error {
   119  	fv := reflect.New(fieldType)
   120  	if err := ToStruct(fv.Interface(), tag, getter); err != nil {
   121  		return err
   122  	}
   123  
   124  	if ptr {
   125  		field.Set(fv)
   126  	} else {
   127  		field.Set(fv.Elem())
   128  	}
   129  
   130  	return nil
   131  }
   132  
   133  // Caster defines the function prototype for cast string a any type.
   134  type Caster func(s string, asPtr bool) (reflect.Value, error)
   135  
   136  // nolint:gochecknoglobals
   137  var (
   138  	InvalidValue = reflect.Value{}
   139  )
   140  
   141  // casters defines default for basic types.
   142  // nolint:gochecknoglobals
   143  var casters = map[reflect.Type]Caster{
   144  	reflect.TypeOf(false):            toBool,
   145  	reflect.TypeOf(float32(0)):       toFloat32,
   146  	reflect.TypeOf(float64(0)):       toFloat64,
   147  	reflect.TypeOf(0):                toInt,
   148  	reflect.TypeOf(int8(0)):          toInt8,
   149  	reflect.TypeOf(int16(0)):         toInt16,
   150  	reflect.TypeOf(int32(0)):         toInt32,
   151  	reflect.TypeOf(int64(0)):         toInt64,
   152  	reflect.TypeOf(""):               toString,
   153  	reflect.TypeOf(uint(0)):          toUint,
   154  	reflect.TypeOf(uint8(0)):         toUint8,
   155  	reflect.TypeOf(uint16(0)):        toUint16,
   156  	reflect.TypeOf(uint32(0)):        toUint32,
   157  	reflect.TypeOf(uint64(0)):        toUint64,
   158  	reflect.TypeOf(time.Duration(0)): toTimeDuration,
   159  }
   160  
   161  // To cast string a any type.
   162  func To(s string, t reflect.Type) (reflect.Value, error) {
   163  	asPtr := t.Kind() == reflect.Ptr
   164  	if asPtr {
   165  		t = t.Elem()
   166  	}
   167  
   168  	if caster, ok := casters[t]; ok {
   169  		return caster(s, asPtr)
   170  	}
   171  
   172  	return InvalidValue, errors.New("casting not supported")
   173  }
   174  
   175  func toTimeDuration(s string, asPtr bool) (reflect.Value, error) {
   176  	d, err := time.ParseDuration(s)
   177  	if err != nil {
   178  		return InvalidValue, err
   179  	}
   180  
   181  	if asPtr {
   182  		return reflect.ValueOf(&d), nil
   183  	}
   184  
   185  	return reflect.ValueOf(d), nil
   186  }
   187  
   188  func toBool(s string, asPtr bool) (reflect.Value, error) {
   189  	v, err := strconv.ParseBool(s)
   190  	if err != nil {
   191  		switch strings.ToLower(s) {
   192  		case "yes", "ok", "1", "on":
   193  			v = true
   194  			err = nil
   195  		}
   196  	}
   197  
   198  	if err != nil {
   199  		return InvalidValue, err
   200  	}
   201  
   202  	if asPtr {
   203  		return reflect.ValueOf(&v), nil
   204  	}
   205  
   206  	return reflect.ValueOf(v), nil
   207  }
   208  
   209  func toFloat32(s string, asPtr bool) (reflect.Value, error) {
   210  	v, err := strconv.ParseFloat(s, 32)
   211  	if err != nil {
   212  		return InvalidValue, err
   213  	}
   214  
   215  	vv := float32(v)
   216  
   217  	if asPtr {
   218  		return reflect.ValueOf(&vv), nil
   219  	}
   220  
   221  	return reflect.ValueOf(vv), nil
   222  }
   223  
   224  func toFloat64(s string, asPtr bool) (reflect.Value, error) {
   225  	v, err := strconv.ParseFloat(s, 64)
   226  	if err != nil {
   227  		return InvalidValue, err
   228  	}
   229  
   230  	vv := v
   231  
   232  	if asPtr {
   233  		return reflect.ValueOf(&vv), nil
   234  	}
   235  
   236  	return reflect.ValueOf(vv), nil
   237  }
   238  
   239  func toInt(s string, asPtr bool) (reflect.Value, error) {
   240  	v, err := strconv.ParseInt(s, 10, 0)
   241  	if err != nil {
   242  		return InvalidValue, err
   243  	}
   244  
   245  	vv := int(v)
   246  
   247  	if asPtr {
   248  		return reflect.ValueOf(&vv), nil
   249  	}
   250  
   251  	return reflect.ValueOf(vv), nil
   252  }
   253  
   254  func toInt8(s string, asPtr bool) (reflect.Value, error) {
   255  	v, err := strconv.ParseInt(s, 10, 8)
   256  	if err != nil {
   257  		return InvalidValue, err
   258  	}
   259  
   260  	vv := int8(v)
   261  
   262  	if asPtr {
   263  		return reflect.ValueOf(&vv), nil
   264  	}
   265  
   266  	return reflect.ValueOf(vv), nil
   267  }
   268  
   269  func toInt16(s string, asPtr bool) (reflect.Value, error) {
   270  	v, err := strconv.ParseInt(s, 10, 16)
   271  	if err != nil {
   272  		return InvalidValue, err
   273  	}
   274  
   275  	vv := int16(v)
   276  
   277  	if asPtr {
   278  		return reflect.ValueOf(&vv), nil
   279  	}
   280  
   281  	return reflect.ValueOf(vv), nil
   282  }
   283  
   284  func toInt32(s string, asPtr bool) (reflect.Value, error) {
   285  	v, err := strconv.ParseInt(s, 10, 32)
   286  	if err != nil {
   287  		return InvalidValue, err
   288  	}
   289  
   290  	vv := int32(v)
   291  
   292  	if asPtr {
   293  		return reflect.ValueOf(&vv), nil
   294  	}
   295  
   296  	return reflect.ValueOf(vv), nil
   297  }
   298  
   299  func toInt64(s string, asPtr bool) (reflect.Value, error) {
   300  	v, err := strconv.ParseInt(s, 10, 64)
   301  	if err != nil {
   302  		return InvalidValue, err
   303  	}
   304  
   305  	if asPtr {
   306  		return reflect.ValueOf(&v), nil
   307  	}
   308  
   309  	return reflect.ValueOf(v), nil
   310  }
   311  
   312  func toString(s string, asPtr bool) (reflect.Value, error) {
   313  	if asPtr {
   314  		return reflect.ValueOf(&s), nil
   315  	}
   316  
   317  	return reflect.ValueOf(s), nil
   318  }
   319  
   320  func toUint(s string, asPtr bool) (reflect.Value, error) {
   321  	v, err := strconv.ParseUint(s, 10, 0)
   322  	if err != nil {
   323  		return InvalidValue, err
   324  	}
   325  
   326  	vv := uint(v)
   327  
   328  	if asPtr {
   329  		return reflect.ValueOf(&vv), nil
   330  	}
   331  
   332  	return reflect.ValueOf(vv), nil
   333  }
   334  
   335  func toUint8(s string, asPtr bool) (reflect.Value, error) {
   336  	v, err := strconv.ParseUint(s, 10, 8)
   337  	if err != nil {
   338  		return InvalidValue, err
   339  	}
   340  
   341  	vv := uint8(v)
   342  
   343  	if asPtr {
   344  		return reflect.ValueOf(&vv), nil
   345  	}
   346  
   347  	return reflect.ValueOf(vv), nil
   348  }
   349  
   350  func toUint16(s string, asPtr bool) (reflect.Value, error) {
   351  	v, err := strconv.ParseUint(s, 10, 16)
   352  	if err != nil {
   353  		return InvalidValue, err
   354  	}
   355  
   356  	vv := uint16(v)
   357  
   358  	if asPtr {
   359  		return reflect.ValueOf(&vv), nil
   360  	}
   361  
   362  	return reflect.ValueOf(vv), nil
   363  }
   364  
   365  func toUint32(s string, asPtr bool) (reflect.Value, error) {
   366  	v, err := strconv.ParseUint(s, 10, 32)
   367  	if err != nil {
   368  		return InvalidValue, err
   369  	}
   370  
   371  	vv := uint32(v)
   372  
   373  	if asPtr {
   374  		return reflect.ValueOf(&vv), nil
   375  	}
   376  
   377  	return reflect.ValueOf(vv), nil
   378  }
   379  
   380  func toUint64(s string, asPtr bool) (reflect.Value, error) {
   381  	v, err := strconv.ParseUint(s, 10, 64)
   382  	if err != nil {
   383  		return InvalidValue, err
   384  	}
   385  
   386  	if asPtr {
   387  		return reflect.ValueOf(&v), nil
   388  	}
   389  
   390  	return reflect.ValueOf(v), nil
   391  }