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