1package main 2 3import ( 4 "bytes" 5 "fmt" 6 "log" 7 "os" 8 "strconv" 9 "strings" 10 11 "github.com/lestrrat-go/codegen" 12 "github.com/pkg/errors" 13) 14 15const ( 16 byteSliceType = "[]byte" 17) 18 19func main() { 20 if err := _main(); err != nil { 21 log.Printf("%s", err) 22 os.Exit(1) 23 } 24} 25 26var tokens []tokenType 27 28func _main() error { 29 for _, t := range tokens { 30 if err := generateToken(t); err != nil { 31 return errors.Wrapf(err, `failed to generate token file %s`, t.filename) 32 } 33 } 34 return nil 35} 36 37type tokenField struct { 38 name string 39 method string 40 returnType string 41 key string 42 typ string 43 Comment string 44 elemtyp string 45 tag string 46 isList bool 47 hasAccept bool 48 hasGet bool 49 noDeref bool 50} 51 52func (t tokenField) Tag() string { 53 if len(t.tag) > 0 { 54 return t.tag 55 } 56 57 return `json:"` + t.key + `,omitempty"` 58} 59 60func (t tokenField) IsList() bool { 61 return t.isList || strings.HasPrefix(t.typ, `[]`) 62} 63 64func (t tokenField) ListElem() string { 65 if t.elemtyp != "" { 66 return t.elemtyp 67 } 68 return strings.TrimPrefix(t.typ, `[]`) 69} 70 71func (t tokenField) IsPointer() bool { 72 return strings.HasPrefix(t.typ, `*`) 73} 74 75func (t tokenField) PointerElem() string { 76 return strings.TrimPrefix(t.typ, `*`) 77} 78 79var zerovals = map[string]string{ 80 `string`: `""`, 81 `time.Time`: `time.Time{}`, 82 `bool`: `false`, 83} 84 85func zeroval(s string) string { 86 if v, ok := zerovals[s]; ok { 87 return v 88 } 89 return `nil` 90} 91 92func fieldStorageType(s string) string { 93 if fieldStorageTypeIsIndirect(s) { 94 return `*` + s 95 } 96 return s 97} 98 99func fieldStorageTypeIsIndirect(s string) bool { 100 return !(strings.HasPrefix(s, `*`) || strings.HasPrefix(s, `[]`) || strings.HasSuffix(s, `List`)) 101} 102 103var stdFields []tokenField 104 105type tokenType struct { 106 filename string 107 structName string 108 ifName string 109 pkg string 110 claims []tokenField 111} 112 113func init() { 114 stdFields = []tokenField{ 115 { 116 name: "audience", 117 method: "Audience", 118 returnType: "[]string", 119 key: "aud", 120 typ: "types.StringList", 121 Comment: `https://tools.ietf.org/html/rfc7519#section-4.1.3`, 122 isList: true, 123 hasAccept: true, 124 hasGet: true, 125 elemtyp: `string`, 126 }, 127 { 128 name: "expiration", 129 method: "Expiration", 130 returnType: "time.Time", 131 key: "exp", 132 typ: "types.NumericDate", 133 Comment: `https://tools.ietf.org/html/rfc7519#section-4.1.4`, 134 hasAccept: true, 135 hasGet: true, 136 noDeref: true, 137 }, 138 { 139 name: "issuedAt", 140 method: "IssuedAt", 141 returnType: "time.Time", 142 key: "iat", 143 typ: "types.NumericDate", 144 Comment: `https://tools.ietf.org/html/rfc7519#section-4.1.6`, 145 hasAccept: true, 146 hasGet: true, 147 noDeref: true, 148 }, 149 { 150 name: "issuer", 151 method: "Issuer", 152 returnType: "string", 153 key: "iss", 154 typ: "string", 155 Comment: `https://tools.ietf.org/html/rfc7519#section-4.1.1`, 156 }, 157 { 158 name: "jwtID", 159 method: "JwtID", 160 returnType: "string", 161 key: "jti", 162 typ: "string", 163 Comment: `https://tools.ietf.org/html/rfc7519#section-4.1.7`, 164 }, 165 { 166 name: "notBefore", 167 method: "NotBefore", 168 returnType: "time.Time", 169 key: "nbf", 170 typ: "types.NumericDate", 171 Comment: `https://tools.ietf.org/html/rfc7519#section-4.1.5`, 172 hasAccept: true, 173 hasGet: true, 174 noDeref: true, 175 }, 176 { 177 name: "subject", 178 method: "Subject", 179 returnType: "string", 180 key: "sub", 181 typ: "string", 182 Comment: `https://tools.ietf.org/html/rfc7519#section-4.1.2`, 183 }, 184 } 185 186 tokens = []tokenType{ 187 { 188 pkg: "jwt", 189 filename: "token_gen.go", 190 ifName: "Token", 191 structName: "stdToken", 192 claims: stdFields, 193 }, 194 { 195 pkg: "openid", 196 filename: "openid/token_gen.go", 197 ifName: "Token", 198 structName: "stdToken", 199 claims: append(stdFields, []tokenField{ 200 { 201 name: "name", 202 method: "Name", 203 returnType: "string", 204 typ: "string", 205 key: "name", 206 }, 207 { 208 name: "givenName", 209 method: "GivenName", 210 returnType: "string", 211 typ: "string", 212 key: "given_name", 213 }, 214 { 215 name: "middleName", 216 method: "MiddleName", 217 returnType: "string", 218 typ: "string", 219 key: "middle_name", 220 }, 221 { 222 name: "familyName", 223 method: "FamilyName", 224 returnType: "string", 225 typ: "string", 226 key: "family_name", 227 }, 228 { 229 name: "nickname", 230 method: "Nickname", 231 returnType: "string", 232 typ: "string", 233 key: "nickname", 234 }, 235 { 236 name: "preferredUsername", 237 method: "PreferredUsername", 238 returnType: "string", 239 typ: "string", 240 key: "preferred_username", 241 }, 242 { 243 name: "profile", 244 method: "Profile", 245 returnType: "string", 246 typ: "string", 247 key: "profile", 248 }, 249 { 250 name: "picture", 251 method: "Picture", 252 returnType: "string", 253 typ: "string", 254 key: "picture", 255 }, 256 { 257 name: "website", 258 method: "Website", 259 returnType: "string", 260 typ: "string", 261 key: "website", 262 }, 263 { 264 name: "email", 265 method: "Email", 266 returnType: "string", 267 typ: "string", 268 key: "email", 269 }, 270 { 271 name: "emailVerified", 272 method: "EmailVerified", 273 returnType: "bool", 274 typ: "bool", 275 key: "email_verified", 276 }, 277 { 278 name: "gender", 279 method: "Gender", 280 returnType: "string", 281 typ: "string", 282 key: "gender", 283 }, 284 { 285 name: "birthdate", 286 method: "Birthdate", 287 returnType: "*BirthdateClaim", 288 typ: "*BirthdateClaim", 289 key: "birthdate", 290 hasAccept: true, 291 }, 292 { 293 name: "zoneinfo", 294 method: "Zoneinfo", 295 returnType: "string", 296 typ: "string", 297 key: "zoneinfo", 298 }, 299 { 300 name: "locale", 301 method: "Locale", 302 returnType: "string", 303 typ: "string", 304 key: "locale", 305 }, 306 { 307 name: "phoneNumber", 308 method: "PhoneNumber", 309 returnType: "string", 310 typ: "string", 311 key: "phone_number", 312 }, 313 { 314 name: "phoneNumberVerified", 315 method: "PhoneNumberVerified", 316 returnType: "bool", 317 typ: "bool", 318 key: "phone_number_verified", 319 }, 320 { 321 name: "address", 322 method: "Address", 323 returnType: "*AddressClaim", 324 typ: "*AddressClaim", 325 key: "address", 326 hasAccept: true, 327 }, 328 { 329 name: "updatedAt", 330 method: "UpdatedAt", 331 returnType: "time.Time", 332 typ: "types.NumericDate", 333 key: "updated_at", 334 hasGet: true, 335 hasAccept: true, 336 }, 337 }...), 338 }, 339 } 340} 341 342func generateToken(tt tokenType) error { 343 var buf bytes.Buffer 344 345 var fields = tt.claims 346 347 o := codegen.NewOutput(&buf) 348 o.L("// This file is auto-generated by jwt/internal/cmd/gentoken/main.go. DO NOT EDIT") 349 o.LL("package %s", tt.pkg) 350 351 o.LL("const (") 352 for _, f := range fields { 353 o.L("%sKey = %s", f.method, strconv.Quote(f.key)) 354 } 355 o.L(")") // end const 356 357 if tt.pkg == "jwt" && tt.structName == "stdToken" { 358 o.LL("// Token represents a generic JWT token.") 359 o.L("// which are type-aware (to an extent). Other claims may be accessed via the `Get`/`Set`") 360 o.L("// methods but their types are not taken into consideration at all. If you have non-standard") 361 o.L("// claims that you must frequently access, consider creating accessors functions") 362 o.L("// like the following") 363 o.L("//\n// func SetFoo(tok jwt.Token) error") 364 o.L("// func GetFoo(tok jwt.Token) (*Customtyp, error)") 365 o.L("//\n// Embedding jwt.Token into another struct is not recommended, because") 366 o.L("// jwt.Token needs to handle private claims, and this really does not") 367 o.L("// work well when it is embedded in other structure") 368 } 369 370 o.L("type %s interface {", tt.ifName) 371 for _, field := range fields { 372 o.L("%s() %s", field.method, field.returnType) 373 } 374 o.L("PrivateClaims() map[string]interface{}") 375 o.L("Get(string) (interface{}, bool)") 376 o.L("Set(string, interface{}) error") 377 o.L("Remove(string) error") 378 if tt.pkg != "jwt" { 379 o.L("Clone() (jwt.Token, error)") 380 } else { 381 o.L("Clone() (Token, error)") 382 } 383 o.L("Iterate(context.Context) Iterator") 384 o.L("Walk(context.Context, Visitor) error") 385 o.L("AsMap(context.Context) (map[string]interface{}, error)") 386 o.L("}") 387 388 o.L("type %s struct {", tt.structName) 389 o.L("mu *sync.RWMutex") 390 o.L("dc DecodeCtx // per-object context for decoding") 391 for _, f := range fields { 392 o.L("%s %s // %s", f.name, fieldStorageType(f.typ), f.Comment) 393 } 394 o.L("privateClaims map[string]interface{}") 395 o.L("}") // end type Token 396 397 o.LL("// New creates a standard token, with minimal knowledge of") 398 o.L("// possible claims. Standard claims include") 399 for i, field := range fields { 400 o.R("%s", strconv.Quote(field.key)) 401 switch { 402 case i < len(fields)-2: 403 o.R(", ") 404 case i == len(fields)-2: 405 o.R(" and ") 406 } 407 } 408 o.R(".\n// Convenience accessors are provided for these standard claims") 409 o.L("func New() %s {", tt.ifName) 410 o.L("return &%s{", tt.structName) 411 o.L("mu: &sync.RWMutex{},") 412 o.L("privateClaims: make(map[string]interface{}),") 413 o.L("}") 414 o.L("}") 415 416 o.LL("func (t *%s) Get(name string) (interface{}, bool) {", tt.structName) 417 o.L("t.mu.RLock()") 418 o.L("defer t.mu.RUnlock()") 419 o.L("switch name {") 420 for _, f := range fields { 421 o.L("case %sKey:", f.method) 422 o.L("if t.%s == nil {", f.name) 423 o.L("return nil, false") 424 o.L("}") 425 if f.hasGet { 426 o.L("v := t.%s.Get()", f.name) 427 } else { 428 if fieldStorageTypeIsIndirect(f.typ) { 429 o.L("v := *(t.%s)", f.name) 430 } else { 431 o.L("v := t.%s", f.name) 432 } 433 } 434 o.L("return v, true") 435 } 436 o.L("default:") 437 o.L("v, ok := t.privateClaims[name]") 438 o.L("return v, ok") 439 o.L("}") // end switch name 440 o.L("}") // end of Get 441 442 o.LL("func (t *stdToken) Remove(key string) error {") 443 o.L("t.mu.Lock()") 444 o.L("defer t.mu.Unlock()") 445 o.L("switch key {") 446 for _, f := range fields { 447 o.L("case %sKey:", f.method) 448 o.L("t.%s = nil", f.name) 449 } 450 o.L("default:") 451 o.L("delete(t.privateClaims, key)") 452 o.L("}") 453 o.L("return nil") // currently unused, but who knows 454 o.L("}") 455 456 o.LL("func (t *%s) Set(name string, value interface{}) error {", tt.structName) 457 o.L("t.mu.Lock()") 458 o.L("defer t.mu.Unlock()") 459 o.L("return t.setNoLock(name, value)") 460 o.L("}") 461 462 o.LL("func (t *%s) DecodeCtx() DecodeCtx {", tt.structName) 463 o.L("t.mu.RLock()") 464 o.L("defer t.mu.RUnlock()") 465 o.L("return t.dc") 466 o.L("}") 467 468 o.LL("func (t *%s) SetDecodeCtx(v DecodeCtx) {", tt.structName) 469 o.L("t.mu.Lock()") 470 o.L("defer t.mu.Unlock()") 471 o.L("t.dc = v") 472 o.L("}") 473 474 o.LL("func (t *%s) setNoLock(name string, value interface{}) error {", tt.structName) 475 o.L("switch name {") 476 for _, f := range fields { 477 keyName := f.method + "Key" 478 o.L("case %s:", keyName) 479 if f.name == `algorithm` { 480 o.L("switch v := value.(type) {") 481 o.L("case string:") 482 o.L("t.algorithm = &v") 483 o.L("case fmt.Stringer:") 484 o.L("tmp := v.String()") 485 o.L("t.algorithm = &tmp") 486 o.L("default:") 487 o.L("return errors.Errorf(`invalid type for %%s key: %%T`, %s, value)", keyName) 488 o.L("}") 489 o.L("return nil") 490 } else if f.hasAccept { 491 if f.IsPointer() { 492 o.L("var acceptor %s", strings.TrimPrefix(f.typ, "*")) 493 } else { 494 o.L("var acceptor %s", f.typ) 495 } 496 497 o.L("if err := acceptor.Accept(value); err != nil {") 498 o.L("return errors.Wrapf(err, `invalid value for %%s key`, %s)", keyName) 499 o.L("}") // end if err := t.%s.Accept(value) 500 if fieldStorageTypeIsIndirect(f.typ) || f.IsPointer() { 501 o.L("t.%s = &acceptor", f.name) 502 } else { 503 o.L("t.%s = acceptor", f.name) 504 } 505 o.L("return nil") 506 } else { 507 o.L("if v, ok := value.(%s); ok {", f.typ) 508 if fieldStorageTypeIsIndirect(f.typ) { 509 o.L("t.%s = &v", f.name) 510 } else { 511 o.L("t.%s = v", f.name) 512 } 513 o.L("return nil") 514 o.L("}") // end if v, ok := value.(%s) 515 o.L("return errors.Errorf(`invalid value for %%s key: %%T`, %s, value)", keyName) 516 } 517 } 518 o.L("default:") 519 o.L("if t.privateClaims == nil {") 520 o.L("t.privateClaims = map[string]interface{}{}") 521 o.L("}") // end if t.privateClaims == nil 522 o.L("t.privateClaims[name] = value") 523 o.L("}") // end switch name 524 o.L("return nil") 525 o.L("}") // end func (t *%s) Set(name string, value interface{}) 526 527 for _, f := range fields { 528 o.LL("func (t *%s) %s() ", tt.structName, f.method) 529 if f.returnType != "" { 530 o.R("%s", f.returnType) 531 } else if f.IsPointer() && f.noDeref { 532 o.R("%s", f.typ) 533 } else { 534 o.R("%s", f.PointerElem()) 535 } 536 o.R(" {") 537 o.L("t.mu.RLock()") 538 o.L("defer t.mu.RUnlock()") 539 540 if f.hasGet { 541 o.L("if t.%s != nil {", f.name) 542 o.L("return t.%s.Get()", f.name) 543 o.L("}") 544 o.L("return %s", zeroval(f.returnType)) 545 } else if !f.IsPointer() { 546 if fieldStorageTypeIsIndirect(f.typ) { 547 o.L("if t.%s != nil {", f.name) 548 o.L("return *(t.%s)", f.name) 549 o.L("}") 550 o.L("return %s", zeroval(f.returnType)) 551 } else { 552 o.L("return t.%s", f.name) 553 } 554 } else { 555 o.L("return t.%s", f.name) 556 } 557 o.L("}") // func (h *stdHeaders) %s() %s 558 } 559 560 o.LL("func (t *%s) PrivateClaims() map[string]interface{} {", tt.structName) 561 o.L("t.mu.RLock()") 562 o.L("defer t.mu.RUnlock()") 563 o.L("return t.privateClaims") 564 o.L("}") 565 566 // Generate a function that iterates through all of the keys 567 // in this header. 568 o.LL("func (t *%s) makePairs() []*ClaimPair {", tt.structName) 569 o.L("t.mu.RLock()") 570 o.L("defer t.mu.RUnlock()") 571 572 // NOTE: building up an array is *slow*? 573 o.LL("pairs := make([]*ClaimPair, 0, %d)", len(fields)) 574 for _, f := range fields { 575 keyName := f.method + "Key" 576 o.L("if t.%s != nil {", f.name) 577 if f.hasGet { 578 o.L("v := t.%s.Get()", f.name) 579 } else { 580 if fieldStorageTypeIsIndirect(f.typ) { 581 o.L("v := *(t.%s)", f.name) 582 } else { 583 o.L("v := t.%s", f.name) 584 } 585 } 586 o.L("pairs = append(pairs, &ClaimPair{Key: %s, Value: v})", keyName) 587 o.L("}") 588 } 589 o.L("for k, v := range t.privateClaims {") 590 o.L("pairs = append(pairs, &ClaimPair{Key: k, Value: v})") 591 o.L("}") 592 o.L("sort.Slice(pairs, func(i, j int) bool {") 593 o.L("return pairs[i].Key.(string) < pairs[j].Key.(string)") 594 o.L("})") 595 o.L("return pairs") 596 o.L("}") // end of (h *stdHeaders) iterate(...) 597 598 o.LL("func (t *stdToken) UnmarshalJSON(buf []byte) error {") 599 o.L("t.mu.Lock()") 600 o.L("defer t.mu.Unlock()") 601 for _, f := range fields { 602 o.L("t.%s = nil", f.name) 603 } 604 605 o.L("dec := json.NewDecoder(bytes.NewReader(buf))") 606 o.L("LOOP:") 607 o.L("for {") 608 o.L("tok, err := dec.Token()") 609 o.L("if err != nil {") 610 o.L("return errors.Wrap(err, `error reading token`)") 611 o.L("}") 612 o.L("switch tok := tok.(type) {") 613 o.L("case json.Delim:") 614 o.L("// Assuming we're doing everything correctly, we should ONLY") 615 o.L("// get either '{' or '}' here.") 616 o.L("if tok == '}' { // End of object") 617 o.L("break LOOP") 618 o.L("} else if tok != '{' {") 619 o.L("return errors.Errorf(`expected '{', but got '%%c'`, tok)") 620 o.L("}") 621 o.L("case string: // Objects can only have string keys") 622 o.L("switch tok {") 623 624 for _, f := range fields { 625 if f.typ == "string" { 626 o.L("case %sKey:", f.method) 627 o.L("if err := json.AssignNextStringToken(&t.%s, dec); err != nil {", f.name) 628 o.L("return errors.Wrapf(err, `failed to decode value for key %%s`, %sKey)", f.method) 629 o.L("}") 630 } else if f.typ == byteSliceType { 631 name := f.method 632 o.L("case %sKey:", name) 633 o.L("if err := json.AssignNextBytesToken(&t.%s, dec); err != nil {", f.name) 634 o.L("return errors.Wrapf(err, `failed to decode value for key %%s`, %sKey)", name) 635 o.L("}") 636 } else if f.typ == "types.StringList" || strings.HasPrefix(f.typ, "[]") { 637 name := f.method 638 o.L("case %sKey:", name) 639 o.L("var decoded %s", f.typ) 640 o.L("if err := dec.Decode(&decoded); err != nil {") 641 o.L("return errors.Wrapf(err, `failed to decode value for key %%s`, %sKey)", name) 642 o.L("}") 643 o.L("t.%s = decoded", f.name) 644 } else { 645 name := f.method 646 o.L("case %sKey:", name) 647 if strings.HasPrefix(f.typ, "*") { 648 o.L("var decoded %s", f.typ[1:]) 649 } else { 650 o.L("var decoded %s", f.typ) 651 } 652 o.L("if err := dec.Decode(&decoded); err != nil {") 653 o.L("return errors.Wrapf(err, `failed to decode value for key %%s`, %sKey)", name) 654 o.L("}") 655 o.L("t.%s = &decoded", f.name) 656 } 657 } 658 o.L("default:") 659 // This looks like bad code, but we're unrolling things for maximum 660 // runtime efficiency 661 o.L("if dc := t.dc; dc != nil {") 662 o.L("if localReg := dc.Registry(); localReg != nil {") 663 o.L("decoded, err := localReg.Decode(dec, tok)") 664 o.L("if err == nil {") 665 o.L("t.setNoLock(tok, decoded)") 666 o.L("continue") 667 o.L("}") 668 o.L("}") 669 o.L("}") 670 671 o.L("decoded, err := registry.Decode(dec, tok)") 672 o.L("if err == nil {") 673 o.L("t.setNoLock(tok, decoded)") 674 o.L("continue") 675 o.L("}") 676 o.L("return errors.Wrapf(err, `could not decode field %%s`, tok)") 677 o.L("}") 678 o.L("default:") 679 o.L("return errors.Errorf(`invalid token %%T`, tok)") 680 o.L("}") 681 o.L("}") 682 683 o.L("return nil") 684 o.L("}") 685 686 var numericDateFields []tokenField 687 for _, field := range fields { 688 if field.typ == "types.NumericDate" { 689 numericDateFields = append(numericDateFields, field) 690 } 691 } 692 693 o.LL("func (t %s) MarshalJSON() ([]byte, error) {", tt.structName) 694 o.L("t.mu.RLock()") 695 o.L("defer t.mu.RUnlock()") 696 o.L("buf := pool.GetBytesBuffer()") 697 o.L("defer pool.ReleaseBytesBuffer(buf)") 698 o.L("buf.WriteByte('{')") 699 o.L("enc := json.NewEncoder(buf)") 700 o.L("for i, pair := range t.makePairs() {") 701 o.L("f := pair.Key.(string)") 702 o.L("if i > 0 {") 703 o.L("buf.WriteByte(',')") 704 o.L("}") 705 o.L("buf.WriteRune('\"')") 706 o.L("buf.WriteString(f)") 707 o.L("buf.WriteString(`\":`)") 708 709 // Handle cases that need specialized handling 710 o.L("switch f {") 711 o.L("case AudienceKey:") 712 o.L("if err := json.EncodeAudience(enc, pair.Value.([]string)); err != nil {") 713 o.L("return nil, errors.Wrap(err, `failed to encode \"aud\"`)") 714 o.L("}") 715 o.L("continue") 716 if lndf := len(numericDateFields); lndf > 0 { 717 o.L("case ") 718 for i, ndf := range numericDateFields { 719 o.R("%sKey", ndf.method) 720 if i < lndf-1 { 721 o.R(",") 722 } 723 } 724 o.R(":") 725 o.L("enc.Encode(pair.Value.(time.Time).Unix())") 726 o.L("continue") 727 } 728 o.L("}") 729 730 o.L("switch v := pair.Value.(type) {") 731 o.L("case []byte:") 732 o.L("buf.WriteRune('\"')") 733 o.L("buf.WriteString(base64.EncodeToString(v))") 734 o.L("buf.WriteRune('\"')") 735 o.L("default:") 736 o.L("if err := enc.Encode(v); err != nil {") 737 o.L("return nil, errors.Wrapf(err, `failed to marshal field %%s`, f)") 738 o.L("}") 739 o.L("buf.Truncate(buf.Len()-1)") 740 o.L("}") 741 o.L("}") 742 o.L("buf.WriteByte('}')") 743 o.L("ret := make([]byte, buf.Len())") 744 o.L("copy(ret, buf.Bytes())") 745 o.L("return ret, nil") 746 o.L("}") 747 748 o.LL("func (t *%s) Iterate(ctx context.Context) Iterator {", tt.structName) 749 o.L("pairs := t.makePairs()") 750 o.L("ch := make(chan *ClaimPair, len(pairs))") 751 o.L("go func(ctx context.Context, ch chan *ClaimPair, pairs []*ClaimPair) {") 752 o.L("defer close(ch)") 753 o.L("for _, pair := range pairs {") 754 o.L("select {") 755 o.L("case <-ctx.Done():") 756 o.L("return") 757 o.L("case ch<-pair:") 758 o.L("}") 759 o.L("}") 760 o.L("}(ctx, ch, pairs)") 761 o.L("return mapiter.New(ch)") 762 o.L("}") 763 764 o.LL("func (t *%s) Walk(ctx context.Context, visitor Visitor) error {", tt.structName) 765 o.L("return iter.WalkMap(ctx, t, visitor)") 766 o.L("}") 767 768 o.LL("func (t *%s) AsMap(ctx context.Context) (map[string]interface{}, error) {", tt.structName) 769 o.L("return iter.AsMap(ctx, t)") 770 o.L("}") 771 772 if err := o.WriteFile(tt.filename, codegen.WithFormatCode(true)); err != nil { 773 if cfe, ok := err.(codegen.CodeFormatError); ok { 774 fmt.Fprint(os.Stderr, cfe.Source()) 775 } 776 return errors.Wrapf(err, `failed to write to %s`, tt.filename) 777 } 778 return nil 779} 780