github.com/goplus/yap@v0.8.1/test/match.go (about) 1 /* 2 * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package test 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "reflect" 23 "strconv" 24 "strings" 25 ) 26 27 const ( 28 GopPackage = true 29 ) 30 31 type basetype interface { 32 string | int | bool | float64 33 } 34 35 func toMapAny[T basetype](val map[string]T) map[string]any { 36 ret := make(map[string]any, len(val)) 37 for k, v := range val { 38 ret[k] = v 39 } 40 return ret 41 } 42 43 func tryToMapAny(val any) (ret map[string]any, ok bool) { 44 v := reflect.ValueOf(val) 45 return castMapAny(v) 46 } 47 48 func castMapAny(v reflect.Value) (ret map[string]any, ok bool) { 49 if v.Kind() != reflect.Map || v.Type().Key() != tyString { 50 return 51 } 52 ret, ok = make(map[string]any, v.Len()), true 53 for it := v.MapRange(); it.Next(); { 54 key := it.Key().String() 55 ret[key] = it.Value().Interface() 56 } 57 return 58 } 59 60 var ( 61 tyString = reflect.TypeOf("") 62 ) 63 64 // ----------------------------------------------------------------------------- 65 66 type baseelem interface { 67 string 68 } 69 70 type baseslice interface { 71 []string 72 } 73 74 type TySet[T baseelem] []T 75 type TyAnySet []any 76 77 func Set__0[T baseelem](vals ...T) TySet[T] { 78 return TySet[T](vals) 79 } 80 81 func Set__1[T []string](v *Var__3[T]) TySet[string] { 82 return TySet[string](v.Val()) 83 } 84 85 func Set__2(vals ...any) TyAnySet { 86 return TyAnySet(vals) 87 } 88 89 // ----------------------------------------------------------------------------- 90 91 type Case struct { 92 CaseT 93 } 94 95 func nameCtx(name []string) string { 96 if name != nil { 97 return " (" + strings.Join(name, ".") + ")" 98 } 99 return "" 100 } 101 102 const ( 103 Gopo_Gopt_Case_Match = "Gopt_Case_MatchTBase,Gopt_Case_MatchMap,Gopt_Case_MatchSlice,Gopt_Case_MatchBaseSlice,Gopt_Case_MatchSet,Gopt_Case_MatchAnySet,Gopt_Case_MatchAny" 104 ) 105 106 func Gopt_Case_MatchTBase[T basetype](t CaseT, expected, got T, name ...string) { 107 if expected != got { 108 t.Helper() 109 t.Fatalf("unmatched value%s - expected: %v, got: %v\n", nameCtx(name), expected, got) 110 } 111 } 112 113 func Gopt_Case_MatchMap(t CaseT, expected, got map[string]any, name ...string) { 114 t.Helper() 115 idx := len(name) 116 name = append(name, "") 117 for key, ev := range expected { 118 name[idx] = key 119 Gopt_Case_MatchAny(t, ev, got[key], name...) 120 } 121 } 122 123 func Gopt_Case_MatchSlice(t CaseT, expected, got []any, name ...string) { 124 t.Helper() 125 if len(expected) != len(got) { 126 t.Fatalf("unmatched slice%s length - expected: %d, got: %d\n", nameCtx(name), len(expected), len(got)) 127 } 128 idx := len(name) - 1 129 if idx < 0 { 130 idx, name = 0, []string{"$"} 131 } 132 for i, ev := range expected { 133 name[idx] = "[" + strconv.Itoa(i) + "]" 134 Gopt_Case_MatchAny(t, ev, got[i], name...) 135 } 136 } 137 138 func Gopt_Case_MatchBaseSlice[T baseelem](t CaseT, expected, got []T, name ...string) { 139 t.Helper() 140 if len(expected) != len(got) { 141 t.Fatalf("unmatched slice%s length - expected: %d, got: %d\n", nameCtx(name), len(expected), len(got)) 142 } 143 idx := len(name) - 1 144 if idx < 0 { 145 idx, name = 0, []string{"$"} 146 } 147 for i, ev := range expected { 148 name[idx] = "[" + strconv.Itoa(i) + "]" 149 Gopt_Case_MatchTBase(t, ev, got[i], name...) 150 } 151 } 152 153 func Gopt_Case_MatchSet[T baseelem](t CaseT, expected TySet[T], got []T, name ...string) { 154 if len(expected) != len(got) { 155 t.Fatalf("unmatched set%s length - expected: %d, got: %d\n", nameCtx(name), len(expected), len(got)) 156 } 157 for _, gv := range got { 158 if !hasElem(gv, expected) { 159 t.Fatalf("unmatched set%s: expected: %v, value %v doesn't exist in it\n", nameCtx(name), expected, gv) 160 } 161 } 162 } 163 164 func Gopt_Case_MatchAnySet(t CaseT, expected TyAnySet, got any, name ...string) { 165 if gv, ok := got.([]any); ok { 166 matchAnySet(t, expected, gv) 167 return 168 } 169 vgot := reflect.ValueOf(got) 170 if vgot.Kind() != reflect.Slice { 171 t.Fatalf("unmatched set%s: expected: %v, got a non slice value: %v\n", nameCtx(name), expected, got) 172 } 173 for i, n := 0, vgot.Len(); i < n; i++ { 174 gv := vgot.Index(i).Interface() 175 if !hasAnyElem(gv, expected) { 176 t.Fatalf("unmatched set%s: expected: %v, value %v doesn't exist in it\n", nameCtx(name), expected, gv) 177 } 178 } 179 } 180 181 func matchAnySet(t CaseT, expected TyAnySet, got []any, name ...string) { 182 if len(expected) != len(got) { 183 t.Fatalf("unmatched set%s length - expected: %d, got: %d\n", nameCtx(name), len(expected), len(got)) 184 } 185 for _, gv := range got { 186 if !hasAnyElem(gv, expected) { 187 t.Fatalf("unmatched set%s: expected: %v, value %v doesn't exist in it\n", nameCtx(name), expected, gv) 188 } 189 } 190 } 191 192 func hasElem[T baseelem](v T, expected []T) bool { 193 for _, ev := range expected { 194 if reflect.DeepEqual(v, ev) { 195 return true 196 } 197 } 198 return false 199 } 200 201 func hasAnyElem(v any, expected []any) bool { 202 for _, ev := range expected { 203 if v == ev { 204 return true 205 } 206 } 207 return false 208 } 209 210 func Gopt_Case_MatchAny(t CaseT, expected, got any, name ...string) { 211 t.Helper() 212 retry: 213 switch ev := expected.(type) { 214 case string: 215 switch gv := got.(type) { 216 case string: 217 Gopt_Case_MatchTBase(t, ev, gv, name...) 218 return 219 case *Var__0[string]: 220 Gopt_Case_MatchTBase(t, ev, gv.Val(), name...) 221 return 222 } 223 case int: 224 switch gv := got.(type) { 225 case int: 226 Gopt_Case_MatchTBase(t, ev, gv, name...) 227 return 228 case *Var__0[int]: 229 Gopt_Case_MatchTBase(t, ev, gv.Val(), name...) 230 return 231 } 232 case bool: 233 switch gv := got.(type) { 234 case bool: 235 Gopt_Case_MatchTBase(t, ev, gv, name...) 236 return 237 case *Var__0[bool]: 238 Gopt_Case_MatchTBase(t, ev, gv.Val(), name...) 239 return 240 } 241 case float64: 242 switch gv := got.(type) { 243 case float64: 244 Gopt_Case_MatchTBase(t, ev, gv, name...) 245 return 246 case *Var__0[float64]: 247 Gopt_Case_MatchTBase(t, ev, gv.Val(), name...) 248 return 249 } 250 case map[string]any: 251 switch gv := got.(type) { 252 case map[string]any: 253 Gopt_Case_MatchMap(t, ev, gv, name...) 254 return 255 case *Var__1[map[string]any]: 256 Gopt_Case_MatchMap(t, ev, gv.Val(), name...) 257 return 258 default: 259 if gv, ok := tryToMapAny(got); ok { 260 Gopt_Case_MatchMap(t, ev, gv, name...) 261 return 262 } 263 } 264 case []any: 265 switch gv := got.(type) { 266 case []any: 267 Gopt_Case_MatchSlice(t, ev, gv, name...) 268 return 269 case *Var__2[[]any]: 270 Gopt_Case_MatchSlice(t, ev, gv.Val(), name...) 271 return 272 } 273 case []string: 274 switch gv := got.(type) { 275 case []string: 276 Gopt_Case_MatchBaseSlice(t, ev, gv, name...) 277 return 278 case *Var__3[[]string]: 279 Gopt_Case_MatchBaseSlice(t, ev, gv.Val(), name...) 280 return 281 } 282 case TySet[string]: 283 switch gv := got.(type) { 284 case []string: 285 Gopt_Case_MatchSet(t, ev, gv, name...) 286 return 287 case *Var__3[[]string]: 288 Gopt_Case_MatchSet(t, ev, gv.Val(), name...) 289 return 290 } 291 case TyAnySet: 292 switch gv := got.(type) { 293 case *Var__2[[]any]: 294 Gopt_Case_MatchAnySet(t, ev, gv.Val(), name...) 295 return 296 default: 297 Gopt_Case_MatchAnySet(t, ev, gv, name...) 298 return 299 } 300 case *Var__0[string]: 301 switch gv := got.(type) { 302 case string: 303 ev.Match(t, gv, name...) 304 return 305 case *Var__0[string]: 306 ev.Match(t, gv.Val(), name...) 307 return 308 } 309 case *Var__0[int]: 310 switch gv := got.(type) { 311 case int: 312 ev.Match(t, gv, name...) 313 return 314 case *Var__0[int]: 315 ev.Match(t, gv.Val(), name...) 316 return 317 } 318 case *Var__0[bool]: 319 switch gv := got.(type) { 320 case bool: 321 ev.Match(t, gv, name...) 322 return 323 case *Var__0[bool]: 324 ev.Match(t, gv.Val(), name...) 325 return 326 } 327 case *Var__0[float64]: 328 switch gv := got.(type) { 329 case float64: 330 ev.Match(t, gv, name...) 331 return 332 case *Var__0[float64]: 333 ev.Match(t, gv.Val(), name...) 334 return 335 } 336 case *Var__1[map[string]any]: 337 switch gv := got.(type) { 338 case map[string]any: 339 ev.Match(t, gv, name...) 340 return 341 case *Var__1[map[string]any]: 342 ev.Match(t, gv.Val(), name...) 343 return 344 } 345 case *Var__2[[]any]: 346 switch gv := got.(type) { 347 case []any: 348 ev.Match(t, gv, name...) 349 return 350 case *Var__2[[]any]: 351 ev.Match(t, gv.Val(), name...) 352 return 353 } 354 case *Var__3[[]string]: 355 switch gv := got.(type) { 356 case []string: 357 ev.Match(t, gv, name...) 358 return 359 case *Var__3[[]string]: 360 ev.Match(t, gv.Val(), name...) 361 return 362 } 363 364 // fallback types: 365 case map[string]string: 366 expected = toMapAny(ev) 367 goto retry 368 case map[string]int: 369 expected = toMapAny(ev) 370 goto retry 371 case map[string]bool: 372 expected = toMapAny(ev) 373 goto retry 374 case map[string]float64: 375 expected = toMapAny(ev) 376 goto retry 377 378 // other types: 379 default: 380 if v, ok := tryToMapAny(expected); ok { 381 expected = v 382 goto retry 383 } 384 if reflect.DeepEqual(expected, got) { 385 return 386 } 387 } 388 t.Fatalf( 389 "unmatched%s - expected: %v (%T), got: %v (%T)\n", 390 nameCtx(name), expected, expected, got, got, 391 ) 392 } 393 394 // ----------------------------------------------------------------------------- 395 396 type Var__0[T basetype] struct { 397 val T 398 valid bool 399 } 400 401 func (p *Var__0[T]) check() { 402 if !p.valid { 403 Fatal("read variable value before initialization") 404 } 405 } 406 407 func (p *Var__0[T]) Valid() bool { 408 return p.valid 409 } 410 411 func (p *Var__0[T]) String() string { 412 p.check() 413 return fmt.Sprint(p.val) // TODO: optimization 414 } 415 416 func (p *Var__0[T]) Val() T { 417 p.check() 418 return p.val 419 } 420 421 func (p *Var__0[T]) MarshalJSON() ([]byte, error) { 422 p.check() 423 return json.Marshal(p.val) 424 } 425 426 func (p *Var__0[T]) UnmarshalJSON(data []byte) error { 427 p.valid = true 428 return json.Unmarshal(data, &p.val) 429 } 430 431 func (p *Var__0[T]) Equal(t CaseT, v T) bool { 432 p.check() 433 return p.val == v 434 } 435 436 func (p *Var__0[T]) Match(t CaseT, v T, name ...string) { 437 if !p.valid { 438 p.val, p.valid = v, true 439 return 440 } 441 t.Helper() 442 Gopt_Case_MatchTBase(t, p.val, v, name...) 443 } 444 445 // ----------------------------------------------------------------------------- 446 447 type Var__1[T map[string]any] struct { 448 val T 449 } 450 451 func (p *Var__1[T]) check() { 452 if p.val == nil { 453 Fatal("read variable value before initialization") 454 } 455 } 456 457 func (p *Var__1[T]) Valid() bool { 458 return p.val != nil 459 } 460 461 func (p *Var__1[T]) Val() T { 462 p.check() 463 return p.val 464 } 465 466 func (p *Var__1[T]) MarshalJSON() ([]byte, error) { 467 p.check() 468 return json.Marshal(p.val) 469 } 470 471 func (p *Var__1[T]) UnmarshalJSON(data []byte) error { 472 return json.Unmarshal(data, &p.val) 473 } 474 475 func (p *Var__1[T]) Match(t CaseT, v T, name ...string) { 476 if p.val == nil { 477 p.val = v 478 return 479 } 480 t.Helper() 481 Gopt_Case_MatchMap(t, p.val, v, name...) 482 } 483 484 // ----------------------------------------------------------------------------- 485 486 type Var__2[T []any] struct { 487 val T 488 valid bool 489 } 490 491 func (p *Var__2[T]) check() { 492 if !p.valid { 493 Fatal("read variable value before initialization") 494 } 495 } 496 497 func (p *Var__2[T]) Valid() bool { 498 return p.valid 499 } 500 501 func (p *Var__2[T]) Val() T { 502 p.check() 503 return p.val 504 } 505 506 func (p *Var__2[T]) MarshalJSON() ([]byte, error) { 507 p.check() 508 return json.Marshal(p.val) 509 } 510 511 func (p *Var__2[T]) UnmarshalJSON(data []byte) error { 512 p.valid = true 513 return json.Unmarshal(data, &p.val) 514 } 515 516 func (p *Var__2[T]) Match(t CaseT, v T, name ...string) { 517 if p.val == nil { 518 p.val, p.valid = v, true 519 return 520 } 521 t.Helper() 522 Gopt_Case_MatchSlice(t, p.val, v, name...) 523 } 524 525 // ----------------------------------------------------------------------------- 526 527 type Var__3[T baseslice] struct { 528 val T 529 valid bool 530 } 531 532 func (p *Var__3[T]) check() { 533 if !p.valid { 534 Fatal("read variable value before initialization") 535 } 536 } 537 538 func (p *Var__3[T]) Valid() bool { 539 return p.valid 540 } 541 542 func (p *Var__3[T]) Val() T { 543 p.check() 544 return p.val 545 } 546 547 func (p *Var__3[T]) MarshalJSON() ([]byte, error) { 548 p.check() 549 return json.Marshal(p.val) 550 } 551 552 func (p *Var__3[T]) UnmarshalJSON(data []byte) error { 553 p.valid = true 554 return json.Unmarshal(data, &p.val) 555 } 556 557 func (p *Var__3[T]) Match(t CaseT, v T, name ...string) { 558 if p.val == nil { 559 p.val, p.valid = v, true 560 return 561 } 562 t.Helper() 563 Gopt_Case_MatchBaseSlice(t, p.val, v, name...) 564 } 565 566 // ----------------------------------------------------------------------------- 567 568 func Gopx_Var_Cast__0[T basetype]() *Var__0[T] { 569 return new(Var__0[T]) 570 } 571 572 func Gopx_Var_Cast__1[T map[string]any]() *Var__1[T] { 573 return new(Var__1[T]) 574 } 575 576 func Gopx_Var_Cast__2[T []any]() *Var__2[T] { 577 return new(Var__2[T]) 578 } 579 580 func Gopx_Var_Cast__3[T []string]() *Var__3[T] { 581 return new(Var__3[T]) 582 } 583 584 // -----------------------------------------------------------------------------