go-ml.dev/pkg/base@v0.0.0-20200610162856-60c38abac71b/tables/enum.go (about)

     1  package tables
     2  
     3  import (
     4  	"fmt"
     5  	"go-ml.dev/pkg/base/fu"
     6  	"golang.org/x/xerrors"
     7  	"reflect"
     8  	"sync"
     9  )
    10  
    11  var enumType = reflect.TypeOf(Enum{})
    12  
    13  /*
    14  Enum encapsulate enumeration abstraction in relation to tables
    15  */
    16  type Enum struct {
    17  	Text  string
    18  	Value int
    19  }
    20  
    21  // Text return enum string representation
    22  func (e Enum) String() string {
    23  	return e.Text
    24  }
    25  
    26  // Enum defines enumerated meta-column with the Enum tipe
    27  func (e Enumset) Enum() Meta {
    28  	return Enumerator{e, &sync.Mutex{}, len(e) != 0}
    29  }
    30  
    31  // Enum defines enumerated meta-column with the string type
    32  func (e Enumset) Text() Meta {
    33  	return TextEnumerator{Enumerator{e, &sync.Mutex{}, len(e) != 0}}
    34  }
    35  
    36  // Enum defines enumerated meta-column with the int type
    37  func (e Enumset) Integer() Meta {
    38  	return IntegerEnumerator{
    39  		Enumerator{e, &sync.Mutex{}, len(e) != 0},
    40  		fu.KeysOf((map[string]int)(e)).([]string),
    41  	}
    42  }
    43  
    44  // Enum defines enumerated meta-column with the float32 type
    45  func (e Enumset) Float32() Meta {
    46  	return Float32Enumerator{
    47  		IntegerEnumerator{
    48  			Enumerator{e, &sync.Mutex{}, len(e) != 0},
    49  			fu.KeysOf((map[string]int)(e)).([]string),
    50  		}}
    51  }
    52  
    53  // Enumset is a set of values belongs to one enumeration
    54  type Enumset map[string]int
    55  
    56  // Len returns length of enumset aka count of enum values
    57  func (m Enumset) Len() int {
    58  	return len(m)
    59  }
    60  
    61  // Enumerator the object enumerates enums in data stream
    62  type Enumerator struct {
    63  	m  Enumset
    64  	mu *sync.Mutex
    65  	ro bool
    66  }
    67  
    68  func (ce Enumerator) enumerate(v string) (e int, ok bool) {
    69  	ce.mu.Lock()
    70  	if e, ok = ce.m[v]; !ok {
    71  		if ce.ro {
    72  			panic(xerrors.Errorf("readonly enumset does not have value `%v`" + v))
    73  		}
    74  		ce.m[v] = len(ce.m)
    75  	}
    76  	ce.mu.Unlock()
    77  	return
    78  }
    79  
    80  // Type returns the type of column
    81  func (ce Enumerator) Type() reflect.Type {
    82  	return enumType // it's the Enum meta-column
    83  }
    84  func (ce Enumerator) Convert(v string, value *reflect.Value, _, _ int) (na bool, err error) {
    85  	if v == "" {
    86  		*value = reflect.ValueOf("")
    87  		return true, nil
    88  	}
    89  	e, _ := ce.enumerate(v)
    90  	*value = reflect.ValueOf(Enum{v, e})
    91  	return
    92  }
    93  func (ce Enumerator) Format(x reflect.Value, na bool) string {
    94  	if na {
    95  		return ""
    96  	}
    97  	if x.Type() == enumType {
    98  		text := x.Interface().(Enum).Text
    99  		if _, ok := ce.m[text]; ok {
   100  			return text
   101  		}
   102  	}
   103  	panic(xerrors.Errorf("`%v` is not an enumeration value", x))
   104  }
   105  
   106  type IntegerEnumerator struct {
   107  	Enumerator
   108  	rev []string
   109  }
   110  
   111  func (ce IntegerEnumerator) Type() reflect.Type {
   112  	return fu.Int
   113  }
   114  
   115  func (ce IntegerEnumerator) Convert(v string, value *reflect.Value, _, _ int) (bool, error) {
   116  	if v == "" {
   117  		*value = reflect.ValueOf("")
   118  		return true, nil
   119  	}
   120  	e, ok := ce.enumerate(v)
   121  	if !ok {
   122  		ce.mu.Lock()
   123  		ce.rev = append(ce.rev, v)
   124  		ce.mu.Unlock()
   125  	}
   126  	*value = reflect.ValueOf(e)
   127  	return false, nil
   128  }
   129  
   130  func (ce IntegerEnumerator) Format(x reflect.Value, na bool) string {
   131  	if na {
   132  		return ""
   133  	}
   134  	if x.Kind() == reflect.String {
   135  		text := x.String()
   136  		if e, ok := ce.m[text]; ok {
   137  			return fmt.Sprint(e)
   138  		}
   139  	}
   140  	panic(xerrors.Errorf("`%v` is not an enumeration value", x))
   141  }
   142  
   143  type Float32Enumerator struct{ IntegerEnumerator }
   144  
   145  func (ce Float32Enumerator) Type() reflect.Type {
   146  	return fu.Float32
   147  }
   148  
   149  func (ce Float32Enumerator) Convert(v string, value *reflect.Value, _, _ int) (na bool, err error) {
   150  	val := reflect.Value{}
   151  	if na, err = ce.IntegerEnumerator.Convert(v, &val, 0, 0); err == nil {
   152  		*value = reflect.ValueOf(float32(val.Int()))
   153  	}
   154  	return
   155  }
   156  
   157  type TextEnumerator struct{ Enumerator }
   158  
   159  func (ce TextEnumerator) Type() reflect.Type {
   160  	return fu.String
   161  }
   162  
   163  func (ce TextEnumerator) Convert(v string, value *reflect.Value, _, _ int) (bool, error) {
   164  	if v == "" {
   165  		*value = reflect.ValueOf("")
   166  		return true, nil
   167  	}
   168  	ce.enumerate(v)
   169  	*value = reflect.ValueOf(v)
   170  	return false, nil
   171  }