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 }