github.com/vcilabs/webrpc@v0.5.2-0.20201116131534-162e27b1b33b/schema/var_type.go (about)

     1  package schema
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  type VarType struct {
    12  	expr string
    13  	Type DataType
    14  
    15  	List   *VarListType
    16  	Map    *VarMapType
    17  	Struct *VarStructType
    18  
    19  	UserDefined *VarUserDefinedType
    20  }
    21  
    22  func (t *VarType) String() string {
    23  	return t.expr
    24  }
    25  
    26  func (t *VarType) MarshalJSON() ([]byte, error) {
    27  	buf := bytes.NewBufferString(`"`)
    28  	buf.WriteString(t.String())
    29  	buf.WriteString(`"`)
    30  	return buf.Bytes(), nil
    31  }
    32  
    33  func (t *VarType) UnmarshalJSON(b []byte) error {
    34  	if len(b) <= 2 {
    35  		return errors.Errorf("json error: type cannot be empty")
    36  	}
    37  	s := string(b) // string value will be wrapped in quotes
    38  
    39  	// validate its a string value
    40  	if s[0:1] != "\"" {
    41  		return errors.Errorf("json error: string value is expected")
    42  	}
    43  	if s[len(s)-1:] != "\"" {
    44  		return errors.Errorf("json error: string value is expected")
    45  	}
    46  
    47  	// trim string quotes from the json string
    48  	s = s[1:]
    49  	s = s[:len(s)-1]
    50  
    51  	// set the expr from value
    52  	t.expr = s
    53  
    54  	return nil
    55  }
    56  
    57  func (t *VarType) Parse(schema *WebRPCSchema) error {
    58  	if t.expr == "" {
    59  		return errors.Errorf("schema error: type expr cannot be empty")
    60  	}
    61  	err := ParseVarTypeExpr(schema, t.expr, t)
    62  	if err != nil {
    63  		return err
    64  	}
    65  	t.expr = buildVarTypeExpr(t, "")
    66  	return nil
    67  }
    68  
    69  type VarListType struct {
    70  	Elem *VarType
    71  }
    72  
    73  type VarMapType struct {
    74  	Key   DataType // see, VarMapKeyDataTypes -- only T_String or T_XintX supported
    75  	Value *VarType
    76  }
    77  
    78  type VarStructType struct {
    79  	Name    string
    80  	Message *Message
    81  }
    82  
    83  type VarUserDefinedType struct {
    84  	Name string
    85  }
    86  
    87  func ParseVarTypeExpr(schema *WebRPCSchema, expr string, vt *VarType) error {
    88  	if expr == "" {
    89  		return nil
    90  	}
    91  	vt.expr = expr
    92  
    93  	// parse data type from string
    94  	dataType, ok := DataTypeFromString[expr]
    95  
    96  	if !ok {
    97  		// test for complex datatype
    98  		if isListExpr(expr) {
    99  			dataType = T_List
   100  		} else if isMapExpr(expr) || isGoSchemaMapExpr(expr) {
   101  			dataType = T_Map
   102  		} else {
   103  			dataType = T_UserDefined
   104  		}
   105  	}
   106  	// Set core data type
   107  	vt.Type = dataType
   108  	// For complex types, keep parsing
   109  	switch vt.Type {
   110  	case T_List:
   111  		// create sub-type object for list element
   112  		vt.List = &VarListType{Elem: &VarType{}}
   113  
   114  		// shift expr, and keep parsing
   115  		expr = strings.TrimPrefix(expr, DataTypeToString[T_List])
   116  		err := ParseVarTypeExpr(schema, expr, vt.List.Elem)
   117  		if err != nil {
   118  			return err
   119  		}
   120  
   121  	case T_Map:
   122  		var key string
   123  		var value string
   124  		var err error
   125  		// parse map expr
   126  		if schema.SchemaType == "go" {
   127  			key, value, err = parseGoSchemaMapExpr(expr)
   128  			if err != nil {
   129  				return err
   130  			}
   131  		} else {
   132  			key, value, err = parseMapExpr(expr)
   133  			if err != nil {
   134  				return err
   135  			}
   136  		}
   137  
   138  		keyDataType, ok := DataTypeFromString[key]
   139  		if !ok {
   140  			return errors.Errorf("schema error: invalid map key type '%s' for expr '%s'", key, expr)
   141  		}
   142  
   143  		// create sub-type object for map
   144  		vt.Map = &VarMapType{Key: keyDataType, Value: &VarType{}}
   145  
   146  		if schema.SchemaType == "go" && value == "interface{" {
   147  			// shift expr and keep parsing
   148  			expr = value + "}"
   149  		} else {
   150  			// shift expr and keep parsing
   151  			expr = value
   152  		}
   153  		err = ParseVarTypeExpr(schema, expr, vt.Map.Value)
   154  		if err != nil {
   155  			return err
   156  		}
   157  
   158  	case T_UserDefined:
   159  		userDefinedExpr := expr
   160  		vt.Type = T_UserDefined
   161  		vt.UserDefined = &VarUserDefinedType{Name: userDefinedExpr}
   162  
   163  	case T_Unknown:
   164  
   165  		structExpr := expr
   166  		msg, ok := getMessageType(schema, structExpr)
   167  		if !ok || msg == nil {
   168  			return errors.Errorf("schema error: invalid struct/message type '%s'", structExpr)
   169  		}
   170  
   171  		vt.Type = T_Struct
   172  		vt.Struct = &VarStructType{Name: structExpr, Message: msg}
   173  
   174  	default:
   175  		// basic type, we're done here
   176  	}
   177  
   178  	return nil
   179  }
   180  
   181  func parseMapExpr(expr string) (string, string, error) {
   182  	if !isMapExpr(expr) {
   183  		return "", "", errors.Errorf("schema error: invalid map expr for '%s'", expr)
   184  	}
   185  
   186  	mapKeyword := DataTypeToString[T_Map]
   187  	expr = expr[len(mapKeyword):]
   188  
   189  	if expr[0:1] != "<" {
   190  		return "", "", errors.Errorf("schema error: invalid map syntax for '%s'", expr)
   191  	}
   192  	if expr[len(expr)-1:] != ">" {
   193  		return "", "", errors.Errorf("schema error: invalid map syntax for '%s'", expr)
   194  	}
   195  	expr = expr[1 : len(expr)-1]
   196  
   197  	p := strings.Index(expr, ",")
   198  	if p < 0 {
   199  		return "", "", errors.Errorf("schema error: invalid map syntax for '%s'", expr)
   200  	}
   201  
   202  	key := expr[0:p]
   203  	value := expr[p+1:]
   204  
   205  	if !isValidVarKeyType(key) {
   206  		return "", "", errors.Errorf("schema error: invalid map key '%s' for '%s'", key, expr)
   207  	}
   208  
   209  	return key, value, nil
   210  }
   211  
   212  func parseGoSchemaMapExpr(expr string) (string, string, error) {
   213  	if !isGoSchemaMapExpr(expr) {
   214  		return "", "", errors.Errorf("schema error: invalid map expr for '%s'", expr)
   215  	}
   216  
   217  	mapKeyword := DataTypeToString[T_Map]
   218  	expr = expr[len(mapKeyword):]
   219  	key := getMapKey(expr)
   220  	value := strings.TrimPrefix(expr, "["+key+"]")
   221  	if !isValidVarKeyType(key) {
   222  		return "", "", errors.Errorf("schema error: invalid map key '%s' for '%s'", key, expr)
   223  	}
   224  
   225  	return key, value, nil
   226  }
   227  
   228  func buildVarTypeExpr(vt *VarType, expr string) string {
   229  	switch vt.Type {
   230  	case T_Unknown:
   231  		return "<unknown>"
   232  
   233  	case T_List:
   234  		expr += "[]" + buildVarTypeExpr(vt.List.Elem, expr)
   235  		return expr
   236  
   237  	case T_Map:
   238  		expr += fmt.Sprintf("map<%s,%s>", vt.Map.Key, buildVarTypeExpr(vt.Map.Value, ""))
   239  		return expr
   240  
   241  	case T_Struct:
   242  		expr += vt.Struct.Name
   243  		return expr
   244  
   245  	case T_UserDefined:
   246  		expr += vt.UserDefined.Name
   247  		return expr
   248  	default:
   249  		// basic type
   250  		expr += vt.Type.String()
   251  		return expr
   252  	}
   253  }
   254  
   255  func isListExpr(expr string) bool {
   256  	listTest := DataTypeToString[T_List]
   257  	return strings.HasPrefix(expr, listTest)
   258  }
   259  
   260  func isMapExpr(expr string) bool {
   261  	mapTest := DataTypeToString[T_Map] + "<"
   262  	return strings.HasPrefix(expr, mapTest)
   263  }
   264  
   265  func isGoSchemaMapExpr(expr string) bool {
   266  	mapTest := DataTypeToString[T_Map] + "["
   267  	return strings.HasPrefix(expr, mapTest)
   268  }
   269  
   270  func getMessageType(schema *WebRPCSchema, structExpr string) (*Message, bool) {
   271  	for _, msg := range schema.Messages {
   272  		if structExpr == string(msg.Name) {
   273  			return msg, true
   274  		}
   275  	}
   276  	return nil, false
   277  }
   278  
   279  var VarKeyDataTypes = []DataType{
   280  	T_String, T_Uint, T_Uint8, T_Uint16, T_Uint32, T_Uint64, T_Int, T_Int8, T_Int16, T_Int32, T_Int64,
   281  }
   282  
   283  var VarIntegerDataTypes = []DataType{
   284  	T_Uint, T_Uint8, T_Uint16, T_Uint32, T_Uint64, T_Int, T_Int8, T_Int16, T_Int32, T_Int64,
   285  }
   286  
   287  func isValidVarKeyType(s string) bool {
   288  	return isValidVarType(s, VarKeyDataTypes)
   289  }
   290  
   291  func isValidVarType(s string, allowedList []DataType) bool {
   292  	dt, ok := DataTypeFromString[s]
   293  	if !ok {
   294  		return false
   295  	}
   296  	for _, t := range allowedList {
   297  		if dt == t {
   298  			return true
   299  		}
   300  	}
   301  	return false
   302  }
   303  
   304  func getMapKey(expr string) string {
   305  	i := strings.Index(expr, "[")
   306  	if i >= 0 {
   307  		j := strings.Index(expr, "]")
   308  		if j >= 0 {
   309  			return expr[i+1 : j]
   310  		}
   311  	}
   312  	return ""
   313  }