Co-authored-by: marbar3778 <marbar3778@yahoo.com> Co-authored-by: Marko <marko@baricevic.me>
579 lines
20 KiB
Go
579 lines
20 KiB
Go
package schema
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"regexp"
|
|
"time"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// Kind represents the basic type of a field in an object.
|
|
// Each kind defines the following encodings:
|
|
//
|
|
// - Go Encoding: the golang type which should be accepted by listeners and
|
|
// generated by decoders when providing entity updates.
|
|
// - JSON Encoding: the JSON encoding which should be used when encoding the field to JSON.
|
|
// - Key Binary Encoding: the encoding which should be used when encoding the field
|
|
// as a key in binary messages. Some encodings specify a terminal and non-terminal form
|
|
// depending on whether or not the field is the last field in the key.
|
|
// - Value Binary Encoding: the encoding which should be used when encoding the field
|
|
// as a value in binary messages.
|
|
//
|
|
// When there is some non-determinism in an encoding, kinds should specify what
|
|
// values they accept and also what is the canonical, deterministic encoding which
|
|
// should be preferably emitted by serializers.
|
|
//
|
|
// Binary encodings were chosen based on what is likely to be the most convenient default binary encoding
|
|
// for state management implementations. This encoding allows for sorted keys whenever it is possible for a kind
|
|
// and is deterministic.
|
|
// Modules that use the specified encoding natively will have a trivial decoder implementation because the
|
|
// encoding is already in the correct format after any initial prefix bytes are stripped.
|
|
type Kind int
|
|
|
|
const (
|
|
// InvalidKind indicates that an invalid type.
|
|
InvalidKind Kind = iota
|
|
|
|
// StringKind is a string type.
|
|
// Go Encoding: UTF-8 string with no null characters.
|
|
// JSON Encoding: string
|
|
// Key Binary Encoding:
|
|
// non-terminal: UTF-8 string with no null characters suffixed with a null character
|
|
// terminal: UTF-8 string with no null characters
|
|
// Value Binary Encoding: the same value binary encoding as BytesKind.
|
|
StringKind
|
|
|
|
// BytesKind represents a byte array.
|
|
// Go Encoding: []byte
|
|
// JSON Encoding: base64 encoded string, canonical values should be encoded with standard encoding and padding.
|
|
// Either standard or URL encoding with or without padding should be accepted.
|
|
// Key Binary Encoding:
|
|
// non-terminal: length prefixed bytes where the width of the length prefix is 1, 2, 3 or 4 bytes depending on
|
|
// the field's MaxLength (defaulting to 4 bytes).
|
|
// Length prefixes should be big-endian encoded.
|
|
// Values larger than 2^32 bytes are not supported (likely key-value stores impose a lower limit).
|
|
// terminal: raw bytes with no length prefix
|
|
// Value Binary Encoding: two 32-bit unsigned little-endian integers, the first one representing the offset of the
|
|
// value in the buffer and the second one representing the length of the value.
|
|
BytesKind
|
|
|
|
// Int8Kind represents an 8-bit signed integer.
|
|
// Go Encoding: int8
|
|
// JSON Encoding: number
|
|
// Key Binary Encoding: 1-byte two's complement encoding, with the first bit inverted for sorting.
|
|
// Value Binary Encoding: 1-byte two's complement encoding.
|
|
Int8Kind
|
|
|
|
// Uint8Kind represents an 8-bit unsigned integer.
|
|
// Go Encoding: uint8
|
|
// JSON Encoding: number
|
|
// Key Binary Encoding: 1-byte unsigned encoding.
|
|
// Value Binary Encoding: 1-byte unsigned encoding.
|
|
Uint8Kind
|
|
|
|
// Int16Kind represents a 16-bit signed integer.
|
|
// Go Encoding: int16
|
|
// JSON Encoding: number
|
|
// Key Binary Encoding: 2-byte two's complement big-endian encoding, with the first bit inverted for sorting.
|
|
// Value Binary Encoding: 2 byte two's complement little-endian encoding.
|
|
Int16Kind
|
|
|
|
// Uint16Kind represents a 16-bit unsigned integer.
|
|
// Go Encoding: uint16
|
|
// JSON Encoding: number
|
|
// Key Binary Encoding: 2-byte unsigned big-endian encoding.
|
|
// Value Binary Encoding: 2-byte unsigned little-endian encoding.
|
|
Uint16Kind
|
|
|
|
// Int32Kind represents a 32-bit signed integer.
|
|
// Go Encoding: int32
|
|
// JSON Encoding: number
|
|
// Key Binary Encoding: 4-byte two's complement big-endian encoding, with the first bit inverted for sorting.
|
|
// Value Binary Encoding: 4-byte two's complement little-endian encoding.
|
|
Int32Kind
|
|
|
|
// Uint32Kind represents a 32-bit unsigned integer.
|
|
// Go Encoding: uint32
|
|
// JSON Encoding: number
|
|
// Key Binary Encoding: 4-byte unsigned big-endian encoding.
|
|
// Value Binary Encoding: 4-byte unsigned little-endian encoding.
|
|
Uint32Kind
|
|
|
|
// Int64Kind represents a 64-bit signed integer.
|
|
// Go Encoding: int64
|
|
// JSON Encoding: base10 integer string which matches the IntegerFormat regex
|
|
// The canonical encoding should include no leading zeros.
|
|
// Key Binary Encoding: 8-byte two's complement big-endian encoding, with the first bit inverted for sorting.
|
|
// Value Binary Encoding: 8-byte two's complement little-endian encoding.
|
|
Int64Kind
|
|
|
|
// Uint64Kind represents a 64-bit unsigned integer.
|
|
// Go Encoding: uint64
|
|
// JSON Encoding: base10 integer string which matches the IntegerFormat regex
|
|
// Canonically encoded values should include no leading zeros.
|
|
// Key Binary Encoding: 8-byte unsigned big-endian encoding.
|
|
// Value Binary Encoding: 8-byte unsigned little-endian encoding.
|
|
Uint64Kind
|
|
|
|
// IntegerKind represents an arbitrary precision integer number.
|
|
// Support for expressing the maximum bit precision of values will be added in the future.
|
|
// Go Encoding: string which matches the IntegerFormat regex (unstable, subject to change).
|
|
// JSON Encoding: base10 integer string
|
|
// Canonically encoded values should include no leading zeros.
|
|
// Equality comparison with integers should be done using numerical equality rather
|
|
// than string equality.
|
|
IntegerKind
|
|
|
|
// DecimalKind represents an arbitrary precision decimal or integer number.
|
|
// Support for optionally limiting the precision may be added in the future.
|
|
// Go Encoding: string which matches the DecimalFormat regex
|
|
// JSON Encoding: base10 decimal string
|
|
// Canonically encoded values should include no leading zeros or trailing zeros,
|
|
// and exponential notation with a lowercase 'e' should be used for any numbers
|
|
// with an absolute value less than or equal to 1e-6 or greater than or equal to 1e6.
|
|
// Equality comparison with decimals should be done using numerical equality rather
|
|
// than string equality.
|
|
DecimalKind
|
|
|
|
// BoolKind represents a boolean true or false value.
|
|
// Go Encoding: bool
|
|
// JSON Encoding: boolean
|
|
// Key Binary Encoding: 1-byte encoding where 0 is false and 1 is true.
|
|
// Value Binary Encoding: 1-byte encoding where 0 is false and 1 is true.
|
|
BoolKind
|
|
|
|
// TimeKind represents a nanosecond precision UNIX time value (with zero representing January 1, 1970 UTC).
|
|
// Its valid range is +/- 2^63 (the range of a 64-bit signed integer).
|
|
// Go Encoding: time.Time
|
|
// JSON Encoding: Any value IS0 8601 time stamp should be accepted.
|
|
// Canonical values should be encoded with UTC time zone Z, nanoseconds should
|
|
// be encoded with no trailing zeros, and T time values should always be present
|
|
// even at 00:00:00.
|
|
// Key Binary Encoding: 8-byte two's complement big-endian encoding, with the first bit inverted for sorting.
|
|
// Value Binary Encoding: 8-byte two's complement little-endian encoding.
|
|
TimeKind
|
|
|
|
// DurationKind represents the elapsed time between two nanosecond precision time values.
|
|
// Its valid range is +/- 2^63 (the range of a 64-bit signed integer).
|
|
// Go Encoding: time.Duration
|
|
// JSON Encoding: the number of seconds as a decimal string with no trailing zeros followed by
|
|
// a lowercase 's' character to represent seconds.
|
|
// Key Binary Encoding: 8-byte two's complement big-endian encoding, with the first bit inverted for sorting.
|
|
// Value Binary Encoding: 8-byte two's complement little-endian encoding.
|
|
DurationKind
|
|
|
|
// Float32Kind represents an IEEE-754 32-bit floating point number.
|
|
// Go Encoding: float32
|
|
// JSON Encoding: number
|
|
// Key Binary Encoding: 4-byte IEEE-754 encoding.
|
|
// Value Binary Encoding: 4-byte IEEE-754 encoding.
|
|
Float32Kind
|
|
|
|
// Float64Kind represents an IEEE-754 64-bit floating point number.
|
|
// Go Encoding: float64
|
|
// JSON Encoding: number
|
|
// Key Binary Encoding: 8-byte IEEE-754 encoding.
|
|
// Value Binary Encoding: 8-byte IEEE-754 encoding.
|
|
Float64Kind
|
|
|
|
// AddressKind represents an account address which is represented by a variable length array of bytes.
|
|
// Addresses usually have a human-readable rendering, such as bech32, and tooling should provide
|
|
// a way for apps to define a string encoder for friendly user-facing display. Addresses have a maximum
|
|
// supported length of 63 bytes.
|
|
// Go Encoding: []byte
|
|
// JSON Encoding: addresses should be encoded as strings using the human-readable address renderer
|
|
// provided to the JSON encoder.
|
|
// Key Binary Encoding:
|
|
// non-terminal: bytes prefixed with 1-byte length prefix
|
|
// terminal: raw bytes with no length prefix
|
|
// Value Binary Encoding: bytes prefixed with 1-byte length prefix.
|
|
AddressKind
|
|
|
|
// EnumKind represents a value of an enum type.
|
|
// Fields of this type are expected to set the EnumType field in the field definition to the enum
|
|
// definition.
|
|
// Go Encoding: string
|
|
// JSON Encoding: string
|
|
// Key Binary Encoding: the same binary encoding as the EnumType's numeric kind.
|
|
// Value Binary Encoding: the same binary encoding as the EnumType's numeric kind.
|
|
EnumKind
|
|
|
|
// JSONKind represents arbitrary JSON data.
|
|
// Go Encoding: json.RawMessage
|
|
// JSON Encoding: any valid JSON value
|
|
// Key Binary Encoding: string encoding
|
|
// Value Binary Encoding: string encoding
|
|
JSONKind
|
|
|
|
// UIntNKind represents a signed integer type with a width in bits specified by the Size field in the
|
|
// field definition.
|
|
// Support for this is currently UNIMPLEMENTED, this notice will be removed when it is added.
|
|
// N must be a multiple of 8, and it is invalid for N to equal 8, 16, 32, 64 as there are more specific
|
|
// types for these widths.
|
|
// Go Encoding: []byte where len([]byte) == Size / 8, little-endian encoded.
|
|
// JSON Encoding: base10 integer string matching the IntegerFormat regex, canonically with no leading zeros.
|
|
// Key Binary Encoding: N / 8 bytes big-endian encoded
|
|
// Value Binary Encoding: N / 8 bytes little-endian encoded
|
|
UIntNKind
|
|
|
|
// IntNKind represents an unsigned integer type with a width in bits specified by the Size field in the
|
|
// field definition. N must be a multiple of 8.
|
|
// Support for this is currently UNIMPLEMENTED, this notice will be removed when it is added.
|
|
// N must be a multiple of 8, and it is invalid for N to equal 8, 16, 32, 64 as there are more specific
|
|
// types for these widths.
|
|
// Go Encoding: []byte where len([]byte) == Size / 8, two's complement little-endian encoded.
|
|
// JSON Encoding: base10 integer string matching the IntegerFormat regex, canonically with no leading zeros.
|
|
// Key Binary Encoding: N / 8 bytes big-endian two's complement encoded with the first bit inverted for sorting.
|
|
// Value Binary Encoding: N / 8 bytes little-endian two's complement encoded.
|
|
IntNKind
|
|
|
|
// StructKind represents a struct object.
|
|
// Support for this is currently UNIMPLEMENTED, this notice will be removed when it is added.
|
|
// Go Encoding: an array of type []interface{} where each element is of the respective field's kind type.
|
|
// JSON Encoding: an object where each key is the field name and the value is the field value.
|
|
// Canonically, keys are in alphabetical order with no extra whitespace.
|
|
// Key Binary Encoding: not valid as a key field.
|
|
// Value Binary Encoding: 32-bit unsigned little-endian length prefix,
|
|
// followed by the value binary encoding of each field in order.
|
|
StructKind
|
|
|
|
// OneOfKind represents a field that can be one of a set of types.
|
|
// Support for this is currently UNIMPLEMENTED, this notice will be removed when it is added.
|
|
// Go Encoding: the anonymous struct { Case string; Value interface{} }, aliased as OneOfValue.
|
|
// JSON Encoding: same as the case's struct encoding with "@type" set to the case name.
|
|
// Key Binary Encoding: not valid as a key field.
|
|
// Value Binary Encoding: the oneof's discriminant numeric value encoded as its discriminant kind
|
|
// followed by the encoded value.
|
|
OneOfKind
|
|
|
|
// ListKind represents a list of elements.
|
|
// Support for this is currently UNIMPLEMENTED, this notice will be removed when it is added.
|
|
// Go Encoding: an array of type []interface{} where each element is of the respective field's kind type.
|
|
// JSON Encoding: an array of values where each element is the field value.
|
|
// Canonically, there is no extra whitespace.
|
|
// Key Binary Encoding: not valid as a key field.
|
|
// Value Binary Encoding: 32-bit unsigned little-endian size prefix indicating the size of the encoded data in bytes,
|
|
// followed by a 32-bit unsigned little-endian count of the number of elements in the list,
|
|
// followed by each element encoded with value binary encoding.
|
|
ListKind
|
|
)
|
|
|
|
// MAX_VALID_KIND is the maximum valid kind value.
|
|
const MAX_VALID_KIND = JSONKind
|
|
|
|
const (
|
|
// IntegerFormat is a regex that describes the format integer number strings must match. It specifies
|
|
// that integers may have at most 100 digits.
|
|
IntegerFormat = `^-?[0-9]{1,100}$`
|
|
|
|
// DecimalFormat is a regex that describes the format decimal number strings must match. It specifies
|
|
// that decimals may have at most 50 digits before and after the decimal point and may have an optional
|
|
// exponent of up to 2 digits. These restrictions ensure that the decimal can be accurately represented
|
|
// by a wide variety of implementations.
|
|
DecimalFormat = `^-?[0-9]{1,50}(\.[0-9]{1,50})?([eE][-+]?[0-9]{1,2})?$`
|
|
)
|
|
|
|
// Validate returns an errContains if the kind is invalid.
|
|
func (t Kind) Validate() error {
|
|
if t <= InvalidKind {
|
|
return fmt.Errorf("unknown type: %d", t)
|
|
}
|
|
if t > JSONKind {
|
|
return fmt.Errorf("invalid type: %d", t)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// String returns a string representation of the kind.
|
|
func (t Kind) String() string {
|
|
switch t {
|
|
case StringKind:
|
|
return "string"
|
|
case BytesKind:
|
|
return "bytes"
|
|
case Int8Kind:
|
|
return "int8"
|
|
case Uint8Kind:
|
|
return "uint8"
|
|
case Int16Kind:
|
|
return "int16"
|
|
case Uint16Kind:
|
|
return "uint16"
|
|
case Int32Kind:
|
|
return "int32"
|
|
case Uint32Kind:
|
|
return "uint32"
|
|
case Int64Kind:
|
|
return "int64"
|
|
case Uint64Kind:
|
|
return "uint64"
|
|
case DecimalKind:
|
|
return "decimal"
|
|
case IntegerKind:
|
|
return "integer"
|
|
case BoolKind:
|
|
return "bool"
|
|
case TimeKind:
|
|
return "time"
|
|
case DurationKind:
|
|
return "duration"
|
|
case Float32Kind:
|
|
return "float32"
|
|
case Float64Kind:
|
|
return "float64"
|
|
case AddressKind:
|
|
return "address"
|
|
case EnumKind:
|
|
return "enum"
|
|
case JSONKind:
|
|
return "json"
|
|
default:
|
|
return fmt.Sprintf("invalid(%d)", t)
|
|
}
|
|
}
|
|
|
|
// ValidateValueType returns an errContains if the value does not conform to the expected go type.
|
|
// Some fields may accept nil values, however, this method does not have any notion of
|
|
// nullability. This method only validates that the go type of the value is correct for the kind
|
|
// and does not validate string or json formats. Kind.ValidateValue does a more thorough validation
|
|
// of number and json string formatting.
|
|
func (t Kind) ValidateValueType(value interface{}) error {
|
|
switch t {
|
|
case StringKind:
|
|
_, ok := value.(string)
|
|
if !ok {
|
|
return fmt.Errorf("expected string, got %T", value)
|
|
}
|
|
case BytesKind:
|
|
_, ok := value.([]byte)
|
|
if !ok {
|
|
return fmt.Errorf("expected []byte, got %T", value)
|
|
}
|
|
case Int8Kind:
|
|
_, ok := value.(int8)
|
|
if !ok {
|
|
return fmt.Errorf("expected int8, got %T", value)
|
|
}
|
|
case Uint8Kind:
|
|
_, ok := value.(uint8)
|
|
if !ok {
|
|
return fmt.Errorf("expected uint8, got %T", value)
|
|
}
|
|
case Int16Kind:
|
|
_, ok := value.(int16)
|
|
if !ok {
|
|
return fmt.Errorf("expected int16, got %T", value)
|
|
}
|
|
case Uint16Kind:
|
|
_, ok := value.(uint16)
|
|
if !ok {
|
|
return fmt.Errorf("expected uint16, got %T", value)
|
|
}
|
|
case Int32Kind:
|
|
_, ok := value.(int32)
|
|
if !ok {
|
|
return fmt.Errorf("expected int32, got %T", value)
|
|
}
|
|
case Uint32Kind:
|
|
_, ok := value.(uint32)
|
|
if !ok {
|
|
return fmt.Errorf("expected uint32, got %T", value)
|
|
}
|
|
case Int64Kind:
|
|
_, ok := value.(int64)
|
|
if !ok {
|
|
return fmt.Errorf("expected int64, got %T", value)
|
|
}
|
|
case Uint64Kind:
|
|
_, ok := value.(uint64)
|
|
if !ok {
|
|
return fmt.Errorf("expected uint64, got %T", value)
|
|
}
|
|
case IntegerKind:
|
|
_, ok := value.(string)
|
|
if !ok {
|
|
return fmt.Errorf("expected string, got %T", value)
|
|
}
|
|
|
|
case DecimalKind:
|
|
_, ok := value.(string)
|
|
if !ok {
|
|
return fmt.Errorf("expected string, got %T", value)
|
|
}
|
|
case BoolKind:
|
|
_, ok := value.(bool)
|
|
if !ok {
|
|
return fmt.Errorf("expected bool, got %T", value)
|
|
}
|
|
case TimeKind:
|
|
_, ok := value.(time.Time)
|
|
if !ok {
|
|
return fmt.Errorf("expected time.Time, got %T", value)
|
|
}
|
|
case DurationKind:
|
|
_, ok := value.(time.Duration)
|
|
if !ok {
|
|
return fmt.Errorf("expected time.Duration, got %T", value)
|
|
}
|
|
case Float32Kind:
|
|
_, ok := value.(float32)
|
|
if !ok {
|
|
return fmt.Errorf("expected float32, got %T", value)
|
|
}
|
|
case Float64Kind:
|
|
_, ok := value.(float64)
|
|
if !ok {
|
|
return fmt.Errorf("expected float64, got %T", value)
|
|
}
|
|
case AddressKind:
|
|
_, ok := value.([]byte)
|
|
if !ok {
|
|
return fmt.Errorf("expected []byte, got %T", value)
|
|
}
|
|
case EnumKind:
|
|
_, ok := value.(string)
|
|
if !ok {
|
|
return fmt.Errorf("expected string, got %T", value)
|
|
}
|
|
case JSONKind:
|
|
_, ok := value.(json.RawMessage)
|
|
if !ok {
|
|
return fmt.Errorf("expected json.RawMessage, got %T", value)
|
|
}
|
|
default:
|
|
return fmt.Errorf("invalid type: %d", t)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateValue returns an errContains if the value does not conform to the expected go type and format.
|
|
// It is more thorough, but slower, than Kind.ValidateValueType and validates that Integer, Decimal and JSON
|
|
// values are formatted correctly. It cannot validate enum values because Kind's do not have enum schemas.
|
|
func (t Kind) ValidateValue(value interface{}) error {
|
|
err := t.ValidateValueType(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch t {
|
|
case StringKind:
|
|
str := value.(string)
|
|
if !utf8.ValidString(str) {
|
|
return fmt.Errorf("expected valid utf-8 string, got %s", value)
|
|
}
|
|
|
|
// check for null characters
|
|
for _, r := range str {
|
|
if r == 0 {
|
|
return fmt.Errorf("expected string without null characters, got %s", value)
|
|
}
|
|
}
|
|
case IntegerKind:
|
|
if !integerRegex.Match([]byte(value.(string))) {
|
|
return fmt.Errorf("expected base10 integer, got %s", value)
|
|
}
|
|
case DecimalKind:
|
|
if !decimalRegex.Match([]byte(value.(string))) {
|
|
return fmt.Errorf("expected decimal number, got %s", value)
|
|
}
|
|
case JSONKind:
|
|
if !json.Valid(value.(json.RawMessage)) {
|
|
return fmt.Errorf("expected valid JSON, got %s", value)
|
|
}
|
|
default:
|
|
return nil
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidKeyKind returns true if the kind is a valid key kind.
|
|
// All kinds except Float32Kind, Float64Kind, and JSONKind are valid key kinds
|
|
// because they do not define a strict form of equality.
|
|
func (t Kind) ValidKeyKind() bool {
|
|
switch t {
|
|
case Float32Kind, Float64Kind, JSONKind:
|
|
return false
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
var (
|
|
integerRegex = regexp.MustCompile(IntegerFormat)
|
|
decimalRegex = regexp.MustCompile(DecimalFormat)
|
|
)
|
|
|
|
// KindForGoValue finds the simplest kind that can represent the given go value. It will not, however,
|
|
// return kinds such as IntegerKind, DecimalKind, AddressKind, or EnumKind which all can be
|
|
// represented as strings.
|
|
func KindForGoValue(value interface{}) Kind {
|
|
switch value.(type) {
|
|
case string:
|
|
return StringKind
|
|
case []byte:
|
|
return BytesKind
|
|
case int8:
|
|
return Int8Kind
|
|
case uint8:
|
|
return Uint8Kind
|
|
case int16:
|
|
return Int16Kind
|
|
case uint16:
|
|
return Uint16Kind
|
|
case int32:
|
|
return Int32Kind
|
|
case uint32:
|
|
return Uint32Kind
|
|
case int64:
|
|
return Int64Kind
|
|
case uint64:
|
|
return Uint64Kind
|
|
case float32:
|
|
return Float32Kind
|
|
case float64:
|
|
return Float64Kind
|
|
case bool:
|
|
return BoolKind
|
|
case time.Time:
|
|
return TimeKind
|
|
case time.Duration:
|
|
return DurationKind
|
|
case json.RawMessage:
|
|
return JSONKind
|
|
default:
|
|
return InvalidKind
|
|
}
|
|
}
|
|
|
|
// MarshalJSON marshals the kind to a JSON string and returns an error if the kind is invalid.
|
|
func (t Kind) MarshalJSON() ([]byte, error) {
|
|
if err := t.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(t.String())
|
|
}
|
|
|
|
// UnmarshalJSON unmarshals the kind from a JSON string and returns an error if the kind is invalid.
|
|
func (t *Kind) UnmarshalJSON(data []byte) error {
|
|
var s string
|
|
err := json.Unmarshal(data, &s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
k, ok := kindStrings[s]
|
|
if !ok {
|
|
return fmt.Errorf("invalid kind: %s", s)
|
|
}
|
|
*t = k
|
|
return nil
|
|
}
|
|
|
|
var kindStrings = map[string]Kind{}
|
|
|
|
func init() {
|
|
for i := InvalidKind + 1; i <= MAX_VALID_KIND; i++ {
|
|
kindStrings[i.String()] = i
|
|
}
|
|
}
|