github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/types/cast.go (about)

     1  package types
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"regexp"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/lmorg/murex/app"
    11  	"github.com/lmorg/murex/utils/json"
    12  )
    13  
    14  const (
    15  	// ErrDataTypeDefaulted is returned if the murex data type is unknown
    16  	ErrDataTypeDefaulted = "unexpected or unknown murex data type"
    17  
    18  	// ErrUnexpectedGoType is returned if the Go data type is unhandled
    19  	ErrUnexpectedGoType = "unexpected Go type"
    20  
    21  	// ErrCannotConvertGoType is returned if the Go data type cannot be converted
    22  	// to the murex data type (eg there is no numeric data in a string of characters)
    23  	ErrCannotConvertGoType = "cannot convert Go type into murex data type (eg no numeric data in a string)"
    24  )
    25  
    26  var (
    27  	rxFirstInt   *regexp.Regexp = regexp.MustCompile(`([0-9]+)`)
    28  	rxFirstFloat *regexp.Regexp = regexp.MustCompile(`([0-9]+)(\.[0-9]+|)`)
    29  )
    30  
    31  // ConvertGoType converts a Go lang variable into a murex variable
    32  func ConvertGoType(v interface{}, dataType string) (interface{}, error) {
    33  	//debug.Log("ConvertGoType:", fmt.Sprintf("%t %s %v", v, dataType, v))
    34  
    35  	switch t := v.(type) {
    36  	case nil:
    37  		return goNilRecast(dataType)
    38  
    39  	case int:
    40  		return goIntegerRecast(t, dataType)
    41  
    42  	//case float32:
    43  	//	return goFloatRecast(t, dataType)
    44  
    45  	case float64:
    46  		return goFloatRecast(t, dataType)
    47  
    48  	case bool:
    49  		return goBooleanRecast(t, dataType)
    50  
    51  	case string:
    52  		return goStringRecast(t, dataType)
    53  
    54  	case []byte:
    55  		return goStringRecast(string(t), dataType)
    56  
    57  	case []rune:
    58  		return goStringRecast(string(t), dataType)
    59  
    60  	default:
    61  		return goDefaultRecast(v, dataType)
    62  	}
    63  
    64  	//return nil, errors.New(ErrUnexpectedGoType)
    65  }
    66  
    67  func goNilRecast(dataType string) (interface{}, error) {
    68  	switch dataType {
    69  	case Integer:
    70  		return 0, nil
    71  
    72  	case Float, Number:
    73  		return float64(0), nil
    74  
    75  	case Boolean:
    76  		return false, nil
    77  
    78  	case CodeBlock, Json, JsonLines:
    79  		return "{}", nil
    80  
    81  	default:
    82  		return "", nil
    83  	}
    84  }
    85  
    86  func goIntegerRecast(v int, dataType string) (interface{}, error) {
    87  	switch dataType {
    88  	case Generic:
    89  		return v, nil
    90  
    91  	case Integer:
    92  		return v, nil
    93  
    94  	case Float, Number:
    95  		return float64(v), nil
    96  
    97  	case Boolean:
    98  		if v == 0 {
    99  			return false, nil
   100  		}
   101  		return true, nil
   102  
   103  	//case CodeBlock:
   104  	//	return fmt.Sprintf("out: %d", v), nil
   105  
   106  	case String:
   107  		return strconv.Itoa(v), nil
   108  
   109  	//case Json, JsonLines:
   110  	//	return fmt.Sprintf(`{ "Value": %d }`, v), nil
   111  
   112  	case Null:
   113  		return "", nil
   114  
   115  	default:
   116  		//	return nil, errors.New(ErrDataTypeDefaulted)
   117  		return strconv.Itoa(v), nil
   118  	}
   119  }
   120  
   121  func goFloatRecast(v float64, dataType string) (interface{}, error) {
   122  	switch dataType {
   123  	case Generic:
   124  		return v, nil
   125  
   126  	case Integer:
   127  		return int(v), nil
   128  
   129  	case Float, Number:
   130  		return v, nil
   131  
   132  	case Boolean:
   133  		if v == 0 {
   134  			return false, nil
   135  		}
   136  		return true, nil
   137  
   138  	//case CodeBlock:
   139  	//	return "out: " + FloatToString(v), nil
   140  
   141  	case String:
   142  		return FloatToString(v), nil
   143  
   144  	//case Json, JsonLines:
   145  	//	return fmt.Sprintf(`{ "Value": %s }`, FloatToString(v)), nil
   146  
   147  	case Null:
   148  		return "", nil
   149  
   150  	default:
   151  		//return nil, errors.New(ErrDataTypeDefaulted)
   152  		return FloatToString(v), nil
   153  	}
   154  }
   155  
   156  func goBooleanRecast(v bool, dataType string) (interface{}, error) {
   157  	switch dataType {
   158  	case Generic:
   159  		return v, nil
   160  
   161  	case Integer:
   162  		if v {
   163  			return 1, nil
   164  		}
   165  		return 0, nil
   166  
   167  	case Float, Number:
   168  		if v {
   169  			return float64(1), nil
   170  		}
   171  		return float64(0), nil
   172  
   173  	case Boolean:
   174  		return v, nil
   175  
   176  	case CodeBlock:
   177  		if v {
   178  			return "true", nil
   179  		}
   180  		return "false", nil
   181  
   182  	case String:
   183  		if v {
   184  			return string(TrueByte), nil
   185  		}
   186  		return string(FalseByte), nil
   187  
   188  	/*case Json, JsonLines:
   189  	if v {
   190  		return `{ "Value": true }`, nil
   191  	}
   192  	return `{ "Value": false }`, nil*/
   193  
   194  	case Null:
   195  		return "", nil
   196  
   197  	default:
   198  		//return nil, errors.New(ErrDataTypeDefaulted)
   199  		if v {
   200  			return string(TrueByte), nil
   201  		}
   202  		return string(FalseByte), nil
   203  	}
   204  }
   205  
   206  func goStringRecast(v string, dataType string) (interface{}, error) {
   207  	switch dataType {
   208  	case Generic:
   209  		return v, nil
   210  
   211  	case Integer:
   212  		if v == "" {
   213  			v = "0"
   214  		}
   215  		//return strconv.Atoi(strings.TrimSpace(v))
   216  		f, err := strconv.ParseFloat(v, 64)
   217  		if err != nil {
   218  			return int(f), fmt.Errorf("cannot convert '%s' to an integer: %s", v, err.Error())
   219  		}
   220  		return int(f), nil
   221  
   222  	case Float, Number:
   223  		if v == "" {
   224  			v = "0"
   225  		}
   226  
   227  		f, err := strconv.ParseFloat(v, 64)
   228  		if err != nil {
   229  			return f, fmt.Errorf("cannot convert '%s' to a floating point number: %s", v, err.Error())
   230  		}
   231  		return f, nil
   232  
   233  	case Boolean:
   234  		return IsTrue([]byte(v), 0), nil
   235  
   236  	case CodeBlock:
   237  		if len(v) > 1 && v[0] == '{' && v[len(v)-1] == '}' {
   238  			return v[1 : len(v)-1], nil
   239  		}
   240  		//errors.New("Not a valid code block: `" + v.(string) + "`")
   241  		return "out: '" + v + "'", nil
   242  
   243  	case String:
   244  		return v, nil
   245  
   246  	case Null:
   247  		return "", nil
   248  
   249  	default:
   250  		//return nil, errors.New(ErrDataTypeDefaulted)
   251  		return v, nil
   252  	}
   253  }
   254  
   255  func goDefaultRecast(v interface{}, dataType string) (interface{}, error) {
   256  	switch dataType {
   257  	case Generic:
   258  		switch t := v.(type) {
   259  		case []string:
   260  			return strings.Join(t, " "), nil
   261  		case []interface{}:
   262  			a := make([]string, len(t))
   263  			for i := range t {
   264  				a[i] = fmt.Sprint(t[i])
   265  			}
   266  			return strings.Join(a, " "), nil
   267  		case []int:
   268  			a := make([]string, len(t))
   269  			for i := range t {
   270  				a[i] = strconv.Itoa(t[i])
   271  			}
   272  			return strings.Join(a, " "), nil
   273  		case []float64:
   274  			a := make([]string, len(t))
   275  			for i := range t {
   276  				a[i] = FloatToString(t[i])
   277  			}
   278  			return strings.Join(a, " "), nil
   279  		case []bool:
   280  			a := make([]string, len(t))
   281  			for i := range t {
   282  				if t[i] {
   283  					a[i] = TrueString
   284  				} else {
   285  					a[i] = FalseString
   286  				}
   287  			}
   288  			return strings.Join(a, " "), nil
   289  		default:
   290  			//return fmt.Sprintf("%t", v), nil // debugging
   291  			b, err := json.Marshal(v, false)
   292  			return string(b), err
   293  		}
   294  
   295  	case String:
   296  		switch t := v.(type) {
   297  		case []string:
   298  			return strings.Join(t, "\n"), nil
   299  		case []interface{}:
   300  			a := make([]string, len(t))
   301  			for i := range t {
   302  				a[i] = fmt.Sprint(t[i])
   303  			}
   304  			return strings.Join(a, "\n"), nil
   305  		case []int:
   306  			a := make([]string, len(t))
   307  			for i := range t {
   308  				a[i] = strconv.Itoa(t[i])
   309  			}
   310  			return strings.Join(a, "\n"), nil
   311  		case []float64:
   312  			a := make([]string, len(t))
   313  			for i := range t {
   314  				a[i] = FloatToString(t[i])
   315  			}
   316  			return strings.Join(a, "\n"), nil
   317  		case []bool:
   318  			a := make([]string, len(t))
   319  			for i := range t {
   320  				if t[i] {
   321  					a[i] = TrueString
   322  				} else {
   323  					a[i] = FalseString
   324  				}
   325  			}
   326  			return strings.Join(a, "\n"), nil
   327  		default:
   328  			//return fmt.Sprintf("%t", v), nil // debugging
   329  			b, err := json.Marshal(v, false)
   330  			return string(b), err
   331  		}
   332  
   333  	case Integer:
   334  		s := fmt.Sprint(v)
   335  		i := rxFirstInt.FindStringSubmatch(s)
   336  		if len(i) > 0 {
   337  			return i[0], nil
   338  		}
   339  		return 0, errors.New(ErrCannotConvertGoType)
   340  
   341  	case Float, Number:
   342  		s := fmt.Sprint(v)
   343  		f := rxFirstFloat.FindStringSubmatch(s)
   344  		if len(f) > 0 {
   345  			return f[0], nil
   346  		}
   347  		return 0, errors.New(ErrCannotConvertGoType)
   348  
   349  	case Boolean:
   350  		s := fmt.Sprint(v)
   351  		if s == "{}" || s == "[]" || s == "[[]]" || s == "" {
   352  			return false, nil
   353  		}
   354  		if !IsTrue([]byte(s), 0) {
   355  			return false, nil
   356  		}
   357  		return true, nil
   358  
   359  	case Null:
   360  		return nil, nil
   361  
   362  	case Json, JsonLines:
   363  		b, err := json.Marshal(v, false)
   364  		return string(b), err
   365  
   366  	default:
   367  		return nil, fmt.Errorf(ErrUnexpectedGoType+": Go '%T', %s '%s'", v, app.Name, dataType)
   368  	}
   369  }
   370  
   371  // FloatToString convert a Float64 (what murex numbers are stored as) into a string. Typically for outputting to Stdout/Stderr.
   372  func FloatToString(f float64) string {
   373  	return strconv.FormatFloat(f, 'f', -1, 64)
   374  }