goki.dev/laser@v0.1.34/basic.go (about)

     1  // Copyright (c) 2023, The Goki Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package laser
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"reflect"
    11  	"strconv"
    12  
    13  	"goki.dev/enums"
    14  	"goki.dev/glop/bools"
    15  )
    16  
    17  // Has convenience functions for converting any (e.g. properties) to given
    18  // types uses the "ok" bool mechanism to report failure -- are as robust and
    19  // general as possible.
    20  //
    21  // WARNING: these violate many of the type-safety features of Go but OTOH give
    22  // maximum robustness, appropriate for the world of end-user settable
    23  // properties, and deal with most common-sense cases, e.g., string <-> number,
    24  // etc.  nil values return !ok
    25  
    26  // AnyIsNil checks if an interface value is nil -- the interface itself could be
    27  // nil, or the value pointed to by the interface could be nil -- this checks
    28  // both, safely
    29  // gopy:interface=handle
    30  func AnyIsNil(it any) bool {
    31  	if it == nil {
    32  		return true
    33  	}
    34  	v := reflect.ValueOf(it)
    35  	vk := v.Kind()
    36  	if vk == reflect.Ptr || vk == reflect.Interface || vk == reflect.Map || vk == reflect.Slice || vk == reflect.Func || vk == reflect.Chan {
    37  		return v.IsNil()
    38  	}
    39  	return false
    40  }
    41  
    42  // KindIsBasic returns true if the reflect.Kind is a basic, elemental
    43  // type such as Int, Float, etc
    44  func KindIsBasic(vk reflect.Kind) bool {
    45  	if vk >= reflect.Bool && vk <= reflect.Complex128 {
    46  		return true
    47  	}
    48  	return false
    49  }
    50  
    51  // ValueIsZero returns true if the reflect.Value is Zero or nil or invalid or
    52  // otherwise doesn't have a useful value -- from
    53  // https://github.com/golang/go/issues/7501
    54  func ValueIsZero(v reflect.Value) bool {
    55  	if !v.IsValid() {
    56  		return true
    57  	}
    58  	switch v.Kind() {
    59  	case reflect.Array, reflect.String:
    60  		return v.Len() == 0
    61  	case reflect.Bool:
    62  		return !v.Bool()
    63  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    64  		return v.Int() == 0
    65  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
    66  		return v.Uint() == 0
    67  	case reflect.Float32, reflect.Float64:
    68  		return v.Float() == 0
    69  	case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
    70  		return v.IsNil()
    71  	case reflect.Func:
    72  		return v == reflect.Zero(v.Type())
    73  	}
    74  	return false
    75  }
    76  
    77  // ToBool robustly converts to a bool any basic elemental type
    78  // (including pointers to such) using a big type switch organized
    79  // for greatest efficiency. It tries the [goki.dev/glop/bools.Booler]
    80  // interface if not a bool type. It falls back on reflection when all
    81  // else fails.
    82  //
    83  //gopy:interface=handle
    84  func ToBool(v any) (bool, error) {
    85  	switch vt := v.(type) {
    86  	case bool:
    87  		return vt, nil
    88  	case *bool:
    89  		if vt == nil {
    90  			return false, fmt.Errorf("got nil *bool")
    91  		}
    92  		return *vt, nil
    93  	}
    94  
    95  	if br, ok := v.(bools.Booler); ok {
    96  		return br.Bool(), nil
    97  	}
    98  
    99  	switch vt := v.(type) {
   100  	case int:
   101  		return vt != 0, nil
   102  	case *int:
   103  		if vt == nil {
   104  			return false, fmt.Errorf("got nil *int")
   105  		}
   106  		return *vt != 0, nil
   107  	case int32:
   108  		return vt != 0, nil
   109  	case *int32:
   110  		if vt == nil {
   111  			return false, fmt.Errorf("got nil *int32")
   112  		}
   113  		return *vt != 0, nil
   114  	case int64:
   115  		return vt != 0, nil
   116  	case *int64:
   117  		if vt == nil {
   118  			return false, fmt.Errorf("got nil *int64")
   119  		}
   120  		return *vt != 0, nil
   121  	case uint8:
   122  		return vt != 0, nil
   123  	case *uint8:
   124  		if vt == nil {
   125  			return false, fmt.Errorf("got nil *uint8")
   126  		}
   127  		return *vt != 0, nil
   128  	case float64:
   129  		return vt != 0, nil
   130  	case *float64:
   131  		if vt == nil {
   132  			return false, fmt.Errorf("got nil *float64")
   133  		}
   134  		return *vt != 0, nil
   135  	case float32:
   136  		return vt != 0, nil
   137  	case *float32:
   138  		if vt == nil {
   139  			return false, fmt.Errorf("got nil *float32")
   140  		}
   141  		return *vt != 0, nil
   142  	case string:
   143  		r, err := strconv.ParseBool(vt)
   144  		if err != nil {
   145  			return false, err
   146  		}
   147  		return r, nil
   148  	case *string:
   149  		if vt == nil {
   150  			return false, fmt.Errorf("got nil *string")
   151  		}
   152  		r, err := strconv.ParseBool(*vt)
   153  		if err != nil {
   154  			return false, err
   155  		}
   156  		return r, nil
   157  	case int8:
   158  		return vt != 0, nil
   159  	case *int8:
   160  		if vt == nil {
   161  			return false, fmt.Errorf("got nil *int8")
   162  		}
   163  		return *vt != 0, nil
   164  	case int16:
   165  		return vt != 0, nil
   166  	case *int16:
   167  		if vt == nil {
   168  			return false, fmt.Errorf("got nil *int16")
   169  		}
   170  		return *vt != 0, nil
   171  	case uint:
   172  		return vt != 0, nil
   173  	case *uint:
   174  		if vt == nil {
   175  			return false, fmt.Errorf("got nil *uint")
   176  		}
   177  		return *vt != 0, nil
   178  	case uint16:
   179  		return vt != 0, nil
   180  	case *uint16:
   181  		if vt == nil {
   182  			return false, fmt.Errorf("got nil *uint16")
   183  		}
   184  		return *vt != 0, nil
   185  	case uint32:
   186  		return vt != 0, nil
   187  	case *uint32:
   188  		if vt == nil {
   189  			return false, fmt.Errorf("got nil *uint32")
   190  		}
   191  		return *vt != 0, nil
   192  	case uint64:
   193  		return vt != 0, nil
   194  	case *uint64:
   195  		if vt == nil {
   196  			return false, fmt.Errorf("got nil *uint64")
   197  		}
   198  		return *vt != 0, nil
   199  	case uintptr:
   200  		return vt != 0, nil
   201  	case *uintptr:
   202  		if vt == nil {
   203  			return false, fmt.Errorf("got nil *uintptr")
   204  		}
   205  		return *vt != 0, nil
   206  	}
   207  
   208  	// then fall back on reflection
   209  	if AnyIsNil(v) {
   210  		return false, fmt.Errorf("got nil value of type %T", v)
   211  	}
   212  	npv := NonPtrValue(reflect.ValueOf(v))
   213  	vk := npv.Kind()
   214  	switch {
   215  	case vk >= reflect.Int && vk <= reflect.Int64:
   216  		return (npv.Int() != 0), nil
   217  	case vk >= reflect.Uint && vk <= reflect.Uint64:
   218  		return (npv.Uint() != 0), nil
   219  	case vk == reflect.Bool:
   220  		return npv.Bool(), nil
   221  	case vk >= reflect.Float32 && vk <= reflect.Float64:
   222  		return (npv.Float() != 0.0), nil
   223  	case vk >= reflect.Complex64 && vk <= reflect.Complex128:
   224  		return (real(npv.Complex()) != 0.0), nil
   225  	case vk == reflect.String:
   226  		r, err := strconv.ParseBool(npv.String())
   227  		if err != nil {
   228  			return false, err
   229  		}
   230  		return r, nil
   231  	default:
   232  		return false, fmt.Errorf("got value %v of unsupported type %T", v, v)
   233  	}
   234  }
   235  
   236  // ToInt robustly converts to an int64 any basic elemental type
   237  // (including pointers to such) using a big type switch organized
   238  // for greatest efficiency, only falling back on reflection when all
   239  // else fails.
   240  //
   241  //gopy:interface=handle
   242  func ToInt(v any) (int64, error) {
   243  	switch vt := v.(type) {
   244  	case int:
   245  		return int64(vt), nil
   246  	case *int:
   247  		if vt == nil {
   248  			return 0, fmt.Errorf("got nil *int")
   249  		}
   250  		return int64(*vt), nil
   251  	case int32:
   252  		return int64(vt), nil
   253  	case *int32:
   254  		if vt == nil {
   255  			return 0, fmt.Errorf("got nil *int32")
   256  		}
   257  		return int64(*vt), nil
   258  	case int64:
   259  		return vt, nil
   260  	case *int64:
   261  		if vt == nil {
   262  			return 0, fmt.Errorf("got nil *int64")
   263  		}
   264  		return *vt, nil
   265  	case uint8:
   266  		return int64(vt), nil
   267  	case *uint8:
   268  		if vt == nil {
   269  			return 0, fmt.Errorf("got nil *uint8")
   270  		}
   271  		return int64(*vt), nil
   272  	case float64:
   273  		return int64(vt), nil
   274  	case *float64:
   275  		if vt == nil {
   276  			return 0, fmt.Errorf("got nil *float64")
   277  		}
   278  		return int64(*vt), nil
   279  	case float32:
   280  		return int64(vt), nil
   281  	case *float32:
   282  		if vt == nil {
   283  			return 0, fmt.Errorf("got nil *float32")
   284  		}
   285  		return int64(*vt), nil
   286  	case bool:
   287  		if vt {
   288  			return 1, nil
   289  		}
   290  		return 0, nil
   291  	case *bool:
   292  		if vt == nil {
   293  			return 0, fmt.Errorf("got nil *bool")
   294  		}
   295  		if *vt {
   296  			return 1, nil
   297  		}
   298  		return 0, nil
   299  	case string:
   300  		r, err := strconv.ParseInt(vt, 0, 64)
   301  		if err != nil {
   302  			return 0, err
   303  		}
   304  		return r, nil
   305  	case *string:
   306  		if vt == nil {
   307  			return 0, fmt.Errorf("got nil *string")
   308  		}
   309  		r, err := strconv.ParseInt(*vt, 0, 64)
   310  		if err != nil {
   311  			return 0, err
   312  		}
   313  		return r, nil
   314  	case enums.Enum:
   315  		return vt.Int64(), nil
   316  	case int8:
   317  		return int64(vt), nil
   318  	case *int8:
   319  		if vt == nil {
   320  			return 0, fmt.Errorf("got nil *int8")
   321  		}
   322  		return int64(*vt), nil
   323  	case int16:
   324  		return int64(vt), nil
   325  	case *int16:
   326  		if vt == nil {
   327  			return 0, fmt.Errorf("got nil *int16")
   328  		}
   329  		return int64(*vt), nil
   330  	case uint:
   331  		return int64(vt), nil
   332  	case *uint:
   333  		if vt == nil {
   334  			return 0, fmt.Errorf("got nil *uint")
   335  		}
   336  		return int64(*vt), nil
   337  	case uint16:
   338  		return int64(vt), nil
   339  	case *uint16:
   340  		if vt == nil {
   341  			return 0, fmt.Errorf("got nil *uint16")
   342  		}
   343  		return int64(*vt), nil
   344  	case uint32:
   345  		return int64(vt), nil
   346  	case *uint32:
   347  		if vt == nil {
   348  			return 0, fmt.Errorf("got nil *uint32")
   349  		}
   350  		return int64(*vt), nil
   351  	case uint64:
   352  		return int64(vt), nil
   353  	case *uint64:
   354  		if vt == nil {
   355  			return 0, fmt.Errorf("got nil *uint64")
   356  		}
   357  		return int64(*vt), nil
   358  	case uintptr:
   359  		return int64(vt), nil
   360  	case *uintptr:
   361  		if vt == nil {
   362  			return 0, fmt.Errorf("got nil *uintptr")
   363  		}
   364  		return int64(*vt), nil
   365  	}
   366  
   367  	// then fall back on reflection
   368  	if AnyIsNil(v) {
   369  		return 0, fmt.Errorf("got nil value of type %T", v)
   370  	}
   371  	npv := NonPtrValue(reflect.ValueOf(v))
   372  	vk := npv.Kind()
   373  	switch {
   374  	case vk >= reflect.Int && vk <= reflect.Int64:
   375  		return npv.Int(), nil
   376  	case vk >= reflect.Uint && vk <= reflect.Uint64:
   377  		return int64(npv.Uint()), nil
   378  	case vk == reflect.Bool:
   379  		if npv.Bool() {
   380  			return 1, nil
   381  		}
   382  		return 0, nil
   383  	case vk >= reflect.Float32 && vk <= reflect.Float64:
   384  		return int64(npv.Float()), nil
   385  	case vk >= reflect.Complex64 && vk <= reflect.Complex128:
   386  		return int64(real(npv.Complex())), nil
   387  	case vk == reflect.String:
   388  		r, err := strconv.ParseInt(npv.String(), 0, 64)
   389  		if err != nil {
   390  			return 0, err
   391  		}
   392  		return r, nil
   393  	default:
   394  		return 0, fmt.Errorf("got value %v of unsupported type %T", v, v)
   395  	}
   396  }
   397  
   398  // ToFloat robustly converts to a float64 any basic elemental type
   399  // (including pointers to such) using a big type switch organized for
   400  // greatest efficiency, only falling back on reflection when all else fails.
   401  //
   402  //gopy:interface=handle
   403  func ToFloat(v any) (float64, error) {
   404  	switch vt := v.(type) {
   405  	case float64:
   406  		return vt, nil
   407  	case *float64:
   408  		if vt == nil {
   409  			return 0, fmt.Errorf("got nil *float64")
   410  		}
   411  		return *vt, nil
   412  	case float32:
   413  		return float64(vt), nil
   414  	case *float32:
   415  		if vt == nil {
   416  			return 0, fmt.Errorf("got nil *float32")
   417  		}
   418  		return float64(*vt), nil
   419  	case int:
   420  		return float64(vt), nil
   421  	case *int:
   422  		if vt == nil {
   423  			return 0, fmt.Errorf("got nil *int")
   424  		}
   425  		return float64(*vt), nil
   426  	case int32:
   427  		return float64(vt), nil
   428  	case *int32:
   429  		if vt == nil {
   430  			return 0, fmt.Errorf("got nil *int32")
   431  		}
   432  		return float64(*vt), nil
   433  	case int64:
   434  		return float64(vt), nil
   435  	case *int64:
   436  		if vt == nil {
   437  			return 0, fmt.Errorf("got nil *int64")
   438  		}
   439  		return float64(*vt), nil
   440  	case uint8:
   441  		return float64(vt), nil
   442  	case *uint8:
   443  		if vt == nil {
   444  			return 0, fmt.Errorf("got nil *uint8")
   445  		}
   446  		return float64(*vt), nil
   447  	case bool:
   448  		if vt {
   449  			return 1, nil
   450  		}
   451  		return 0, nil
   452  	case *bool:
   453  		if vt == nil {
   454  			return 0, fmt.Errorf("got nil *bool")
   455  		}
   456  		if *vt {
   457  			return 1, nil
   458  		}
   459  		return 0, nil
   460  	case string:
   461  		r, err := strconv.ParseFloat(vt, 64)
   462  		if err != nil {
   463  			return 0.0, err
   464  		}
   465  		return r, nil
   466  	case *string:
   467  		if vt == nil {
   468  			return 0, fmt.Errorf("got nil *string")
   469  		}
   470  		r, err := strconv.ParseFloat(*vt, 64)
   471  		if err != nil {
   472  			return 0.0, err
   473  		}
   474  		return r, nil
   475  	case int8:
   476  		return float64(vt), nil
   477  	case *int8:
   478  		if vt == nil {
   479  			return 0, fmt.Errorf("got nil *int8")
   480  		}
   481  		return float64(*vt), nil
   482  	case int16:
   483  		return float64(vt), nil
   484  	case *int16:
   485  		if vt == nil {
   486  			return 0, fmt.Errorf("got nil *int16")
   487  		}
   488  		return float64(*vt), nil
   489  	case uint:
   490  		return float64(vt), nil
   491  	case *uint:
   492  		if vt == nil {
   493  			return 0, fmt.Errorf("got nil *uint")
   494  		}
   495  		return float64(*vt), nil
   496  	case uint16:
   497  		return float64(vt), nil
   498  	case *uint16:
   499  		if vt == nil {
   500  			return 0, fmt.Errorf("got nil *uint16")
   501  		}
   502  		return float64(*vt), nil
   503  	case uint32:
   504  		return float64(vt), nil
   505  	case *uint32:
   506  		if vt == nil {
   507  			return 0, fmt.Errorf("got nil *uint32")
   508  		}
   509  		return float64(*vt), nil
   510  	case uint64:
   511  		return float64(vt), nil
   512  	case *uint64:
   513  		if vt == nil {
   514  			return 0, fmt.Errorf("got nil *uint64")
   515  		}
   516  		return float64(*vt), nil
   517  	case uintptr:
   518  		return float64(vt), nil
   519  	case *uintptr:
   520  		if vt == nil {
   521  			return 0, fmt.Errorf("got nil *uintptr")
   522  		}
   523  		return float64(*vt), nil
   524  	}
   525  
   526  	// then fall back on reflection
   527  	if AnyIsNil(v) {
   528  		return 0, fmt.Errorf("got nil value of type %T", v)
   529  	}
   530  	npv := NonPtrValue(reflect.ValueOf(v))
   531  	vk := npv.Kind()
   532  	switch {
   533  	case vk >= reflect.Int && vk <= reflect.Int64:
   534  		return float64(npv.Int()), nil
   535  	case vk >= reflect.Uint && vk <= reflect.Uint64:
   536  		return float64(npv.Uint()), nil
   537  	case vk == reflect.Bool:
   538  		if npv.Bool() {
   539  			return 1, nil
   540  		}
   541  		return 0, nil
   542  	case vk >= reflect.Float32 && vk <= reflect.Float64:
   543  		return npv.Float(), nil
   544  	case vk >= reflect.Complex64 && vk <= reflect.Complex128:
   545  		return real(npv.Complex()), nil
   546  	case vk == reflect.String:
   547  		r, err := strconv.ParseFloat(npv.String(), 64)
   548  		if err != nil {
   549  			return 0, err
   550  		}
   551  		return r, nil
   552  	default:
   553  		return 0, fmt.Errorf("got value %v of unsupported type %T", v, v)
   554  	}
   555  }
   556  
   557  // ToFloat32 robustly converts to a float32 any basic elemental type
   558  // (including pointers to such) using a big type switch organized for
   559  // greatest efficiency, only falling back on reflection when all else fails.
   560  //
   561  //gopy:interface=handle
   562  func ToFloat32(v any) (float32, error) {
   563  	switch vt := v.(type) {
   564  	case float32:
   565  		return vt, nil
   566  	case *float32:
   567  		if vt == nil {
   568  			return 0, fmt.Errorf("got nil *float32")
   569  		}
   570  		return *vt, nil
   571  	case float64:
   572  		return float32(vt), nil
   573  	case *float64:
   574  		if vt == nil {
   575  			return 0, fmt.Errorf("got nil *float64")
   576  		}
   577  		return float32(*vt), nil
   578  	case int:
   579  		return float32(vt), nil
   580  	case *int:
   581  		if vt == nil {
   582  			return 0, fmt.Errorf("got nil *int")
   583  		}
   584  		return float32(*vt), nil
   585  	case int32:
   586  		return float32(vt), nil
   587  	case *int32:
   588  		if vt == nil {
   589  			return 0, fmt.Errorf("got nil *int32")
   590  		}
   591  		return float32(*vt), nil
   592  	case int64:
   593  		return float32(vt), nil
   594  	case *int64:
   595  		if vt == nil {
   596  			return 0, fmt.Errorf("got nil *int64")
   597  		}
   598  		return float32(*vt), nil
   599  	case uint8:
   600  		return float32(vt), nil
   601  	case *uint8:
   602  		if vt == nil {
   603  			return 0, fmt.Errorf("got nil *uint8")
   604  		}
   605  		return float32(*vt), nil
   606  	case bool:
   607  		if vt {
   608  			return 1, nil
   609  		}
   610  		return 0, nil
   611  	case *bool:
   612  		if vt == nil {
   613  			return 0, fmt.Errorf("got nil *bool")
   614  		}
   615  		if *vt {
   616  			return 1, nil
   617  		}
   618  		return 0, nil
   619  	case string:
   620  		r, err := strconv.ParseFloat(vt, 32)
   621  		if err != nil {
   622  			return 0, err
   623  		}
   624  		return float32(r), nil
   625  	case *string:
   626  		if vt == nil {
   627  			return 0, fmt.Errorf("got nil *string")
   628  		}
   629  		r, err := strconv.ParseFloat(*vt, 32)
   630  		if err != nil {
   631  			return 0, err
   632  		}
   633  		return float32(r), nil
   634  	case int8:
   635  		return float32(vt), nil
   636  	case *int8:
   637  		if vt == nil {
   638  			return 0, fmt.Errorf("got nil *int8")
   639  		}
   640  		return float32(*vt), nil
   641  	case int16:
   642  		return float32(vt), nil
   643  	case *int16:
   644  		if vt == nil {
   645  			return 0, fmt.Errorf("got nil *int8")
   646  		}
   647  		return float32(*vt), nil
   648  	case uint:
   649  		return float32(vt), nil
   650  	case *uint:
   651  		if vt == nil {
   652  			return 0, fmt.Errorf("got nil *uint")
   653  		}
   654  		return float32(*vt), nil
   655  	case uint16:
   656  		return float32(vt), nil
   657  	case *uint16:
   658  		if vt == nil {
   659  			return 0, fmt.Errorf("got nil *uint16")
   660  		}
   661  		return float32(*vt), nil
   662  	case uint32:
   663  		return float32(vt), nil
   664  	case *uint32:
   665  		if vt == nil {
   666  			return 0, fmt.Errorf("got nil *uint32")
   667  		}
   668  		return float32(*vt), nil
   669  	case uint64:
   670  		return float32(vt), nil
   671  	case *uint64:
   672  		if vt == nil {
   673  			return 0, fmt.Errorf("got nil *uint64")
   674  		}
   675  		return float32(*vt), nil
   676  	case uintptr:
   677  		return float32(vt), nil
   678  	case *uintptr:
   679  		if vt == nil {
   680  			return 0, fmt.Errorf("got nil *uintptr")
   681  		}
   682  		return float32(*vt), nil
   683  	}
   684  
   685  	// then fall back on reflection
   686  	if AnyIsNil(v) {
   687  		return 0, fmt.Errorf("got nil value of type %T", v)
   688  	}
   689  	npv := NonPtrValue(reflect.ValueOf(v))
   690  	vk := npv.Kind()
   691  	switch {
   692  	case vk >= reflect.Int && vk <= reflect.Int64:
   693  		return float32(npv.Int()), nil
   694  	case vk >= reflect.Uint && vk <= reflect.Uint64:
   695  		return float32(npv.Uint()), nil
   696  	case vk == reflect.Bool:
   697  		if npv.Bool() {
   698  			return 1, nil
   699  		}
   700  		return 0, nil
   701  	case vk >= reflect.Float32 && vk <= reflect.Float64:
   702  		return float32(npv.Float()), nil
   703  	case vk >= reflect.Complex64 && vk <= reflect.Complex128:
   704  		return float32(real(npv.Complex())), nil
   705  	case vk == reflect.String:
   706  		r, err := strconv.ParseFloat(npv.String(), 32)
   707  		if err != nil {
   708  			return 0, err
   709  		}
   710  		return float32(r), nil
   711  	default:
   712  		return 0, fmt.Errorf("got value %v of unsupported type %T", v, v)
   713  	}
   714  }
   715  
   716  // ToString robustly converts anything to a String
   717  // using a big type switch organized for greatest efficiency.
   718  // First checks for string or []byte and returns that immediately,
   719  // then checks for the Stringer interface as the preferred conversion
   720  // (e.g., for enums), and then falls back on strconv calls for numeric types.
   721  // If everything else fails, it uses Sprintf("%v") which always works,
   722  // so there is no need for an error return value.
   723  //   - returns "nil" for any nil pointers
   724  //   - byte is converted as string(byte) not the decimal representation
   725  //
   726  //gopy:interface=handle
   727  func ToString(v any) string {
   728  	nilstr := "nil"
   729  	switch vt := v.(type) {
   730  	case string:
   731  		return vt
   732  	case *string:
   733  		if vt == nil {
   734  			return nilstr
   735  		}
   736  		return *vt
   737  	case []byte:
   738  		return string(vt)
   739  	case *[]byte:
   740  		if vt == nil {
   741  			return nilstr
   742  		}
   743  		return string(*vt)
   744  	}
   745  
   746  	if stringer, ok := v.(fmt.Stringer); ok {
   747  		return stringer.String()
   748  	}
   749  
   750  	switch vt := v.(type) {
   751  	case bool:
   752  		if vt {
   753  			return "true"
   754  		}
   755  		return "false"
   756  	case *bool:
   757  		if vt == nil {
   758  			return nilstr
   759  		}
   760  		if *vt {
   761  			return "true"
   762  		}
   763  		return "false"
   764  	case int:
   765  		return strconv.FormatInt(int64(vt), 10)
   766  	case *int:
   767  		if vt == nil {
   768  			return nilstr
   769  		}
   770  		return strconv.FormatInt(int64(*vt), 10)
   771  	case int32:
   772  		return strconv.FormatInt(int64(vt), 10)
   773  	case *int32:
   774  		if vt == nil {
   775  			return nilstr
   776  		}
   777  		return strconv.FormatInt(int64(*vt), 10)
   778  	case int64:
   779  		return strconv.FormatInt(vt, 10)
   780  	case *int64:
   781  		if vt == nil {
   782  			return nilstr
   783  		}
   784  		return strconv.FormatInt(*vt, 10)
   785  	case uint8: // byte, converts as string char
   786  		return string(vt)
   787  	case *uint8:
   788  		if vt == nil {
   789  			return nilstr
   790  		}
   791  		return string(*vt)
   792  	case float64:
   793  		return strconv.FormatFloat(vt, 'G', -1, 64)
   794  	case *float64:
   795  		if vt == nil {
   796  			return nilstr
   797  		}
   798  		return strconv.FormatFloat(*vt, 'G', -1, 64)
   799  	case float32:
   800  		return strconv.FormatFloat(float64(vt), 'G', -1, 32)
   801  	case *float32:
   802  		if vt == nil {
   803  			return nilstr
   804  		}
   805  		return strconv.FormatFloat(float64(*vt), 'G', -1, 32)
   806  	case uintptr:
   807  		return fmt.Sprintf("%#x", uintptr(vt))
   808  	case *uintptr:
   809  		if vt == nil {
   810  			return nilstr
   811  		}
   812  		return fmt.Sprintf("%#x", uintptr(*vt))
   813  
   814  	case int8:
   815  		return strconv.FormatInt(int64(vt), 10)
   816  	case *int8:
   817  		if vt == nil {
   818  			return nilstr
   819  		}
   820  		return strconv.FormatInt(int64(*vt), 10)
   821  	case int16:
   822  		return strconv.FormatInt(int64(vt), 10)
   823  	case *int16:
   824  		if vt == nil {
   825  			return nilstr
   826  		}
   827  		return strconv.FormatInt(int64(*vt), 10)
   828  	case uint:
   829  		return strconv.FormatInt(int64(vt), 10)
   830  	case *uint:
   831  		if vt == nil {
   832  			return nilstr
   833  		}
   834  		return strconv.FormatInt(int64(*vt), 10)
   835  	case uint16:
   836  		return strconv.FormatInt(int64(vt), 10)
   837  	case *uint16:
   838  		if vt == nil {
   839  			return nilstr
   840  		}
   841  		return strconv.FormatInt(int64(*vt), 10)
   842  	case uint32:
   843  		return strconv.FormatInt(int64(vt), 10)
   844  	case *uint32:
   845  		if vt == nil {
   846  			return nilstr
   847  		}
   848  		return strconv.FormatInt(int64(*vt), 10)
   849  	case uint64:
   850  		return strconv.FormatInt(int64(vt), 10)
   851  	case *uint64:
   852  		if vt == nil {
   853  			return nilstr
   854  		}
   855  		return strconv.FormatInt(int64(*vt), 10)
   856  	case complex64:
   857  		return strconv.FormatFloat(float64(real(vt)), 'G', -1, 32) + "," + strconv.FormatFloat(float64(imag(vt)), 'G', -1, 32)
   858  	case *complex64:
   859  		if vt == nil {
   860  			return nilstr
   861  		}
   862  		return strconv.FormatFloat(float64(real(*vt)), 'G', -1, 32) + "," + strconv.FormatFloat(float64(imag(*vt)), 'G', -1, 32)
   863  	case complex128:
   864  		return strconv.FormatFloat(real(vt), 'G', -1, 64) + "," + strconv.FormatFloat(imag(vt), 'G', -1, 64)
   865  	case *complex128:
   866  		if vt == nil {
   867  			return nilstr
   868  		}
   869  		return strconv.FormatFloat(real(*vt), 'G', -1, 64) + "," + strconv.FormatFloat(imag(*vt), 'G', -1, 64)
   870  	}
   871  
   872  	// then fall back on reflection
   873  	if AnyIsNil(v) {
   874  		return nilstr
   875  	}
   876  	npv := NonPtrValue(reflect.ValueOf(v))
   877  	vk := npv.Kind()
   878  	switch {
   879  	case vk >= reflect.Int && vk <= reflect.Int64:
   880  		return strconv.FormatInt(npv.Int(), 10)
   881  	case vk >= reflect.Uint && vk <= reflect.Uint64:
   882  		return strconv.FormatUint(npv.Uint(), 10)
   883  	case vk == reflect.Bool:
   884  		return strconv.FormatBool(npv.Bool())
   885  	case vk >= reflect.Float32 && vk <= reflect.Float64:
   886  		return strconv.FormatFloat(npv.Float(), 'G', -1, 64)
   887  	case vk >= reflect.Complex64 && vk <= reflect.Complex128:
   888  		cv := npv.Complex()
   889  		rv := strconv.FormatFloat(real(cv), 'G', -1, 64) + "," + strconv.FormatFloat(imag(cv), 'G', -1, 64)
   890  		return rv
   891  	case vk == reflect.String:
   892  		return npv.String()
   893  	case vk == reflect.Slice:
   894  		eltyp := SliceElType(v)
   895  		if eltyp.Kind() == reflect.Uint8 { // []byte
   896  			return string(v.([]byte))
   897  		}
   898  		fallthrough
   899  	default:
   900  		return fmt.Sprintf("%v", v)
   901  	}
   902  }
   903  
   904  // ToStringPrec robustly converts anything to a String using given precision
   905  // for converting floating values -- using a value like 6 truncates the
   906  // nuisance random imprecision of actual floating point values due to the
   907  // fact that they are represented with binary bits.
   908  // Otherwise is identical to ToString for any other cases.
   909  //
   910  //gopy:interface=handle
   911  func ToStringPrec(v any, prec int) string {
   912  	nilstr := "nil"
   913  	switch vt := v.(type) {
   914  	case string:
   915  		return vt
   916  	case *string:
   917  		if vt == nil {
   918  			return nilstr
   919  		}
   920  		return *vt
   921  	case []byte:
   922  		return string(vt)
   923  	case *[]byte:
   924  		if vt == nil {
   925  			return nilstr
   926  		}
   927  		return string(*vt)
   928  	}
   929  
   930  	if stringer, ok := v.(fmt.Stringer); ok {
   931  		return stringer.String()
   932  	}
   933  
   934  	switch vt := v.(type) {
   935  	case float64:
   936  		return strconv.FormatFloat(vt, 'G', prec, 64)
   937  	case *float64:
   938  		if vt == nil {
   939  			return nilstr
   940  		}
   941  		return strconv.FormatFloat(*vt, 'G', prec, 64)
   942  	case float32:
   943  		return strconv.FormatFloat(float64(vt), 'G', prec, 32)
   944  	case *float32:
   945  		if vt == nil {
   946  			return nilstr
   947  		}
   948  		return strconv.FormatFloat(float64(*vt), 'G', prec, 32)
   949  	case complex64:
   950  		return strconv.FormatFloat(float64(real(vt)), 'G', prec, 32) + "," + strconv.FormatFloat(float64(imag(vt)), 'G', prec, 32)
   951  	case *complex64:
   952  		if vt == nil {
   953  			return nilstr
   954  		}
   955  		return strconv.FormatFloat(float64(real(*vt)), 'G', prec, 32) + "," + strconv.FormatFloat(float64(imag(*vt)), 'G', prec, 32)
   956  	case complex128:
   957  		return strconv.FormatFloat(real(vt), 'G', prec, 64) + "," + strconv.FormatFloat(imag(vt), 'G', prec, 64)
   958  	case *complex128:
   959  		if vt == nil {
   960  			return nilstr
   961  		}
   962  		return strconv.FormatFloat(real(*vt), 'G', prec, 64) + "," + strconv.FormatFloat(imag(*vt), 'G', prec, 64)
   963  	}
   964  	return ToString(v)
   965  }
   966  
   967  // SetRobust robustly sets the 'to' value from the 'from' value.
   968  // destination must be a pointer-to. Copies slices and maps robustly,
   969  // and can set a struct, slice or map from a JSON-formatted string from value.
   970  // Note that a map is _not_ reset prior to setting, whereas a slice length
   971  // is set to the source length and is thus equivalent to the source slice.
   972  //
   973  //gopy:interface=handle
   974  func SetRobust(to, frm any) error {
   975  	if sa, ok := to.(SetAnyer); ok {
   976  		err := sa.SetAny(frm)
   977  		if err != nil {
   978  			return err
   979  		}
   980  		return nil
   981  	}
   982  	if ss, ok := to.(SetStringer); ok {
   983  		if s, ok := frm.(string); ok {
   984  			err := ss.SetString(s)
   985  			if err != nil {
   986  				return err
   987  			}
   988  			return nil
   989  		}
   990  	}
   991  
   992  	if AnyIsNil(to) {
   993  		return fmt.Errorf("got nil destination value")
   994  	}
   995  	v := reflect.ValueOf(to)
   996  	vnp := NonPtrValue(v)
   997  	if !vnp.IsValid() {
   998  		return fmt.Errorf("got invalid destination value %v of type %T", to, to)
   999  	}
  1000  	typ := vnp.Type()
  1001  	vp := OnePtrValue(vnp)
  1002  	vk := vnp.Kind()
  1003  	if !vp.Elem().CanSet() {
  1004  		return fmt.Errorf("destination value cannot be set; it must be a variable or field, not a const or tmp or other value that cannot be set (value: %v of type %T)", vp, vp)
  1005  	}
  1006  
  1007  	if es, ok := to.(enums.EnumSetter); ok {
  1008  		if en, ok := frm.(enums.Enum); ok {
  1009  			es.SetInt64(en.Int64())
  1010  			return nil
  1011  		}
  1012  		if str, ok := frm.(string); ok {
  1013  			return es.SetString(str)
  1014  		}
  1015  		fm, err := ToInt(frm)
  1016  		if err != nil {
  1017  			return err
  1018  		}
  1019  		es.SetInt64(fm)
  1020  		return nil
  1021  	}
  1022  
  1023  	if bv, ok := to.(bools.BoolSetter); ok {
  1024  		fb, err := ToBool(frm)
  1025  		if err != nil {
  1026  			return err
  1027  		}
  1028  		bv.SetBool(fb)
  1029  		return nil
  1030  	}
  1031  
  1032  	switch {
  1033  	case vk >= reflect.Int && vk <= reflect.Int64:
  1034  		fm, err := ToInt(frm)
  1035  		if err != nil {
  1036  			return err
  1037  		}
  1038  		vp.Elem().Set(reflect.ValueOf(fm).Convert(typ))
  1039  		return nil
  1040  	case vk >= reflect.Uint && vk <= reflect.Uint64:
  1041  		fm, err := ToInt(frm)
  1042  		if err != nil {
  1043  			return err
  1044  		}
  1045  		vp.Elem().Set(reflect.ValueOf(fm).Convert(typ))
  1046  		return nil
  1047  	case vk == reflect.Bool:
  1048  		fm, err := ToBool(frm)
  1049  		if err != nil {
  1050  			return err
  1051  		}
  1052  		vp.Elem().Set(reflect.ValueOf(fm).Convert(typ))
  1053  		return nil
  1054  	case vk >= reflect.Float32 && vk <= reflect.Float64:
  1055  		fm, err := ToFloat(frm)
  1056  		if err != nil {
  1057  			return err
  1058  		}
  1059  		vp.Elem().Set(reflect.ValueOf(fm).Convert(typ))
  1060  		return nil
  1061  	case vk >= reflect.Complex64 && vk <= reflect.Complex128:
  1062  		// cv := v.Complex()
  1063  		// rv := strconv.FormatFloat(real(cv), 'G', -1, 64) + "," + strconv.FormatFloat(imag(cv), 'G', -1, 64)
  1064  		// return rv, nil
  1065  	case vk == reflect.String:
  1066  		fm := ToString(frm)
  1067  		vp.Elem().Set(reflect.ValueOf(fm).Convert(typ))
  1068  		return nil
  1069  	case vk == reflect.Struct:
  1070  		if NonPtrType(reflect.TypeOf(frm)).Kind() == reflect.String {
  1071  			err := json.Unmarshal([]byte(ToString(frm)), to) // todo: this is not working -- see what marshal says, etc
  1072  			if err != nil {
  1073  				marsh, _ := json.Marshal(to)
  1074  				return fmt.Errorf("error setting struct from string: %w (example format for string: %s)", err, string(marsh))
  1075  			}
  1076  			return nil
  1077  		}
  1078  	case vk == reflect.Slice:
  1079  		if NonPtrType(reflect.TypeOf(frm)).Kind() == reflect.String {
  1080  			err := json.Unmarshal([]byte(ToString(frm)), to)
  1081  			if err != nil {
  1082  				marsh, _ := json.Marshal(to)
  1083  				return fmt.Errorf("error setting slice from string: %w (example format for string: %s)", err, string(marsh))
  1084  			}
  1085  			return nil
  1086  		}
  1087  		return CopySliceRobust(to, frm)
  1088  	case vk == reflect.Map:
  1089  		if NonPtrType(reflect.TypeOf(frm)).Kind() == reflect.String {
  1090  			err := json.Unmarshal([]byte(ToString(frm)), to)
  1091  			if err != nil {
  1092  				marsh, _ := json.Marshal(to)
  1093  				return fmt.Errorf("error setting map from string: %w (example format for string: %s)", err, string(marsh))
  1094  			}
  1095  			return nil
  1096  		}
  1097  		return CopyMapRobust(to, frm)
  1098  	}
  1099  
  1100  	fv := NonPtrValue(reflect.ValueOf(frm))
  1101  	// Just set it if possible to assign
  1102  	if fv.Type().AssignableTo(typ) {
  1103  		vp.Elem().Set(fv)
  1104  		return nil
  1105  	}
  1106  	return fmt.Errorf("unable to set value %v of type %T from value %v of type %T (not a supported type pair and direct assigning is not possible)", to, to, frm, frm)
  1107  }