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 }