github.com/ipld/go-ipld-prime@v0.21.0/schema/gen/go/genStructReprMap.go (about)

     1  package gengo
     2  
     3  import (
     4  	"io"
     5  	"strconv"
     6  
     7  	"github.com/ipld/go-ipld-prime/schema"
     8  	"github.com/ipld/go-ipld-prime/schema/gen/go/mixins"
     9  )
    10  
    11  var _ TypeGenerator = &structReprMapGenerator{}
    12  
    13  func NewStructReprMapGenerator(pkgName string, typ *schema.TypeStruct, adjCfg *AdjunctCfg) TypeGenerator {
    14  	return structReprMapGenerator{
    15  		structGenerator{
    16  			adjCfg,
    17  			mixins.MapTraits{
    18  				PkgName:    pkgName,
    19  				TypeName:   string(typ.Name()),
    20  				TypeSymbol: adjCfg.TypeSymbol(typ),
    21  			},
    22  			pkgName,
    23  			typ,
    24  		},
    25  	}
    26  }
    27  
    28  type structReprMapGenerator struct {
    29  	structGenerator
    30  }
    31  
    32  func (g structReprMapGenerator) GetRepresentationNodeGen() NodeGenerator {
    33  	return structReprMapReprGenerator{
    34  		g.AdjCfg,
    35  		mixins.MapTraits{
    36  			PkgName:    g.PkgName,
    37  			TypeName:   string(g.Type.Name()) + ".Repr",
    38  			TypeSymbol: "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr",
    39  		},
    40  		g.PkgName,
    41  		g.Type,
    42  	}
    43  }
    44  
    45  type structReprMapReprGenerator struct {
    46  	AdjCfg *AdjunctCfg
    47  	mixins.MapTraits
    48  	PkgName string
    49  	Type    *schema.TypeStruct
    50  }
    51  
    52  func (structReprMapReprGenerator) IsRepr() bool { return true } // hint used in some generalized templates.
    53  
    54  func (g structReprMapReprGenerator) EmitNodeType(w io.Writer) {
    55  	// The type is structurally the same, but will have a different set of methods.
    56  	doTemplate(`
    57  		type _{{ .Type | TypeSymbol }}__Repr _{{ .Type | TypeSymbol }}
    58  	`, w, g.AdjCfg, g)
    59  
    60  	// We do also want some constants for our fields;
    61  	//  they'll make iterators able to work faster.
    62  	//  These might be the same strings as the type-level field names
    63  	//   (in fact, they are, unless renames are used)... but that's fine.
    64  	//    We get simpler code by just doing this unconditionally.
    65  	doTemplate(`
    66  		var (
    67  			{{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}}
    68  			{{- range $field := .Type.Fields }}
    69  			fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}_serial = _String{"{{ $field | $type.RepresentationStrategy.GetFieldKey }}"}
    70  			{{- end }}
    71  		)
    72  	`, w, g.AdjCfg, g)
    73  }
    74  
    75  func (g structReprMapReprGenerator) EmitNodeTypeAssertions(w io.Writer) {
    76  	doTemplate(`
    77  		var _ datamodel.Node = &_{{ .Type | TypeSymbol }}__Repr{}
    78  	`, w, g.AdjCfg, g)
    79  }
    80  
    81  func (g structReprMapReprGenerator) EmitNodeMethodLookupByString(w io.Writer) {
    82  	// Similar to the type-level method, except any absent fields also return ErrNotExists.
    83  	doTemplate(`
    84  		func (n *_{{ .Type | TypeSymbol }}__Repr) LookupByString(key string) (datamodel.Node, error) {
    85  			switch key {
    86  			{{- range $field := .Type.Fields }}
    87  			case "{{ $field | $field.Parent.RepresentationStrategy.GetFieldKey }}":
    88  				{{- if $field.IsOptional }}
    89  				if n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Absent {
    90  					return datamodel.Absent, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfString(key)}
    91  				}
    92  				{{- end}}
    93  				{{- if $field.IsNullable }}
    94  				if n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Null {
    95  					return datamodel.Null, nil
    96  				}
    97  				{{- end}}
    98  				{{- if $field.IsMaybe }}
    99  				return n.{{ $field | FieldSymbolLower }}.v.Representation(), nil
   100  				{{- else}}
   101  				return n.{{ $field | FieldSymbolLower }}.Representation(), nil
   102  				{{- end}}
   103  			{{- end}}
   104  			default:
   105  				return nil, schema.ErrNoSuchField{Type: nil /*TODO*/, Field: datamodel.PathSegmentOfString(key)}
   106  			}
   107  		}
   108  	`, w, g.AdjCfg, g)
   109  }
   110  
   111  func (g structReprMapReprGenerator) EmitNodeMethodLookupByNode(w io.Writer) {
   112  	doTemplate(`
   113  		func (n *_{{ .Type | TypeSymbol }}__Repr) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
   114  			ks, err := key.AsString()
   115  			if err != nil {
   116  				return nil, err
   117  			}
   118  			return n.LookupByString(ks)
   119  		}
   120  	`, w, g.AdjCfg, g)
   121  }
   122  
   123  func (g structReprMapReprGenerator) EmitNodeMethodMapIterator(w io.Writer) {
   124  	// The 'idx' int is what field we'll yield next.
   125  	// Note that this iterator doesn't mention fields that are absent.
   126  	//  This makes things a bit trickier -- especially the 'Done' predicate,
   127  	//   since it may have to do lookahead if there's any optionals at the end of the structure!
   128  	//  It also means 'idx' can jump ahead by more than one per Next call in order to skip over absent fields.
   129  	// TODO : support for implicits is still future work.
   130  
   131  	// First: Determine if there are any optionals at all.
   132  	//  If there are none, some control flow symbols need to not be emitted.
   133  	fields := g.Type.Fields()
   134  	haveOptionals := false
   135  	for _, field := range fields {
   136  		if field.IsOptional() {
   137  			haveOptionals = true
   138  			break
   139  		}
   140  	}
   141  
   142  	// Second: Count how many trailing fields are optional.
   143  	//  The 'Done' predicate gets more complex when in the trailing optionals.
   144  	fieldCount := len(fields)
   145  	beginTrailingOptionalField := fieldCount
   146  	for i := fieldCount - 1; i >= 0; i-- {
   147  		if !fields[i].IsOptional() {
   148  			break
   149  		}
   150  		beginTrailingOptionalField = i
   151  	}
   152  	haveTrailingOptionals := beginTrailingOptionalField < fieldCount
   153  
   154  	// Now: finally we can get on with the actual templating.
   155  	doTemplate(`
   156  		func (n *_{{ .Type | TypeSymbol }}__Repr) MapIterator() datamodel.MapIterator {
   157  			{{- if .HaveTrailingOptionals }}
   158  			end := {{ len .Type.Fields }}`+
   159  		func() string { // this next part was too silly in templates due to lack of reverse ranging.
   160  			v := "\n"
   161  			for i := fieldCount - 1; i >= beginTrailingOptionalField; i-- {
   162  				v += "\t\t\tif n." + g.AdjCfg.FieldSymbolLower(fields[i]) + ".m == schema.Maybe_Absent {\n"
   163  				v += "\t\t\t\tend = " + strconv.Itoa(i) + "\n"
   164  				v += "\t\t\t} else {\n"
   165  				v += "\t\t\t\tgoto done\n"
   166  				v += "\t\t\t}\n"
   167  			}
   168  			return v
   169  		}()+`done:
   170  			return &_{{ .Type | TypeSymbol }}__ReprMapItr{n, 0, end}
   171  			{{- else}}
   172  			return &_{{ .Type | TypeSymbol }}__ReprMapItr{n, 0}
   173  			{{- end}}
   174  		}
   175  
   176  		type _{{ .Type | TypeSymbol }}__ReprMapItr struct {
   177  			n   *_{{ .Type | TypeSymbol }}__Repr
   178  			idx int
   179  			{{if .HaveTrailingOptionals }}end int{{end}}
   180  		}
   181  
   182  		func (itr *_{{ .Type | TypeSymbol }}__ReprMapItr) Next() (k datamodel.Node, v datamodel.Node, _ error) {
   183  			{{- if not .Type.Fields }}
   184  			{{- /* TODO: deduplicate all these methods which just error */ -}}
   185  			return nil, nil, datamodel.ErrIteratorOverread{}
   186  			{{ else -}}
   187  			{{ if .HaveOptionals }}advance:{{end -}}
   188  			if itr.idx >= {{ len .Type.Fields }} {
   189  				return nil, nil, datamodel.ErrIteratorOverread{}
   190  			}
   191  			switch itr.idx {
   192  			{{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}}
   193  			{{- range $i, $field := .Type.Fields }}
   194  			case {{ $i }}:
   195  				k = &fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}_serial
   196  				{{- if $field.IsOptional }}
   197  				if itr.n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Absent {
   198  					itr.idx++
   199  					goto advance
   200  				}
   201  				{{- end}}
   202  				{{- if $field.IsNullable }}
   203  				if itr.n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Null {
   204  					v = datamodel.Null
   205  					break
   206  				}
   207  				{{- end}}
   208  				{{- if $field.IsMaybe }}
   209  				v = itr.n.{{ $field | FieldSymbolLower}}.v.Representation()
   210  				{{- else}}
   211  				v = itr.n.{{ $field | FieldSymbolLower}}.Representation()
   212  				{{- end}}
   213  			{{- end}}
   214  			default:
   215  				panic("unreachable")
   216  			}
   217  			itr.idx++
   218  			return
   219  			{{- end}}
   220  		}
   221  		{{- if .HaveTrailingOptionals }}
   222  		func (itr *_{{ .Type | TypeSymbol }}__ReprMapItr) Done() bool {
   223  			return itr.idx >= itr.end
   224  		}
   225  		{{- else}}
   226  		func (itr *_{{ .Type | TypeSymbol }}__ReprMapItr) Done() bool {
   227  			return itr.idx >= {{ len .Type.Fields }}
   228  		}
   229  		{{- end}}
   230  	`, w, g.AdjCfg, struct {
   231  		Type                       *schema.TypeStruct
   232  		HaveOptionals              bool
   233  		HaveTrailingOptionals      bool
   234  		BeginTrailingOptionalField int
   235  	}{
   236  		g.Type,
   237  		haveOptionals,
   238  		haveTrailingOptionals,
   239  		beginTrailingOptionalField,
   240  	})
   241  }
   242  
   243  func (g structReprMapReprGenerator) EmitNodeMethodLength(w io.Writer) {
   244  	// This is fun: it has to count down for any unset optional fields.
   245  	// TODO : support for implicits is still future work.
   246  	doTemplate(`
   247  		func (rn *_{{ .Type | TypeSymbol }}__Repr) Length() int64 {
   248  			l := {{ len .Type.Fields }}
   249  			{{- range $field := .Type.Fields }}
   250  			{{- if $field.IsOptional }}
   251  			if rn.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Absent {
   252  				l--
   253  			}
   254  			{{- end}}
   255  			{{- end}}
   256  			return int64(l)
   257  		}
   258  	`, w, g.AdjCfg, g)
   259  }
   260  
   261  func (g structReprMapReprGenerator) EmitNodeMethodPrototype(w io.Writer) {
   262  	emitNodeMethodPrototype_typical(w, g.AdjCfg, g)
   263  }
   264  
   265  func (g structReprMapReprGenerator) EmitNodePrototypeType(w io.Writer) {
   266  	emitNodePrototypeType_typical(w, g.AdjCfg, g)
   267  }
   268  
   269  // --- NodeBuilder and NodeAssembler --->
   270  
   271  func (g structReprMapReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator {
   272  	return structReprMapReprBuilderGenerator{
   273  		g.AdjCfg,
   274  		mixins.MapAssemblerTraits{
   275  			PkgName:       g.PkgName,
   276  			TypeName:      g.TypeName,
   277  			AppliedPrefix: "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr",
   278  		},
   279  		g.PkgName,
   280  		g.Type,
   281  	}
   282  }
   283  
   284  type structReprMapReprBuilderGenerator struct {
   285  	AdjCfg *AdjunctCfg
   286  	mixins.MapAssemblerTraits
   287  	PkgName string
   288  	Type    *schema.TypeStruct
   289  }
   290  
   291  func (structReprMapReprBuilderGenerator) IsRepr() bool { return true } // hint used in some generalized templates.
   292  
   293  func (g structReprMapReprBuilderGenerator) EmitNodeBuilderType(w io.Writer) {
   294  	emitEmitNodeBuilderType_typical(w, g.AdjCfg, g)
   295  }
   296  func (g structReprMapReprBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) {
   297  	emitNodeBuilderMethods_typical(w, g.AdjCfg, g)
   298  }
   299  func (g structReprMapReprBuilderGenerator) EmitNodeAssemblerType(w io.Writer) {
   300  	// - 'w' is the "**w**ip" pointer.
   301  	// - 'm' is the **m**aybe which communicates our completeness to the parent if we're a child assembler.
   302  	// - 'state' is what it says on the tin.  this is used for the map state (the broad transitions between null, start-map, and finish are handled by 'm' for consistency.)
   303  	// - 's' is a bitfield for what's been **s**et.
   304  	// - 'f' is the **f**ocused field that will be assembled next.
   305  	//
   306  	// - 'cm' is **c**hild **m**aybe and is used for the completion message from children that aren't allowed to be nullable (for those that are, their own maybe.m is used).
   307  	// - the 'ca_*' fields embed **c**hild **a**ssemblers -- these are embedded so we can yield pointers to them without causing new allocations.
   308  	doTemplate(`
   309  		type _{{ .Type | TypeSymbol }}__ReprAssembler struct {
   310  			w *_{{ .Type | TypeSymbol }}
   311  			m *schema.Maybe
   312  			state maState
   313  			s int
   314  			f int
   315  
   316  			cm schema.Maybe
   317  			{{range $field := .Type.Fields -}}
   318  			ca_{{ $field | FieldSymbolLower }} _{{ $field.Type | TypeSymbol }}__ReprAssembler
   319  			{{end -}}
   320  		}
   321  
   322  		func (na *_{{ .Type | TypeSymbol }}__ReprAssembler) reset() {
   323  			na.state = maState_initial
   324  			na.s = 0
   325  			{{- range $field := .Type.Fields }}
   326  			na.ca_{{ $field | FieldSymbolLower }}.reset()
   327  			{{- end}}
   328  		}
   329  	`, w, g.AdjCfg, g)
   330  }
   331  func (g structReprMapReprBuilderGenerator) EmitNodeAssemblerMethodBeginMap(w io.Writer) {
   332  	emitNodeAssemblerMethodBeginMap_strictoid(w, g.AdjCfg, g)
   333  }
   334  func (g structReprMapReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) {
   335  	emitNodeAssemblerMethodAssignNull_recursive(w, g.AdjCfg, g)
   336  }
   337  func (g structReprMapReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) {
   338  	// AssignNode goes through three phases:
   339  	// 1. is it null?  Jump over to AssignNull (which may or may not reject it).
   340  	// 2. is it our own type?  Handle specially -- we might be able to do efficient things.
   341  	// 3. is it the right kind to morph into us?  Do so.
   342  	//
   343  	// We do not set m=midvalue in phase 3 -- it shouldn't matter unless you're trying to pull off concurrent access, which is wrong and unsafe regardless.
   344  	doTemplate(`
   345  		func (na *_{{ .Type | TypeSymbol }}__ReprAssembler) AssignNode(v datamodel.Node) error {
   346  			if v.IsNull() {
   347  				return na.AssignNull()
   348  			}
   349  			if v2, ok := v.(*_{{ .Type | TypeSymbol }}); ok {
   350  				switch *na.m {
   351  				case schema.Maybe_Value, schema.Maybe_Null:
   352  					panic("invalid state: cannot assign into assembler that's already finished")
   353  				case midvalue:
   354  					panic("invalid state: cannot assign null into an assembler that's already begun working on recursive structures!")
   355  				}
   356  				{{- if .Type | MaybeUsesPtr }}
   357  				if na.w == nil {
   358  					na.w = v2
   359  					*na.m = schema.Maybe_Value
   360  					return nil
   361  				}
   362  				{{- end}}
   363  				*na.w = *v2
   364  				*na.m = schema.Maybe_Value
   365  				return nil
   366  			}
   367  			if v.Kind() != datamodel.Kind_Map {
   368  				return datamodel.ErrWrongKind{TypeName: "{{ .PkgName }}.{{ .Type.Name }}.Repr", MethodName: "AssignNode", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: v.Kind()}
   369  			}
   370  			itr := v.MapIterator()
   371  			for !itr.Done() {
   372  				k, v, err := itr.Next()
   373  				if err != nil {
   374  					return err
   375  				}
   376  				if err := na.AssembleKey().AssignNode(k); err != nil {
   377  					return err
   378  				}
   379  				if err := na.AssembleValue().AssignNode(v); err != nil {
   380  					return err
   381  				}
   382  			}
   383  			return na.Finish()
   384  		}
   385  	`, w, g.AdjCfg, g)
   386  }
   387  func (g structReprMapReprBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) {
   388  	g.emitMapAssemblerChildTidyHelper(w)
   389  	g.emitMapAssemblerMethods(w)
   390  	g.emitKeyAssembler(w)
   391  }
   392  func (g structReprMapReprBuilderGenerator) emitMapAssemblerChildTidyHelper(w io.Writer) {
   393  	// This is exactly the same as the matching method on the type-level assembler;
   394  	//  everything that differs happens to be hidden behind the 'f' indirection, which is numeric.
   395  	doTemplate(`
   396  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) valueFinishTidy() bool {
   397  			switch ma.f {
   398  			{{- range $i, $field := .Type.Fields }}
   399  			case {{ $i }}:
   400  				{{- if $field.IsNullable }}
   401  				switch ma.w.{{ $field | FieldSymbolLower }}.m {
   402  				case schema.Maybe_Null:
   403  					ma.state = maState_initial
   404  					return true
   405  				case schema.Maybe_Value:
   406  					{{- if (MaybeUsesPtr $field.Type) }}
   407  					ma.w.{{ $field | FieldSymbolLower }}.v = ma.ca_{{ $field | FieldSymbolLower }}.w
   408  					{{- end}}
   409  					ma.state = maState_initial
   410  					return true
   411  				default:
   412  					return false
   413  				}
   414  				{{- else if $field.IsOptional }}
   415  				switch ma.w.{{ $field | FieldSymbolLower }}.m {
   416  				case schema.Maybe_Value:
   417  					{{- if (MaybeUsesPtr $field.Type) }}
   418  					ma.w.{{ $field | FieldSymbolLower }}.v = ma.ca_{{ $field | FieldSymbolLower }}.w
   419  					{{- end}}
   420  					ma.state = maState_initial
   421  					return true
   422  				default:
   423  					return false
   424  				}
   425  				{{- else}}
   426  				switch ma.cm {
   427  				case schema.Maybe_Value:
   428  					{{- /* while defense in depth here might avoid some 'wat' outcomes, it's not strictly necessary for safety */ -}}
   429  					{{- /* ma.ca_{{ $field | FieldSymbolLower }}.w = nil */ -}}
   430  					{{- /* ma.ca_{{ $field | FieldSymbolLower }}.m = nil */ -}}
   431  					ma.cm = schema.Maybe_Absent
   432  					ma.state = maState_initial
   433  					return true
   434  				default:
   435  					return false
   436  				}
   437  				{{- end}}
   438  			{{- end}}
   439  			default:
   440  				panic("unreachable")
   441  			}
   442  		}
   443  	`, w, g.AdjCfg, g)
   444  }
   445  func (g structReprMapReprBuilderGenerator) emitMapAssemblerMethods(w io.Writer) {
   446  	// FUTURE: some of the setup of the child assemblers could probably be DRY'd up.
   447  	doTemplate(`
   448  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) AssembleEntry(k string) (datamodel.NodeAssembler, error) {
   449  			switch ma.state {
   450  			case maState_initial:
   451  				// carry on
   452  			case maState_midKey:
   453  				panic("invalid state: AssembleEntry cannot be called when in the middle of assembling another key")
   454  			case maState_expectValue:
   455  				panic("invalid state: AssembleEntry cannot be called when expecting start of value assembly")
   456  			case maState_midValue:
   457  				if !ma.valueFinishTidy() {
   458  					panic("invalid state: AssembleEntry cannot be called when in the middle of assembling a value")
   459  				} // if tidy success: carry on
   460  			case maState_finished:
   461  				panic("invalid state: AssembleEntry cannot be called on an assembler that's already finished")
   462  			}
   463  			{{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}}
   464  			{{- if .Type.Fields }}
   465  			switch k {
   466  			{{- range $i, $field := .Type.Fields }}
   467  			case "{{ $field | $type.RepresentationStrategy.GetFieldKey }}":
   468  				if ma.s & fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} != 0 {
   469  					return nil, datamodel.ErrRepeatedMapKey{Key: &fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}_serial}
   470  				}
   471  				ma.s += fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}
   472  				ma.state = maState_midValue
   473  				ma.f = {{ $i }}
   474  				{{- if $field.IsMaybe }}
   475  				ma.ca_{{ $field | FieldSymbolLower }}.w = {{if not (MaybeUsesPtr $field.Type) }}&{{end}}ma.w.{{ $field | FieldSymbolLower }}.v
   476  				ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.w.{{ $field | FieldSymbolLower }}.m
   477  				{{if $field.IsNullable }}ma.w.{{ $field | FieldSymbolLower }}.m = allowNull{{end}}
   478  				{{- else}}
   479  				ma.ca_{{ $field | FieldSymbolLower }}.w = &ma.w.{{ $field | FieldSymbolLower }}
   480  				ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.cm
   481  				{{- end}}
   482  				return &ma.ca_{{ $field | FieldSymbolLower }}, nil
   483  			{{- end}}
   484  			default:
   485  			}
   486  			{{- end}}
   487  			return nil, schema.ErrInvalidKey{TypeName:"{{ .PkgName }}.{{ .Type.Name }}.Repr", Key:&_String{k}}
   488  		}
   489  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) AssembleKey() datamodel.NodeAssembler {
   490  			switch ma.state {
   491  			case maState_initial:
   492  				// carry on
   493  			case maState_midKey:
   494  				panic("invalid state: AssembleKey cannot be called when in the middle of assembling another key")
   495  			case maState_expectValue:
   496  				panic("invalid state: AssembleKey cannot be called when expecting start of value assembly")
   497  			case maState_midValue:
   498  				if !ma.valueFinishTidy() {
   499  					panic("invalid state: AssembleKey cannot be called when in the middle of assembling a value")
   500  				} // if tidy success: carry on
   501  			case maState_finished:
   502  				panic("invalid state: AssembleKey cannot be called on an assembler that's already finished")
   503  			}
   504  			ma.state = maState_midKey
   505  			return (*_{{ .Type | TypeSymbol }}__ReprKeyAssembler)(ma)
   506  		}
   507  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) AssembleValue() datamodel.NodeAssembler {
   508  			switch ma.state {
   509  			case maState_initial:
   510  				panic("invalid state: AssembleValue cannot be called when no key is primed")
   511  			case maState_midKey:
   512  				panic("invalid state: AssembleValue cannot be called when in the middle of assembling a key")
   513  			case maState_expectValue:
   514  				// carry on
   515  			case maState_midValue:
   516  				panic("invalid state: AssembleValue cannot be called when in the middle of assembling another value")
   517  			case maState_finished:
   518  				panic("invalid state: AssembleValue cannot be called on an assembler that's already finished")
   519  			}
   520  			ma.state = maState_midValue
   521  			switch ma.f {
   522  			{{- range $i, $field := .Type.Fields }}
   523  			case {{ $i }}:
   524  				{{- if $field.IsMaybe }}
   525  				ma.ca_{{ $field | FieldSymbolLower }}.w = {{if not (MaybeUsesPtr $field.Type) }}&{{end}}ma.w.{{ $field | FieldSymbolLower }}.v
   526  				ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.w.{{ $field | FieldSymbolLower }}.m
   527  				{{if $field.IsNullable }}ma.w.{{ $field | FieldSymbolLower }}.m = allowNull{{end}}
   528  				{{- else}}
   529  				ma.ca_{{ $field | FieldSymbolLower }}.w = &ma.w.{{ $field | FieldSymbolLower }}
   530  				ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.cm
   531  				{{- end}}
   532  				return &ma.ca_{{ $field | FieldSymbolLower }}
   533  			{{- end}}
   534  			default:
   535  				panic("unreachable")
   536  			}
   537  		}
   538  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) Finish() error {
   539  			switch ma.state {
   540  			case maState_initial:
   541  				// carry on
   542  			case maState_midKey:
   543  				panic("invalid state: Finish cannot be called when in the middle of assembling a key")
   544  			case maState_expectValue:
   545  				panic("invalid state: Finish cannot be called when expecting start of value assembly")
   546  			case maState_midValue:
   547  				if !ma.valueFinishTidy() {
   548  					panic("invalid state: Finish cannot be called when in the middle of assembling a value")
   549  				} // if tidy success: carry on
   550  			case maState_finished:
   551  				panic("invalid state: Finish cannot be called on an assembler that's already finished")
   552  			}
   553  			if ma.s & fieldBits__{{ $type | TypeSymbol }}_sufficient != fieldBits__{{ $type | TypeSymbol }}_sufficient {
   554  				err := schema.ErrMissingRequiredField{Missing: make([]string, 0)}
   555  				{{- range $i, $field := .Type.Fields }}
   556  				{{- if not $field.IsMaybe}}
   557  				if ma.s & fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} == 0 {
   558  					{{- if $field | $type.RepresentationStrategy.FieldHasRename }}
   559  					err.Missing = append(err.Missing, "{{ $field.Name }} (serial:\"{{ $field | $type.RepresentationStrategy.GetFieldKey }}\")")
   560  					{{- else}}
   561  					err.Missing = append(err.Missing, "{{ $field.Name }}")
   562  					{{- end}}
   563  				}
   564  				{{- end}}
   565  				{{- end}}
   566  				return err
   567  			}
   568  			ma.state = maState_finished
   569  			*ma.m = schema.Maybe_Value
   570  			return nil
   571  		}
   572  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) KeyPrototype() datamodel.NodePrototype {
   573  			return _String__Prototype{}
   574  		}
   575  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) ValuePrototype(k string) datamodel.NodePrototype {
   576  			panic("todo structbuilder mapassembler repr valueprototype")
   577  		}
   578  	`, w, g.AdjCfg, g)
   579  }
   580  func (g structReprMapReprBuilderGenerator) emitKeyAssembler(w io.Writer) {
   581  	doTemplate(`
   582  		type _{{ .Type | TypeSymbol }}__ReprKeyAssembler _{{ .Type | TypeSymbol }}__ReprAssembler
   583  	`, w, g.AdjCfg, g)
   584  	stubs := mixins.StringAssemblerTraits{
   585  		PkgName:       g.PkgName,
   586  		TypeName:      g.TypeName + ".KeyAssembler", // ".Repr" is already in `g.TypeName`, so don't stutter the "Repr" part.
   587  		AppliedPrefix: "_" + g.AdjCfg.TypeSymbol(g.Type) + "__ReprKey",
   588  	}
   589  	// This key assembler can disregard any idea of complex keys because it's at the representation level!
   590  	//  Map keys must always be plain strings at the representation level.
   591  	stubs.EmitNodeAssemblerMethodBeginMap(w)
   592  	stubs.EmitNodeAssemblerMethodBeginList(w)
   593  	stubs.EmitNodeAssemblerMethodAssignNull(w)
   594  	stubs.EmitNodeAssemblerMethodAssignBool(w)
   595  	stubs.EmitNodeAssemblerMethodAssignInt(w)
   596  	stubs.EmitNodeAssemblerMethodAssignFloat(w)
   597  	doTemplate(`
   598  		func (ka *_{{ .Type | TypeSymbol }}__ReprKeyAssembler) AssignString(k string) error {
   599  			if ka.state != maState_midKey {
   600  				panic("misuse: KeyAssembler held beyond its valid lifetime")
   601  			}
   602  			{{- if .Type.Fields }}
   603  			switch k {
   604  			{{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}}
   605  			{{- range $i, $field := .Type.Fields }}
   606  			case "{{ $field | $type.RepresentationStrategy.GetFieldKey }}":
   607  				if ka.s & fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} != 0 {
   608  					return datamodel.ErrRepeatedMapKey{Key: &fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}_serial}
   609  				}
   610  				ka.s += fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}
   611  				ka.state = maState_expectValue
   612  				ka.f = {{ $i }}
   613  				return nil
   614  			{{- end }}
   615  			}
   616  			{{- end }}
   617  			return schema.ErrInvalidKey{TypeName:"{{ .PkgName }}.{{ .Type.Name }}.Repr", Key:&_String{k}}
   618  		}
   619  	`, w, g.AdjCfg, g)
   620  	stubs.EmitNodeAssemblerMethodAssignBytes(w)
   621  	stubs.EmitNodeAssemblerMethodAssignLink(w)
   622  	doTemplate(`
   623  		func (ka *_{{ .Type | TypeSymbol }}__ReprKeyAssembler) AssignNode(v datamodel.Node) error {
   624  			if v2, err := v.AsString(); err != nil {
   625  				return err
   626  			} else {
   627  				return ka.AssignString(v2)
   628  			}
   629  		}
   630  		func (_{{ .Type | TypeSymbol }}__ReprKeyAssembler) Prototype() datamodel.NodePrototype {
   631  			return _String__Prototype{}
   632  		}
   633  	`, w, g.AdjCfg, g)
   634  }