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  // -----------------------------------------------------------------------------