github.com/cloudwego/dynamicgo@v0.2.6-0.20240519101509-707f41b6b834/thrift/idl.go (about) 1 /** 2 * Copyright 2023 CloudWeGo Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package thrift 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "math" 24 "math/rand" 25 "path/filepath" 26 "strings" 27 "time" 28 29 "github.com/cloudwego/dynamicgo/http" 30 "github.com/cloudwego/dynamicgo/internal/json" 31 "github.com/cloudwego/dynamicgo/internal/rt" 32 "github.com/cloudwego/dynamicgo/internal/util" 33 "github.com/cloudwego/dynamicgo/meta" 34 "github.com/cloudwego/thriftgo/parser" 35 "github.com/cloudwego/thriftgo/semantic" 36 ) 37 38 const ( 39 Request ParseTarget = iota 40 Response 41 Exception 42 ) 43 44 // ParseTarget indicates the target to parse 45 type ParseTarget uint8 46 47 // Options is options for parsing thrift IDL. 48 type Options struct { 49 // ParseServiceMode indicates how to parse service. 50 ParseServiceMode meta.ParseServiceMode 51 52 // MapFieldWay indicates StructDescriptor.FieldByKey() uses alias to map field. 53 // By default, we use alias to map, and alias always equals to field name if not given. 54 MapFieldWay meta.MapFieldWay 55 56 // ParseFieldRandomRate indicates whether to parse partial fields and is only used for mock test. 57 // The value means the possibility of randomly parse and embed one field into StructDescriptor. 58 // It must be within (0, 1], and 0 means always parse all fields. 59 ParseFieldRandomRate float64 60 61 // ParseEnumAsInt64 indicates whether to parse enum as I64 (default I32). 62 ParseEnumAsInt64 bool 63 64 // SetOptionalBitmap indicates to set bitmap for optional fields 65 SetOptionalBitmap bool 66 67 // UseDefaultValue indicates to parse and store default value defined on IDL fields. 68 UseDefaultValue bool 69 70 // ParseFunctionMode indicates to parse only response or request for a function 71 ParseFunctionMode meta.ParseFunctionMode 72 73 // EnableThriftBase indicates to explictly handle thrift/base (see README.md) fields. 74 // One field is identified as a thrift base if it satisfies **BOTH** of the following conditions: 75 // 1. Its type is 'base.Base' (for request base) or 'base.BaseResp' (for response base); 76 // 2. it is on the top layer of the root struct of one function. 77 EnableThriftBase bool 78 79 // PutNameSpaceToAnnotation indicates to extract the name-space of one type 80 // and put it on the type's annotation. The annotion format is: 81 // - Key: "thrift.name_space" (== NameSpaceAnnotationKey) 82 // - Values: pairs of Language and Name. for example: 83 // `namespace go base` will got ["go", "base"] 84 // NOTICE: at present, only StructDescriptor.Annotations() can get this 85 PutNameSpaceToAnnotation bool 86 } 87 88 // NewDefaultOptions creates a default Options. 89 func NewDefaultOptions() Options { 90 return Options{} 91 } 92 93 // path := /a/b/c.thrift 94 // includePath := ../d.thrift 95 // result := /a/d.thrift 96 func absPath(path, includePath string) string { 97 if filepath.IsAbs(includePath) { 98 return includePath 99 } 100 return filepath.Join(filepath.Dir(path), includePath) 101 } 102 103 // NewDescritorFromPath behaviors like NewDescritorFromPath, besides it uses DefaultOptions. 104 func NewDescritorFromPath(ctx context.Context, path string, includeDirs ...string) (*ServiceDescriptor, error) { 105 return NewDefaultOptions().NewDescritorFromPath(ctx, path, includeDirs...) 106 } 107 108 // NewDescritorFromContent creates a ServiceDescriptor from a thrift path and its includes, which uses the given options. 109 // The includeDirs is used to find the include files. 110 func (opts Options) NewDescritorFromPath(ctx context.Context, path string, includeDirs ...string) (*ServiceDescriptor, error) { 111 tree, err := parser.ParseFile(path, includeDirs, true) 112 if err != nil { 113 return nil, err 114 } 115 svc, err := parse(ctx, tree, opts.ParseServiceMode, opts) 116 if err != nil { 117 return nil, err 118 } 119 return svc, nil 120 } 121 122 // NewDescritorFromContent creates a ServiceDescriptor from a thrift path and its includes, with specific methods. 123 // If methods is empty, all methods will be parsed. 124 // The includeDirs is used to find the include files. 125 func (opts Options) NewDescriptorFromPathWithMethod(ctx context.Context, path string, includeDirs []string, methods ...string) (*ServiceDescriptor, error) { 126 tree, err := parser.ParseFile(path, includeDirs, true) 127 if err != nil { 128 return nil, err 129 } 130 svc, err := parse(ctx, tree, opts.ParseServiceMode, opts, methods...) 131 if err != nil { 132 return nil, err 133 } 134 return svc, nil 135 } 136 137 // NewDescritorFromContent behaviors like NewDescritorFromPath, besides it uses DefaultOptions. 138 func NewDescritorFromContent(ctx context.Context, path, content string, includes map[string]string, isAbsIncludePath bool) (*ServiceDescriptor, error) { 139 return NewDefaultOptions().NewDescritorFromContent(ctx, path, content, includes, isAbsIncludePath) 140 } 141 142 // NewDescritorFromContent creates a ServiceDescriptor from a thrift content and its includes, which uses the default options. 143 // path is the main thrift file path, content is the main thrift file content. 144 // includes is the thrift file content map, and its keys are specific including thrift file path. 145 // isAbsIncludePath indicates whether these keys of includes are absolute path. If true, the include path will be joined with the main thrift file path. 146 func (opts Options) NewDescritorFromContent(ctx context.Context, path, content string, includes map[string]string, isAbsIncludePath bool) (*ServiceDescriptor, error) { 147 tree, err := parseIDLContent(path, content, includes, isAbsIncludePath) 148 if err != nil { 149 return nil, err 150 } 151 svc, err := parse(ctx, tree, opts.ParseServiceMode, opts) 152 if err != nil { 153 return nil, err 154 } 155 return svc, nil 156 } 157 158 // NewDescritorFromContentWithMethod creates a ServiceDescriptor from a thrift content and its includes, but only parse specific methods. 159 func (opts Options) NewDescriptorFromContentWithMethod(ctx context.Context, path, content string, includes map[string]string, isAbsIncludePath bool, methods ...string) (*ServiceDescriptor, error) { 160 tree, err := parseIDLContent(path, content, includes, isAbsIncludePath) 161 if err != nil { 162 return nil, err 163 } 164 svc, err := parse(ctx, tree, opts.ParseServiceMode, opts, methods...) 165 if err != nil { 166 return nil, err 167 } 168 return svc, nil 169 } 170 171 func parseIDLContent(path, content string, includes map[string]string, isAbsIncludePath bool) (*parser.Thrift, error) { 172 tree, err := parser.ParseString(path, content) 173 if err != nil { 174 return nil, err 175 } 176 _includes := make(map[string]*parser.Thrift, len(includes)) 177 for k, v := range includes { 178 t, err := parser.ParseString(k, v) 179 if err != nil { 180 return nil, err 181 } 182 _includes[k] = t 183 } 184 185 done := map[string]*parser.Thrift{path: tree} 186 if err := refIncludes(tree, path, done, _includes, isAbsIncludePath); err != nil { 187 return nil, err 188 } 189 return tree, nil 190 } 191 192 func refIncludes(tree *parser.Thrift, path string, done map[string]*parser.Thrift, includes map[string]*parser.Thrift, isAbsIncludePath bool) error { 193 done[path] = tree 194 for _, i := range tree.Includes { 195 p := i.Path 196 if isAbsIncludePath { 197 p = absPath(tree.Filename, i.Path) 198 } 199 200 // check cycle reference 201 if t := done[p]; t != nil { 202 i.Reference = t 203 continue 204 } 205 206 ref, ok := includes[p] 207 if !ok { 208 return fmt.Errorf("miss include path: %s for file: %s", p, tree.Filename) 209 } 210 if err := refIncludes(ref, p, done, includes, isAbsIncludePath); err != nil { 211 return err 212 } 213 i.Reference = ref 214 } 215 return nil 216 } 217 218 // Parse descriptor from parser.Thrift 219 func parse(ctx context.Context, tree *parser.Thrift, mode meta.ParseServiceMode, opts Options, methods ...string) (*ServiceDescriptor, error) { 220 if len(tree.Services) == 0 { 221 return nil, errors.New("empty serverce from idls") 222 } 223 if err := semantic.ResolveSymbols(tree); err != nil { 224 return nil, err 225 } 226 227 sDsc := &ServiceDescriptor{ 228 functions: map[string]*FunctionDescriptor{}, 229 annotations: []parser.Annotation{}, 230 } 231 232 structsCache := compilingCache{} 233 234 // support one service 235 svcs := tree.Services 236 switch mode { 237 case meta.LastServiceOnly: 238 svcs = svcs[len(svcs)-1:] 239 sDsc.name = svcs[len(svcs)-1].Name 240 case meta.FirstServiceOnly: 241 svcs = svcs[:1] 242 sDsc.name = svcs[0].Name 243 case meta.CombineServices: 244 sDsc.name = "CombinedServices" 245 } 246 247 for _, svc := range svcs { 248 sopts := opts 249 // pass origin annotations 250 sDsc.annotations = copyAnnotationValues(svc.Annotations) 251 // handle thrid-party annotations 252 anns, _, next, err := mapAnnotations(ctx, svc.Annotations, AnnoScopeService, svc, sopts) 253 if err != nil { 254 return nil, err 255 } 256 for _, p := range anns { 257 if err := handleAnnotation(ctx, AnnoScopeService, p.inter, p.cont, &sopts, svcs); err != nil { 258 return nil, err 259 } 260 } 261 262 funcs := make([]funcTreePair, 0, len(svc.Functions)) 263 getAllFuncs(svc, tree, &funcs) 264 if len(methods) > 0 { 265 funcs = findFuncs(funcs, methods) 266 } 267 for _, p := range funcs { 268 injectAnnotations((*[]*parser.Annotation)(&p.fn.Annotations), next) 269 if err := addFunction(ctx, p.fn, p.tree, sDsc, structsCache, sopts); err != nil { 270 return nil, err 271 } 272 } 273 274 } 275 return sDsc, nil 276 } 277 278 type funcTreePair struct { 279 tree *parser.Thrift 280 fn *parser.Function 281 } 282 283 func findFuncs(funcs []funcTreePair, methods []string) (ret []funcTreePair) { 284 for _, m := range methods { 285 for _, p := range funcs { 286 if p.fn.Name == m { 287 ret = append(ret, p) 288 } 289 } 290 } 291 return 292 } 293 294 func getAllFuncs(svc *parser.Service, tree *parser.Thrift, ret *[]funcTreePair) { 295 funcs := *ret 296 n := len(svc.Functions) 297 l := len(funcs) 298 if cap(funcs) < l+n { 299 tmp := make([]funcTreePair, l, l+n) 300 copy(tmp, funcs) 301 funcs = tmp 302 } 303 304 for _, fn := range svc.Functions { 305 funcs = append(funcs, funcTreePair{ 306 tree: tree, 307 fn: fn, 308 }) 309 } 310 311 if svc.Extends != "" { 312 ref := svc.GetReference() 313 if ref != nil { 314 idx := ref.GetIndex() 315 name := ref.GetName() 316 subTree := tree.Includes[idx].Reference 317 sub, _ := subTree.GetService(name) 318 if sub != nil { 319 getAllFuncs(sub, subTree, &funcs) 320 } 321 } 322 } 323 *ret = funcs 324 return 325 } 326 327 func addFunction(ctx context.Context, fn *parser.Function, tree *parser.Thrift, sDsc *ServiceDescriptor, structsCache compilingCache, opts Options) error { 328 // for fuzzing test 329 if opts.ParseFieldRandomRate > 0 { 330 rand.Seed(time.Now().UnixNano()) 331 } 332 333 if sDsc.functions[fn.Name] != nil { 334 return fmt.Errorf("duplicate method name: %s", fn.Name) 335 } 336 if len(fn.Arguments) == 0 { 337 return fmt.Errorf("empty arguments in function: %s", fn.Name) 338 } 339 340 // find all endpoints of this function 341 enpdoints := []http.Endpoint{} 342 // for http router 343 for _, val := range fn.Annotations { 344 method := http.AnnoToMethod(val.Key) 345 if method != "" && len(val.Values) != 0 { 346 // record the pair{method,path} 347 enpdoints = append(enpdoints, http.Endpoint{Method: method, Path: val.Values[0]}) 348 } 349 } 350 351 annos := copyAnnotationValues(fn.Annotations) 352 // handle thrid-party annotations 353 anns, _, nextAnns, err := mapAnnotations(ctx, fn.Annotations, AnnoScopeFunction, fn, opts) 354 if err != nil { 355 return err 356 } 357 for _, p := range anns { 358 // handle thrid-party annotations 359 if err := handleAnnotation(ctx, AnnoScopeFunction, p.inter, p.cont, &opts, fn); err != nil { 360 return err 361 } 362 363 } 364 365 var hasRequestBase bool 366 var req *TypeDescriptor 367 var resp *TypeDescriptor 368 369 // parse request field 370 if opts.ParseFunctionMode != meta.ParseResponseOnly { 371 // WARN: only support single argument 372 reqAst := fn.Arguments[0] 373 req = &TypeDescriptor{ 374 typ: STRUCT, 375 struc: &StructDescriptor{ 376 baseID: FieldID(math.MaxUint16), 377 ids: FieldIDMap{}, 378 names: FieldNameMap{}, 379 requires: make(RequiresBitmap, 1), 380 }, 381 } 382 383 reqType, err := parseType(ctx, reqAst.Type, tree, structsCache, 0, opts, nextAnns, Request) 384 if err != nil { 385 return err 386 } 387 if reqType.Type() == STRUCT { 388 for _, f := range reqType.Struct().names.all { 389 x := (*FieldDescriptor)(f.Val) 390 if x.isRequestBase { 391 hasRequestBase = true 392 break 393 } 394 } 395 } 396 reqField := &FieldDescriptor{ 397 name: reqAst.Name, 398 id: FieldID(reqAst.ID), 399 typ: reqType, 400 } 401 req.Struct().ids.Set(FieldID(reqAst.ID), reqField) 402 req.Struct().names.Set(reqAst.Name, reqField) 403 req.Struct().names.Build() 404 } 405 406 // parse response filed 407 if opts.ParseFunctionMode != meta.ParseRequestOnly { 408 respAst := fn.FunctionType 409 resp = &TypeDescriptor{ 410 typ: STRUCT, 411 struc: &StructDescriptor{ 412 baseID: FieldID(math.MaxUint16), 413 ids: FieldIDMap{}, 414 names: FieldNameMap{}, 415 requires: make(RequiresBitmap, 1), 416 }, 417 } 418 respType, err := parseType(ctx, respAst, tree, structsCache, 0, opts, nextAnns, Response) 419 if err != nil { 420 return err 421 } 422 respField := &FieldDescriptor{ 423 typ: respType, 424 } 425 resp.Struct().ids.Set(0, respField) 426 // response has no name or id 427 resp.Struct().names.Set("", respField) 428 429 // parse exceptions 430 if len(fn.Throws) > 0 { 431 // only support single exception 432 exp := fn.Throws[0] 433 exceptionType, err := parseType(ctx, exp.Type, tree, structsCache, 0, opts, nextAnns, Exception) 434 if err != nil { 435 return err 436 } 437 exceptionField := &FieldDescriptor{ 438 name: exp.Name, 439 alias: exp.Name, 440 id: FieldID(exp.ID), 441 // isException: true, 442 typ: exceptionType, 443 } 444 resp.Struct().ids.Set(FieldID(exp.ID), exceptionField) 445 resp.Struct().names.Set(exp.Name, exceptionField) 446 } 447 resp.Struct().names.Build() 448 } 449 450 fnDsc := &FunctionDescriptor{ 451 name: fn.Name, 452 oneway: fn.Oneway, 453 request: req, 454 response: resp, 455 hasRequestBase: hasRequestBase, 456 endpoints: enpdoints, 457 annotations: annos, 458 } 459 sDsc.functions[fn.Name] = fnDsc 460 return nil 461 } 462 463 // reuse builtin types 464 var builtinTypes = map[string]*TypeDescriptor{ 465 "void": {name: "void", typ: VOID, struc: new(StructDescriptor)}, 466 "bool": {name: "bool", typ: BOOL}, 467 "byte": {name: "byte", typ: BYTE}, 468 "i8": {name: "i8", typ: I08}, 469 "i16": {name: "i16", typ: I16}, 470 "i32": {name: "i32", typ: I32}, 471 "i64": {name: "i64", typ: I64}, 472 "double": {name: "double", typ: DOUBLE}, 473 "string": {name: "string", typ: STRING}, 474 "binary": {name: "binary", typ: STRING}, 475 } 476 477 type compilingInstance struct { 478 desc *TypeDescriptor 479 opts Options 480 parseTarget ParseTarget 481 } 482 483 type compilingCache map[string]*compilingInstance 484 485 // arg cache: 486 // only support self reference on the same file 487 // cross file self reference complicate matters 488 func parseType(ctx context.Context, t *parser.Type, tree *parser.Thrift, cache compilingCache, recursionDepth int, opts Options, nextAnns []parser.Annotation, parseTarget ParseTarget) (*TypeDescriptor, error) { 489 if ty, ok := builtinTypes[t.Name]; ok { 490 return ty, nil 491 } 492 493 nextRecursionDepth := recursionDepth + 1 494 495 var err error 496 switch t.Name { 497 case "list": 498 ty := &TypeDescriptor{name: t.Name} 499 ty.typ = LIST 500 ty.elem, err = parseType(ctx, t.ValueType, tree, cache, nextRecursionDepth, opts, nextAnns, parseTarget) 501 return ty, err 502 case "set": 503 ty := &TypeDescriptor{name: t.Name} 504 ty.typ = SET 505 ty.elem, err = parseType(ctx, t.ValueType, tree, cache, nextRecursionDepth, opts, nextAnns, parseTarget) 506 return ty, err 507 case "map": 508 ty := &TypeDescriptor{name: t.Name} 509 ty.typ = MAP 510 if ty.key, err = parseType(ctx, t.KeyType, tree, cache, nextRecursionDepth, opts, nextAnns, parseTarget); err != nil { 511 return nil, err 512 } 513 ty.elem, err = parseType(ctx, t.ValueType, tree, cache, nextRecursionDepth, opts, nextAnns, parseTarget) 514 return ty, err 515 default: 516 // check the cache 517 if ty, ok := cache[t.Name]; ok && ty.parseTarget == parseTarget { 518 return ty.desc, nil 519 } 520 521 // get type from AST tree 522 typePkg, typeName := util.SplitSubfix(t.Name) 523 if typePkg != "" { 524 ref, ok := tree.GetReference(typePkg) 525 if !ok { 526 return nil, fmt.Errorf("miss reference: %s", typePkg) 527 } 528 tree = ref 529 // cross file reference need empty cache 530 cache = compilingCache{} 531 } 532 if typDef, ok := tree.GetTypedef(typeName); ok { 533 return parseType(ctx, typDef.Type, tree, cache, nextRecursionDepth, opts, nextAnns, parseTarget) 534 } 535 if _, ok := tree.GetEnum(typeName); ok { 536 if opts.ParseEnumAsInt64 { 537 return builtinTypes["i64"], nil 538 } 539 return builtinTypes["i32"], nil 540 } 541 542 // handle STRUCT type 543 var st *parser.StructLike 544 var ok bool 545 st, ok = tree.GetUnion(typeName) 546 if !ok { 547 st, ok = tree.GetStruct(typeName) 548 } 549 if !ok { 550 st, ok = tree.GetException(typeName) 551 } 552 if !ok { 553 return nil, fmt.Errorf("missing type: %s", typeName) 554 } 555 556 // copy original annotations 557 oannos := copyAnnotationValues(st.Annotations) 558 if opts.PutNameSpaceToAnnotation { 559 oannos = append(oannos, extractNameSpaceToAnnos(tree)) 560 } 561 562 // inject previous annotations 563 injectAnnotations((*[]*parser.Annotation)(&st.Annotations), nextAnns) 564 565 // make struct descriptor 566 ty := &TypeDescriptor{ 567 name: t.Name, 568 typ: STRUCT, 569 struc: &StructDescriptor{ 570 baseID: FieldID(math.MaxUint16), 571 name: typeName, 572 ids: FieldIDMap{}, 573 names: FieldNameMap{}, 574 requires: make(RequiresBitmap, len(st.Fields)), 575 annotations: oannos, 576 }, 577 } 578 579 if recursionDepth == 0 { 580 ctx = context.WithValue(ctx, CtxKeyIsBodyRoot, true) 581 } else { 582 ctx = context.WithValue(ctx, CtxKeyIsBodyRoot, false) 583 } 584 585 // handle thrid-party annotations for struct itself 586 anns, _, nextAnns2, err := mapAnnotations(ctx, st.Annotations, AnnoScopeStruct, st, opts) 587 if err != nil { 588 return nil, err 589 } 590 for _, p := range anns { 591 if err := handleAnnotation(ctx, AnnoScopeStruct, p.inter, p.cont, &opts, st); err != nil { 592 return nil, err 593 } 594 } 595 if st := ty.Struct(); st != nil { 596 cache[t.Name] = &compilingInstance{parseTarget: parseTarget, desc: ty} 597 } 598 599 // parse fields 600 for _, field := range st.Fields { 601 // fork field-specific options 602 fopts := opts 603 if fopts.ParseFieldRandomRate > 0 && rand.Float64() > fopts.ParseFieldRandomRate { 604 continue 605 } 606 var isRequestBase, isResponseBase bool 607 if fopts.EnableThriftBase { 608 isRequestBase = field.Type.Name == "base.Base" && recursionDepth == 0 609 isResponseBase = field.Type.Name == "base.BaseResp" && recursionDepth == 0 610 } 611 // cannot cache the request base 612 if isRequestBase { 613 delete(cache, t.Name) 614 } 615 if isRequestBase || isResponseBase { 616 ty.struc.baseID = FieldID(field.ID) 617 } 618 _f := &FieldDescriptor{ 619 isRequestBase: isRequestBase, 620 isResponseBase: isResponseBase, 621 id: FieldID(field.ID), 622 name: field.Name, 623 alias: field.Name, 624 annotations: copyAnnotationValues(field.Annotations), 625 } 626 627 // inject previous annotations 628 injectAnnotations((*[]*parser.Annotation)(&field.Annotations), nextAnns2) 629 anns, left, _, err := mapAnnotations(ctx, field.Annotations, AnnoScopeField, field, fopts) 630 if err != nil { 631 return nil, err 632 } 633 634 // handle annotations 635 ignore := false 636 for _, val := range left { 637 skip, err := handleNativeFieldAnnotation(val, _f, parseTarget) 638 if err != nil { 639 return nil, err 640 } 641 if skip { 642 ignore = true 643 break 644 } 645 } 646 if ignore { 647 continue 648 } 649 for _, p := range anns { 650 if err := handleFieldAnnotation(ctx, p.inter, p.cont, &fopts, _f, ty.struc, field); err != nil { 651 return nil, err 652 } 653 } 654 // copyAnnos(_f.annotations, anns) 655 656 // recursively parse field type 657 // WARN: options and annotations on field SHOULD NOT override these on their type definition 658 if _f.typ, err = parseType(ctx, field.Type, tree, cache, nextRecursionDepth, opts, nil, parseTarget); err != nil { 659 return nil, err 660 } 661 662 // make default value 663 // WARN: ignore errors here 664 if fopts.UseDefaultValue { 665 dv, _ := makeDefaultValue(_f.typ, field.Default, tree) 666 _f.defaultValue = dv 667 } 668 // set field id 669 ty.Struct().ids.Set(FieldID(field.ID), _f) 670 // set field requireness 671 convertRequireness(field.Requiredness, ty.struc, _f, fopts) 672 // set field key 673 if fopts.MapFieldWay == meta.MapFieldUseAlias { 674 ty.Struct().names.Set(_f.alias, _f) 675 } else if fopts.MapFieldWay == meta.MapFieldUseFieldName { 676 ty.Struct().names.Set(_f.name, _f) 677 } else { 678 ty.Struct().names.Set(_f.alias, _f) 679 ty.Struct().names.Set(_f.name, _f) 680 } 681 682 } 683 // buidl field name map 684 ty.Struct().names.Build() 685 return ty, nil 686 } 687 } 688 689 func convertRequireness(r parser.FieldType, st *StructDescriptor, f *FieldDescriptor, opts Options) { 690 var req Requireness 691 switch r { 692 case parser.FieldType_Default: 693 f.required = DefaultRequireness 694 if opts.SetOptionalBitmap { 695 req = RequiredRequireness 696 } else { 697 req = DefaultRequireness 698 } 699 case parser.FieldType_Optional: 700 f.required = OptionalRequireness 701 if opts.SetOptionalBitmap { 702 req = DefaultRequireness 703 } else { 704 req = OptionalRequireness 705 } 706 case parser.FieldType_Required: 707 f.required = RequiredRequireness 708 req = RequiredRequireness 709 default: 710 panic("invalid requireness type") 711 } 712 713 if f.isRequestBase || f.isResponseBase { 714 // hence users who set EnableThriftBase can pass thrift base through conversion context, the field is always set optional 715 req = OptionalRequireness 716 } 717 718 st.requires.Set(f.id, req) 719 } 720 721 func assertType(expected, but Type) error { 722 if expected == but { 723 return nil 724 } 725 return fmt.Errorf("need %s type, but got: %s", expected, but) 726 } 727 728 func makeDefaultValue(typ *TypeDescriptor, val *parser.ConstValue, tree *parser.Thrift) (*DefaultValue, error) { 729 if val == nil { 730 return nil, nil 731 } 732 switch val.Type { 733 case parser.ConstType_ConstInt: 734 if !typ.typ.IsInt() { 735 return nil, fmt.Errorf("mismatched int default value with type %s", typ.name) 736 } 737 if x := val.TypedValue.Int; x != nil { 738 v := int64(*x) 739 p := BinaryProtocol{Buf: make([]byte, 0, 4)} 740 p.WriteInt(typ.typ, int(v)) 741 jbuf := json.EncodeInt64(make([]byte, 0, 8), v) 742 return &DefaultValue{ 743 goValue: v, 744 jsonValue: rt.Mem2Str(jbuf), 745 thriftBinary: rt.Mem2Str(p.Buf), 746 }, nil 747 } 748 case parser.ConstType_ConstDouble: 749 if typ.typ != DOUBLE { 750 return nil, fmt.Errorf("mismatched double default value with type %s", typ.name) 751 } 752 if x := val.TypedValue.Double; x != nil { 753 v := float64(*x) 754 tbuf := make([]byte, 8) 755 BinaryEncoding{}.EncodeDouble(tbuf, v) 756 jbuf := json.EncodeFloat64(make([]byte, 0, 8), v) 757 return &DefaultValue{ 758 goValue: v, 759 jsonValue: rt.Mem2Str(jbuf), 760 thriftBinary: rt.Mem2Str(tbuf), 761 }, nil 762 } 763 case parser.ConstType_ConstLiteral: 764 if typ.typ != STRING { 765 return nil, fmt.Errorf("mismatched string default value with type %s", typ.name) 766 } 767 if x := val.TypedValue.Literal; x != nil { 768 v := string(*x) 769 tbuf := make([]byte, len(v)+4) 770 BinaryEncoding{}.EncodeString(tbuf, v) 771 jbuf := json.EncodeString(make([]byte, 0, len(v)+2), v) 772 return &DefaultValue{ 773 goValue: v, 774 jsonValue: rt.Mem2Str(jbuf), 775 thriftBinary: rt.Mem2Str(tbuf), 776 }, nil 777 } 778 case parser.ConstType_ConstIdentifier: 779 x := *val.TypedValue.Identifier 780 781 // try to handle it as bool 782 if typ.typ == BOOL { 783 if v := strings.ToLower(x); v == "true" { 784 return &DefaultValue{ 785 goValue: true, 786 jsonValue: "true", 787 thriftBinary: string([]byte{0x01}), 788 }, nil 789 } else if v == "false" { 790 return &DefaultValue{ 791 goValue: false, 792 jsonValue: "false", 793 thriftBinary: string([]byte{0x00}), 794 }, nil 795 } 796 } 797 798 // try to handle as const value 799 var ctree = tree 800 pkg, name := util.SplitSubfix(x) 801 if pkg != "" { 802 if nt, ok := tree.GetReference(pkg); ok && nt != nil { 803 ctree = nt 804 } 805 } 806 y, ok := ctree.GetConstant(name) 807 if ok && y != nil { 808 return makeDefaultValue(typ, y.Value, ctree) 809 } 810 811 // try to handle as enum value 812 if pkg == "" { 813 return nil, fmt.Errorf("not found identifier: %s", x) 814 } 815 emv := name 816 emp, emt := util.SplitSubfix(pkg) 817 if emp != "" { 818 if nt, ok := tree.GetReference(emp); ok && nt != nil { 819 tree = nt 820 } 821 } 822 z, ok := tree.GetEnum(emt) 823 if !ok || z == nil { 824 return nil, fmt.Errorf("not found enum type: %s", emt) 825 } 826 if !typ.typ.IsInt() { 827 return nil, fmt.Errorf("mismatched int default value with type %s", typ.name) 828 } 829 for _, vv := range z.Values { 830 if vv.Name == emv { 831 v := int64(vv.Value) 832 p := BinaryProtocol{Buf: make([]byte, 0, 4)} 833 p.WriteInt(typ.typ, int(v)) 834 jbuf := json.EncodeInt64(make([]byte, 0, 8), v) 835 return &DefaultValue{ 836 goValue: v, 837 jsonValue: rt.Mem2Str(jbuf), 838 thriftBinary: rt.Mem2Str(p.Buf), 839 }, nil 840 } 841 } 842 843 } 844 return nil, nil 845 }