case.mx raw

   1  package cm
   2  
   3  // CaseUnmarshaler returns an function that can unmarshal text into
   4  // [variant] or [enum] case T.
   5  //
   6  // [enum]: https://component-model.bytecodealliance.org/design/wit.html#enums
   7  // [variant]: https://component-model.bytecodealliance.org/design/wit.html#variants
   8  func CaseUnmarshaler[T ~uint8 | ~uint16 | ~uint32](cases [][]byte) func(v *T, text []byte) error {
   9  	if len(cases) <= linearScanThreshold {
  10  		return func(v *T, text []byte) error {
  11  			if len(text) == 0 {
  12  				return &emptyTextError{}
  13  			}
  14  			s := []byte(text)
  15  			for i := 0; i < len(cases); i++ {
  16  				if cases[i] == s {
  17  					*v = T(i)
  18  					return nil
  19  				}
  20  			}
  21  			return &noMatchingCaseError{}
  22  		}
  23  	}
  24  
  25  	m := map[string]T{}
  26  	for i, v := range cases {
  27  		m[v] = T(i)
  28  	}
  29  
  30  	return func(v *T, text []byte) error {
  31  		if len(text) == 0 {
  32  			return &emptyTextError{}
  33  		}
  34  		c, ok := m[[]byte(text)]
  35  		if !ok {
  36  			return &noMatchingCaseError{}
  37  		}
  38  		*v = c
  39  		return nil
  40  	}
  41  }
  42  
  43  const linearScanThreshold = 16
  44  
  45  type emptyTextError struct{}
  46  
  47  func (*emptyTextError) Error() string { return "empty text" }
  48  
  49  type noMatchingCaseError struct{}
  50  
  51  func (*noMatchingCaseError) Error() string { return "no matching case" }
  52