go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/projects/nodes/pkg/incrutil/cast_value.go (about)

     1  /*
     2  
     3  Copyright (c) 2023 - Present. Will Charczuk. All rights reserved.
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository.
     5  
     6  */
     7  
     8  package incrutil
     9  
    10  import (
    11  	"fmt"
    12  	"strconv"
    13  	"time"
    14  
    15  	"go.charczuk.com/projects/nodes/pkg/types"
    16  	"go.charczuk.com/sdk/errutil"
    17  	"go.charczuk.com/sdk/iter"
    18  )
    19  
    20  // CastValue casts a value as an output given an ambiguous input.
    21  func CastValue[Output any](input any) (output Output, err error) {
    22  	err = CastAny(&input, &output)
    23  	return
    24  }
    25  
    26  // CastAny casts an input to an output as non-generic references.
    27  func CastAny(input, output any) error {
    28  	if input == nil {
    29  		return nil
    30  	}
    31  
    32  	switch inputTyped := input.(type) {
    33  	case *any:
    34  		input = *inputTyped
    35  	default:
    36  	}
    37  
    38  	switch inputTyped := input.(type) {
    39  	case bool:
    40  		switch outputTyped := output.(type) {
    41  		case *any:
    42  			*outputTyped = inputTyped
    43  			return nil
    44  		case *bool:
    45  			*outputTyped = inputTyped
    46  			return nil
    47  		case *int64:
    48  			*outputTyped = b2i64(inputTyped)
    49  			return nil
    50  		case *string:
    51  			*outputTyped = fmt.Sprint(inputTyped)
    52  			return nil
    53  		case *float64:
    54  			*outputTyped = b2f64(inputTyped)
    55  			return nil
    56  		case *[]any:
    57  			*outputTyped = []any{inputTyped}
    58  			return nil
    59  		case *[]bool:
    60  			*outputTyped = []bool{inputTyped}
    61  			return nil
    62  		case *[]int64:
    63  			*outputTyped = []int64{b2i64(inputTyped)}
    64  			return nil
    65  		case *[]string:
    66  			*outputTyped = []string{fmt.Sprint(inputTyped)}
    67  			return nil
    68  		case *[]float64:
    69  			*outputTyped = []float64{b2f64(inputTyped)}
    70  			return nil
    71  		default:
    72  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
    73  		}
    74  	case int64:
    75  		switch outputTyped := output.(type) {
    76  		case *any:
    77  			*outputTyped = inputTyped
    78  			return nil
    79  		case *bool:
    80  			*outputTyped = inputTyped != 0
    81  			return nil
    82  		case *int64:
    83  			*outputTyped = inputTyped
    84  			return nil
    85  		case *string:
    86  			*outputTyped = strconv.FormatInt(inputTyped, 10)
    87  			return nil
    88  		case *float64:
    89  			*outputTyped = float64(inputTyped)
    90  			return nil
    91  		case *time.Time:
    92  			*outputTyped = time.UnixMilli(inputTyped)
    93  			return nil
    94  		case *time.Duration:
    95  			*outputTyped = time.Duration(inputTyped)
    96  			return nil
    97  		case *[]any:
    98  			*outputTyped = []any{inputTyped}
    99  			return nil
   100  		case *[]bool:
   101  			*outputTyped = []bool{inputTyped > 0}
   102  			return nil
   103  		case *[]int64:
   104  			*outputTyped = []int64{inputTyped}
   105  			return nil
   106  		case *[]string:
   107  			*outputTyped = []string{strconv.FormatInt(inputTyped, 10)}
   108  			return nil
   109  		case *[]float64:
   110  			*outputTyped = []float64{float64(inputTyped)}
   111  			return nil
   112  		case *[]time.Time:
   113  			*outputTyped = []time.Time{time.UnixMilli(inputTyped)}
   114  			return nil
   115  		case *[]time.Duration:
   116  			*outputTyped = []time.Duration{time.Duration(inputTyped)}
   117  			return nil
   118  		default:
   119  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   120  		}
   121  	case string:
   122  		switch outputTyped := output.(type) {
   123  		case *any:
   124  			*outputTyped = inputTyped
   125  			return nil
   126  		case *bool:
   127  			*outputTyped = inputTyped != ""
   128  			return nil
   129  		case *int64:
   130  			inputParsed, err := strconv.ParseInt(inputTyped, 10, 64)
   131  			if err != nil {
   132  				return err
   133  			}
   134  			*outputTyped = inputParsed
   135  			return nil
   136  		case *string:
   137  			*outputTyped = inputTyped
   138  			return nil
   139  		case *float64:
   140  			inputParsed, err := strconv.ParseFloat(inputTyped, 64)
   141  			if err != nil {
   142  				return err
   143  			}
   144  			*outputTyped = inputParsed
   145  			return nil
   146  		case *time.Time:
   147  			inputParsed, err := ParseTime(inputTyped)
   148  			if err != nil {
   149  				return err
   150  			}
   151  			*outputTyped = inputParsed
   152  			return nil
   153  		case *time.Duration:
   154  			inputParsed, err := parseDuration(inputTyped)
   155  			if err != nil {
   156  				return err
   157  			}
   158  			*outputTyped = inputParsed
   159  			return nil
   160  		case *[]any:
   161  			*outputTyped = []any{inputTyped}
   162  			return nil
   163  		case *[]bool:
   164  			*outputTyped = []bool{inputTyped != ""}
   165  			return nil
   166  		case *[]int64:
   167  			inputParsed, err := strconv.ParseInt(inputTyped, 10, 64)
   168  			if err != nil {
   169  				return err
   170  			}
   171  			*outputTyped = []int64{inputParsed}
   172  			return nil
   173  		case *[]string:
   174  			*outputTyped = []string{inputTyped}
   175  			return nil
   176  		case *[]float64:
   177  			inputParsed, err := strconv.ParseFloat(inputTyped, 64)
   178  			if err != nil {
   179  				return err
   180  			}
   181  			*outputTyped = []float64{inputParsed}
   182  			return nil
   183  		case *[]time.Time:
   184  			inputParsed, err := ParseTime(inputTyped)
   185  			if err != nil {
   186  				return err
   187  			}
   188  			*outputTyped = []time.Time{inputParsed}
   189  			return nil
   190  		case *[]time.Duration:
   191  			inputParsed, err := parseDuration(inputTyped)
   192  			if err != nil {
   193  				return err
   194  			}
   195  			*outputTyped = []time.Duration{inputParsed}
   196  			return nil
   197  		default:
   198  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   199  		}
   200  	case float64:
   201  		switch outputTyped := output.(type) {
   202  		case *any:
   203  			*outputTyped = inputTyped
   204  			return nil
   205  		case *bool:
   206  			*outputTyped = inputTyped != 0
   207  			return nil
   208  		case *int64:
   209  			*outputTyped = int64(inputTyped)
   210  			return nil
   211  		case *string:
   212  			*outputTyped = fmt.Sprint(inputTyped)
   213  			return nil
   214  		case *float64:
   215  			*outputTyped = inputTyped
   216  			return nil
   217  		case *time.Time:
   218  			*outputTyped = time.UnixMilli(int64(inputTyped))
   219  			return nil
   220  		case *time.Duration:
   221  			*outputTyped = time.Duration(int64(inputTyped))
   222  			return nil
   223  		case *[]any:
   224  			*outputTyped = []any{inputTyped}
   225  			return nil
   226  		case *[]bool:
   227  			*outputTyped = []bool{inputTyped != 0}
   228  			return nil
   229  		case *[]int64:
   230  			*outputTyped = []int64{int64(inputTyped)}
   231  			return nil
   232  		case *[]string:
   233  			*outputTyped = []string{fmt.Sprint(inputTyped)}
   234  			return nil
   235  		case *[]float64:
   236  			*outputTyped = []float64{inputTyped}
   237  			return nil
   238  		case *[]time.Time:
   239  			*outputTyped = []time.Time{time.UnixMilli(int64(inputTyped))}
   240  			return nil
   241  		case *[]time.Duration:
   242  			*outputTyped = []time.Duration{time.Duration(int64(inputTyped))}
   243  			return nil
   244  		default:
   245  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   246  		}
   247  	case time.Time:
   248  		switch outputTyped := output.(type) {
   249  		case *any:
   250  			*outputTyped = inputTyped
   251  			return nil
   252  		case *bool:
   253  			*outputTyped = !inputTyped.IsZero()
   254  			return nil
   255  		case *int64:
   256  			*outputTyped = inputTyped.UnixMilli()
   257  			return nil
   258  		case *string:
   259  			str := inputTyped.Format(time.RFC3339)
   260  			*outputTyped = str
   261  			return nil
   262  		case *float64:
   263  			*outputTyped = float64(inputTyped.Unix())
   264  			return nil
   265  		case *time.Time:
   266  			*outputTyped = inputTyped
   267  			return nil
   268  		case *[]any:
   269  			*outputTyped = []any{inputTyped}
   270  			return nil
   271  		case *[]bool:
   272  			*outputTyped = []bool{!inputTyped.IsZero()}
   273  			return nil
   274  		case *[]int64:
   275  			*outputTyped = []int64{inputTyped.UnixMilli()}
   276  			return nil
   277  		case *[]string:
   278  			*outputTyped = []string{inputTyped.Format(time.RFC3339)}
   279  			return nil
   280  		case *[]float64:
   281  			*outputTyped = []float64{float64(inputTyped.UnixMilli())}
   282  			return nil
   283  		case *[]time.Time:
   284  			*outputTyped = []time.Time{inputTyped}
   285  			return nil
   286  		default:
   287  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   288  		}
   289  	case time.Duration:
   290  		switch outputTyped := output.(type) {
   291  		case *any:
   292  			*outputTyped = inputTyped
   293  			return nil
   294  		case *bool:
   295  			*outputTyped = inputTyped != 0
   296  			return nil
   297  		case *int64:
   298  			*outputTyped = int64(inputTyped)
   299  			return nil
   300  		case *string:
   301  			*outputTyped = formatDuration(inputTyped)
   302  			return nil
   303  		case *float64:
   304  			*outputTyped = float64(inputTyped)
   305  			return nil
   306  		case *time.Duration:
   307  			*outputTyped = inputTyped
   308  			return nil
   309  		case *[]any:
   310  			*outputTyped = []any{inputTyped}
   311  			return nil
   312  		case *[]bool:
   313  			*outputTyped = []bool{inputTyped != 0}
   314  			return nil
   315  		case *[]int64:
   316  			*outputTyped = []int64{int64(inputTyped)}
   317  			return nil
   318  		case *[]string:
   319  			*outputTyped = []string{formatDuration(inputTyped)}
   320  			return nil
   321  		case *[]float64:
   322  			*outputTyped = []float64{float64(inputTyped)}
   323  			return nil
   324  		case *[]time.Duration:
   325  			*outputTyped = []time.Duration{inputTyped}
   326  			return nil
   327  		default:
   328  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   329  		}
   330  	case []bool:
   331  		switch outputTyped := output.(type) {
   332  		case *any:
   333  			*outputTyped = inputTyped
   334  			return nil
   335  		case *[]any:
   336  			*outputTyped = iter.Apply(inputTyped, func(v bool) any { return v })
   337  			return nil
   338  		case *[]bool:
   339  			*outputTyped = inputTyped
   340  			return nil
   341  		case *[]int64:
   342  			*outputTyped = iter.Apply(inputTyped, func(v bool) int64 { return b2i64(v) })
   343  			return nil
   344  		case *[]string:
   345  			*outputTyped = iter.Apply(inputTyped, func(v bool) string { return fmt.Sprint(v) })
   346  			return nil
   347  		case *[]float64:
   348  			*outputTyped = iter.Apply(inputTyped, func(v bool) float64 { return b2f64(v) })
   349  			return nil
   350  		default:
   351  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   352  		}
   353  	case []int64:
   354  		switch outputTyped := output.(type) {
   355  		case *any:
   356  			*outputTyped = inputTyped
   357  			return nil
   358  		case *[]any:
   359  			*outputTyped = iter.Apply(inputTyped, func(v int64) any { return v })
   360  			return nil
   361  		case *[]int64:
   362  			*outputTyped = inputTyped
   363  			return nil
   364  		case *[]string:
   365  			*outputTyped = iter.Apply(inputTyped, func(v int64) string { return strconv.FormatInt(v, 10) })
   366  			return nil
   367  		case *[]float64:
   368  			*outputTyped = iter.Apply(inputTyped, func(v int64) float64 { return float64(v) })
   369  			return nil
   370  		case *[]time.Time:
   371  			*outputTyped = iter.Apply(inputTyped, func(v int64) time.Time { return time.UnixMilli(v) })
   372  			return nil
   373  		case *[]time.Duration:
   374  			*outputTyped = iter.Apply(inputTyped, func(v int64) time.Duration { return time.Duration(v) })
   375  			return nil
   376  		default:
   377  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   378  		}
   379  	case []string:
   380  		switch outputTyped := output.(type) {
   381  		case *any:
   382  			*outputTyped = inputTyped
   383  			return nil
   384  		case *[]any:
   385  			*outputTyped = iter.Apply(inputTyped, func(v string) any { return v })
   386  			return nil
   387  		case *[]bool:
   388  			*outputTyped = iter.Apply(inputTyped, func(v string) bool { return v == "true" })
   389  			return nil
   390  		case *[]int64:
   391  			var err error
   392  			*outputTyped, err = iter.ApplyError(inputTyped, func(v string) (int64, error) {
   393  				return strconv.ParseInt(v, 10, 64)
   394  			})
   395  			return err
   396  		case *[]string:
   397  			*outputTyped = inputTyped
   398  			return nil
   399  		case *[]float64:
   400  			var err error
   401  			*outputTyped, err = iter.ApplyError(inputTyped, func(v string) (float64, error) {
   402  				return strconv.ParseFloat(v, 64)
   403  			})
   404  			return err
   405  		case *[]time.Time:
   406  			var err error
   407  			*outputTyped, err = iter.ApplyError(inputTyped, func(v string) (time.Time, error) {
   408  				parsed, parseError := ParseTime(v)
   409  				return parsed, parseError
   410  			})
   411  			return err
   412  		case *[]time.Duration:
   413  			var err error
   414  			*outputTyped, err = iter.ApplyError(inputTyped, func(v string) (time.Duration, error) {
   415  				parsed, parseErr := parseDuration(v)
   416  				return parsed, parseErr
   417  			})
   418  			return err
   419  		default:
   420  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   421  		}
   422  	case []float64:
   423  		switch outputTyped := output.(type) {
   424  		case *any:
   425  			*outputTyped = inputTyped
   426  			return nil
   427  		case *[]any:
   428  			*outputTyped = iter.Apply(inputTyped, func(v float64) any { return v })
   429  			return nil
   430  		case *[]bool:
   431  			*outputTyped = iter.Apply(inputTyped, func(v float64) bool { return v != 0 })
   432  			return nil
   433  		case *[]int64:
   434  			*outputTyped = iter.Apply(inputTyped, func(v float64) int64 {
   435  				return int64(v)
   436  			})
   437  			return nil
   438  		case *[]string:
   439  			*outputTyped = iter.Apply(inputTyped, func(v float64) string {
   440  				return fmt.Sprint(v)
   441  			})
   442  			return nil
   443  		case *[]float64:
   444  			*outputTyped = inputTyped
   445  			return nil
   446  		case *[]time.Time:
   447  			*outputTyped = iter.Apply(inputTyped, func(v float64) time.Time {
   448  				parsed := time.UnixMilli(int64(v))
   449  				return parsed
   450  			})
   451  			return nil
   452  		case *[]time.Duration:
   453  			*outputTyped = iter.Apply(inputTyped, func(v float64) time.Duration {
   454  				parsed := time.Duration(int64(v))
   455  				return parsed
   456  			})
   457  			return nil
   458  		default:
   459  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   460  		}
   461  	case []time.Time:
   462  		switch outputTyped := output.(type) {
   463  		case *any:
   464  			*outputTyped = inputTyped
   465  			return nil
   466  		case *[]any:
   467  			*outputTyped = iter.Apply(inputTyped, func(v time.Time) any { return v })
   468  			return nil
   469  		case *[]int64:
   470  			*outputTyped = iter.Apply(inputTyped, func(v time.Time) int64 {
   471  				return v.UnixMilli()
   472  			})
   473  			return nil
   474  		case *[]string:
   475  			*outputTyped = iter.Apply(inputTyped, func(v time.Time) string {
   476  				return v.Format(time.RFC3339)
   477  			})
   478  			return nil
   479  		case *[]float64:
   480  			*outputTyped = iter.Apply(inputTyped, func(v time.Time) float64 {
   481  				return float64(v.UnixMilli())
   482  			})
   483  			return nil
   484  		case *[]time.Time:
   485  			*outputTyped = inputTyped
   486  			return nil
   487  		default:
   488  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   489  		}
   490  	case []any:
   491  		switch outputTyped := output.(type) {
   492  		case *any:
   493  			*outputTyped = inputTyped
   494  			return nil
   495  		case *[]any:
   496  			*outputTyped = inputTyped
   497  			return nil
   498  		case *[]bool:
   499  			*outputTyped = iter.Apply(inputTyped, func(v any) bool {
   500  				var output bool
   501  				CastAny(v, &output)
   502  				return output
   503  			})
   504  			return nil
   505  		case *[]int64:
   506  			*outputTyped = iter.Apply(inputTyped, func(v any) int64 {
   507  				var output int64
   508  				CastAny(v, &output)
   509  				return output
   510  			})
   511  			return nil
   512  		case *[]string:
   513  			*outputTyped = iter.Apply(inputTyped, func(v any) string {
   514  				var output string
   515  				CastAny(v, &output)
   516  				return output
   517  			})
   518  			return nil
   519  		case *[]float64:
   520  			*outputTyped = iter.Apply(inputTyped, func(v any) float64 {
   521  				var output float64
   522  				CastAny(v, &output)
   523  				return output
   524  			})
   525  			return nil
   526  		case *[]time.Time:
   527  			*outputTyped = iter.Apply(inputTyped, func(v any) time.Time {
   528  				var output time.Time
   529  				CastAny(v, &output)
   530  				return output
   531  			})
   532  			return nil
   533  		case *[]time.Duration:
   534  			*outputTyped = iter.Apply(inputTyped, func(v any) time.Duration {
   535  				var output time.Duration
   536  				CastAny(v, &output)
   537  				return output
   538  			})
   539  			return nil
   540  		default:
   541  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   542  		}
   543  	case *types.Table:
   544  		switch outputTyped := output.(type) {
   545  		case *types.Table:
   546  			*outputTyped = *inputTyped
   547  			return nil
   548  		case **types.Table:
   549  			*outputTyped = inputTyped
   550  			return nil
   551  		case *any:
   552  			*outputTyped = inputTyped
   553  			return nil
   554  		default:
   555  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   556  		}
   557  	case types.SVG:
   558  		switch outputTyped := output.(type) {
   559  		case *types.SVG:
   560  			*outputTyped = inputTyped
   561  			return nil
   562  		case *any:
   563  			*outputTyped = inputTyped
   564  			return nil
   565  		default:
   566  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   567  		}
   568  	case map[string]any:
   569  		switch outputTyped := output.(type) {
   570  		case *any:
   571  			*outputTyped = inputTyped
   572  			return nil
   573  		default:
   574  			return fmt.Errorf("cast: invalid output type %T for input type %T", output, input)
   575  		}
   576  	default:
   577  		return fmt.Errorf("cast: invalid input type %T\n%v", input, errutil.GetStackTrace())
   578  	}
   579  }