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

     1  package gengo
     2  
     3  import (
     4  	"io"
     5  
     6  	"github.com/ipld/go-ipld-prime/schema"
     7  	"github.com/ipld/go-ipld-prime/schema/gen/go/mixins"
     8  )
     9  
    10  var _ TypeGenerator = &unionReprKeyedGenerator{}
    11  
    12  // General observation: many things about the keyed representation of unions is *very* similar to the type-level code,
    13  //  because the type level code effective does espouse keyed-style behavior (just with type names as the keys).
    14  //  Be advised that this similarity does not hold at *all* true of any of the other representation modes of unions!
    15  
    16  func NewUnionReprKeyedGenerator(pkgName string, typ *schema.TypeUnion, adjCfg *AdjunctCfg) TypeGenerator {
    17  	return unionReprKeyedGenerator{
    18  		unionGenerator{
    19  			adjCfg,
    20  			mixins.MapTraits{
    21  				PkgName:    pkgName,
    22  				TypeName:   string(typ.Name()),
    23  				TypeSymbol: adjCfg.TypeSymbol(typ),
    24  			},
    25  			pkgName,
    26  			typ,
    27  		},
    28  	}
    29  }
    30  
    31  type unionReprKeyedGenerator struct {
    32  	unionGenerator
    33  }
    34  
    35  func (g unionReprKeyedGenerator) GetRepresentationNodeGen() NodeGenerator {
    36  	return unionReprKeyedReprGenerator{
    37  		g.AdjCfg,
    38  		mixins.MapTraits{
    39  			PkgName:    g.PkgName,
    40  			TypeName:   string(g.Type.Name()) + ".Repr",
    41  			TypeSymbol: "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr",
    42  		},
    43  		g.PkgName,
    44  		g.Type,
    45  	}
    46  }
    47  
    48  type unionReprKeyedReprGenerator struct {
    49  	AdjCfg *AdjunctCfg
    50  	mixins.MapTraits
    51  	PkgName string
    52  	Type    *schema.TypeUnion
    53  }
    54  
    55  func (unionReprKeyedReprGenerator) IsRepr() bool { return true } // hint used in some generalized templates.
    56  
    57  func (g unionReprKeyedReprGenerator) EmitNodeType(w io.Writer) {
    58  	// The type is structurally the same, but will have a different set of methods.
    59  	doTemplate(`
    60  		type _{{ .Type | TypeSymbol }}__Repr _{{ .Type | TypeSymbol }}
    61  	`, w, g.AdjCfg, g)
    62  
    63  	// We do also want some constants for our discriminant values;
    64  	//  they'll make iterators able to work faster.
    65  	doTemplate(`
    66  		var (
    67  			{{- range $member := .Type.Members }}
    68  			memberName__{{ dot.Type | TypeSymbol }}_{{ $member.Name }}_serial = _String{"{{ $member | dot.Type.RepresentationStrategy.GetDiscriminant }}"}
    69  			{{- end }}
    70  		)
    71  	`, w, g.AdjCfg, g)
    72  }
    73  
    74  func (g unionReprKeyedReprGenerator) EmitNodeTypeAssertions(w io.Writer) {
    75  	doTemplate(`
    76  		var _ datamodel.Node = &_{{ .Type | TypeSymbol }}__Repr{}
    77  	`, w, g.AdjCfg, g)
    78  }
    79  
    80  func (g unionReprKeyedReprGenerator) EmitNodeMethodLookupByString(w io.Writer) {
    81  	// Similar to the type-level method, except uses discriminant values as keys instead of the member type names.
    82  	doTemplate(`
    83  		func (n *_{{ .Type | TypeSymbol }}__Repr) LookupByString(key string) (datamodel.Node, error) {
    84  			switch key {
    85  			{{- range $i, $member := .Type.Members }}
    86  			case "{{ $member | dot.Type.RepresentationStrategy.GetDiscriminant }}":
    87  				{{- if (eq (dot.AdjCfg.UnionMemlayout dot.Type) "embedAll") }}
    88  				if n.tag != {{ add $i 1 }} {
    89  					return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfString(key)}
    90  				}
    91  				return n.x{{ add $i 1 }}.Representation(), nil
    92  				{{- else if (eq (dot.AdjCfg.UnionMemlayout dot.Type) "interface") }}
    93  				if n2, ok := n.x.({{ $member | TypeSymbol }}); ok {
    94  					return n2.Representation(), nil
    95  				} else {
    96  					return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfString(key)}
    97  				}
    98  				{{- end}}
    99  			{{- end}}
   100  			default:
   101  				return nil, schema.ErrNoSuchField{Type: nil /*TODO*/, Field: datamodel.PathSegmentOfString(key)}
   102  			}
   103  		}
   104  	`, w, g.AdjCfg, g)
   105  }
   106  
   107  func (g unionReprKeyedReprGenerator) EmitNodeMethodLookupByNode(w io.Writer) {
   108  	doTemplate(`
   109  		func (n *_{{ .Type | TypeSymbol }}__Repr) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
   110  			ks, err := key.AsString()
   111  			if err != nil {
   112  				return nil, err
   113  			}
   114  			return n.LookupByString(ks)
   115  		}
   116  	`, w, g.AdjCfg, g)
   117  }
   118  
   119  func (g unionReprKeyedReprGenerator) EmitNodeMethodMapIterator(w io.Writer) {
   120  	// Similar to the type-level method, except yields discriminant values as keys instead of the member type names.
   121  	doTemplate(`
   122  		func (n *_{{ .Type | TypeSymbol }}__Repr) MapIterator() datamodel.MapIterator {
   123  			return &_{{ .Type | TypeSymbol }}__ReprMapItr{n, false}
   124  		}
   125  
   126  		type _{{ .Type | TypeSymbol }}__ReprMapItr struct {
   127  			n *_{{ .Type | TypeSymbol }}__Repr
   128  			done bool
   129  		}
   130  
   131  		func (itr *_{{ .Type | TypeSymbol }}__ReprMapItr) Next() (k datamodel.Node, v datamodel.Node, _ error) {
   132  			if itr.done {
   133  				return nil, nil, datamodel.ErrIteratorOverread{}
   134  			}
   135  			{{- if (eq (.AdjCfg.UnionMemlayout .Type) "embedAll") }}
   136  			switch itr.n.tag {
   137  			{{- range $i, $member := .Type.Members }}
   138  			case {{ add $i 1 }}:
   139  				k, v = &memberName__{{ dot.Type | TypeSymbol }}_{{ $member.Name }}_serial, itr.n.x{{ add $i 1 }}.Representation()
   140  			{{- end}}
   141  			{{- else if (eq (.AdjCfg.UnionMemlayout .Type) "interface") }}
   142  			switch n2 := itr.n.x.(type) {
   143  			{{- range $member := .Type.Members }}
   144  			case {{ $member | TypeSymbol }}:
   145  				k, v = &memberName__{{ dot.Type | TypeSymbol }}_{{ $member.Name }}_serial, n2.Representation()
   146  			{{- end}}
   147  			{{- end}}
   148  			default:
   149  				panic("unreachable")
   150  			}
   151  			itr.done = true
   152  			return
   153  		}
   154  		func (itr *_{{ .Type | TypeSymbol }}__ReprMapItr) Done() bool {
   155  			return itr.done
   156  		}
   157  
   158  	`, w, g.AdjCfg, g)
   159  }
   160  
   161  func (g unionReprKeyedReprGenerator) EmitNodeMethodLength(w io.Writer) {
   162  	doTemplate(`
   163  		func (_{{ .Type | TypeSymbol }}__Repr) Length() int64 {
   164  			return 1
   165  		}
   166  	`, w, g.AdjCfg, g)
   167  }
   168  
   169  func (g unionReprKeyedReprGenerator) EmitNodeMethodPrototype(w io.Writer) {
   170  	emitNodeMethodPrototype_typical(w, g.AdjCfg, g)
   171  }
   172  
   173  func (g unionReprKeyedReprGenerator) EmitNodePrototypeType(w io.Writer) {
   174  	emitNodePrototypeType_typical(w, g.AdjCfg, g)
   175  }
   176  
   177  // --- NodeBuilder and NodeAssembler --->
   178  
   179  func (g unionReprKeyedReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator {
   180  	return unionReprKeyedReprBuilderGenerator{
   181  		g.AdjCfg,
   182  		mixins.MapAssemblerTraits{
   183  			PkgName:       g.PkgName,
   184  			TypeName:      g.TypeName,
   185  			AppliedPrefix: "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr",
   186  		},
   187  		g.PkgName,
   188  		g.Type,
   189  	}
   190  }
   191  
   192  type unionReprKeyedReprBuilderGenerator struct {
   193  	AdjCfg *AdjunctCfg
   194  	mixins.MapAssemblerTraits
   195  	PkgName string
   196  	Type    *schema.TypeUnion
   197  }
   198  
   199  func (unionReprKeyedReprBuilderGenerator) IsRepr() bool { return true } // hint used in some generalized templates.
   200  
   201  func (g unionReprKeyedReprBuilderGenerator) EmitNodeBuilderType(w io.Writer) {
   202  	emitEmitNodeBuilderType_typical(w, g.AdjCfg, g)
   203  }
   204  func (g unionReprKeyedReprBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) {
   205  	emitNodeBuilderMethods_typical(w, g.AdjCfg, g)
   206  }
   207  func (g unionReprKeyedReprBuilderGenerator) EmitNodeAssemblerType(w io.Writer) {
   208  	// Nearly identical to the type-level system, except it embeds the Repr variant of child assemblers
   209  	//  (which is a very minor difference textually, but means this structure can end up with a pretty wildly different resident memory size than the type-level one).
   210  	doTemplate(`
   211  		type _{{ .Type | TypeSymbol }}__ReprAssembler struct {
   212  			w *_{{ .Type | TypeSymbol }}
   213  			m *schema.Maybe
   214  			state maState
   215  
   216  			cm schema.Maybe
   217  			{{- range $i, $member := .Type.Members }}
   218  			ca{{ add $i 1 }} {{ if (eq (dot.AdjCfg.UnionMemlayout dot.Type) "interface") }}*{{end}}_{{ $member | TypeSymbol }}__ReprAssembler
   219  			{{end -}}
   220  			ca uint
   221  		}
   222  	`, w, g.AdjCfg, g)
   223  
   224  	// Reset methods: also nearly identical to the type-level ones.
   225  	doTemplate(`
   226  		func (na *_{{ .Type | TypeSymbol }}__ReprAssembler) reset() {
   227  			na.state = maState_initial
   228  			switch na.ca {
   229  			case 0:
   230  				return
   231  			{{- range $i, $member := .Type.Members }}
   232  			case {{ add $i 1 }}:
   233  				na.ca{{ add $i 1 }}.reset()
   234  			{{end -}}
   235  			default:
   236  				panic("unreachable")
   237  			}
   238  			na.ca = 0
   239  			na.cm = schema.Maybe_Absent
   240  		}
   241  	`, w, g.AdjCfg, g)
   242  }
   243  func (g unionReprKeyedReprBuilderGenerator) EmitNodeAssemblerMethodBeginMap(w io.Writer) {
   244  	emitNodeAssemblerMethodBeginMap_strictoid(w, g.AdjCfg, g)
   245  }
   246  func (g unionReprKeyedReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) {
   247  	// It might sound a bit odd to call a union "recursive", since it's so very trivially so (no fan-out),
   248  	//  but it's functionally accurate: the generated method should include a branch for the 'midvalue' state.
   249  	emitNodeAssemblerMethodAssignNull_recursive(w, g.AdjCfg, g)
   250  }
   251  func (g unionReprKeyedReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) {
   252  	// DRY: this is once again not-coincidentally very nearly equal to the type-level method.  Would be good to dedup them... after we do the get-to-the-point-in-phase-3 improvement.
   253  	doTemplate(`
   254  		func (na *_{{ .Type | TypeSymbol }}__ReprAssembler) AssignNode(v datamodel.Node) error {
   255  			if v.IsNull() {
   256  				return na.AssignNull()
   257  			}
   258  			if v2, ok := v.(*_{{ .Type | TypeSymbol }}); ok {
   259  				switch *na.m {
   260  				case schema.Maybe_Value, schema.Maybe_Null:
   261  					panic("invalid state: cannot assign into assembler that's already finished")
   262  				case midvalue:
   263  					panic("invalid state: cannot assign null into an assembler that's already begun working on recursive structures!")
   264  				}
   265  				{{- if .Type | MaybeUsesPtr }}
   266  				if na.w == nil {
   267  					na.w = v2
   268  					*na.m = schema.Maybe_Value
   269  					return nil
   270  				}
   271  				{{- end}}
   272  				*na.w = *v2
   273  				*na.m = schema.Maybe_Value
   274  				return nil
   275  			}
   276  			if v.Kind() != datamodel.Kind_Map {
   277  				return datamodel.ErrWrongKind{TypeName: "{{ .PkgName }}.{{ .Type.Name }}.Repr", MethodName: "AssignNode", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: v.Kind()}
   278  			}
   279  			itr := v.MapIterator()
   280  			for !itr.Done() {
   281  				k, v, err := itr.Next()
   282  				if err != nil {
   283  					return err
   284  				}
   285  				if err := na.AssembleKey().AssignNode(k); err != nil {
   286  					return err
   287  				}
   288  				if err := na.AssembleValue().AssignNode(v); err != nil {
   289  					return err
   290  				}
   291  			}
   292  			return na.Finish()
   293  		}
   294  	`, w, g.AdjCfg, g)
   295  }
   296  func (g unionReprKeyedReprBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) {
   297  	g.emitMapAssemblerChildTidyHelper(w)
   298  	g.emitMapAssemblerMethods(w)
   299  	g.emitKeyAssembler(w)
   300  }
   301  func (g unionReprKeyedReprBuilderGenerator) emitMapAssemblerChildTidyHelper(w io.Writer) {
   302  	// Nearly identical to the type-level equivalent.
   303  	doTemplate(`
   304  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) valueFinishTidy() bool {
   305  			switch ma.cm {
   306  			case schema.Maybe_Value:
   307  				{{- /* nothing to do for memlayout=embedAll; the tag is already set and memory already in place. */ -}}
   308  				{{- /* nothing to do for memlayout=interface either; same story, the values are already in place. */ -}}
   309  				ma.state = maState_initial
   310  				return true
   311  			default:
   312  				return false
   313  			}
   314  		}
   315  	`, w, g.AdjCfg, g)
   316  }
   317  func (g unionReprKeyedReprBuilderGenerator) emitMapAssemblerMethods(w io.Writer) {
   318  	// All of these: shamelessly similar to the type-level equivalent, modulo a few appearances of "Repr".
   319  	//  Alright, and also the "discriminant values as keys instead of the member type names" thing.
   320  	// DRY: the number of times these `ma.state` switches are appearing is truly intense!  This is starting to look like one of them most important things to shrink the GSLOC/ASM size of!
   321  
   322  	doTemplate(`
   323  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) AssembleEntry(k string) (datamodel.NodeAssembler, error) {
   324  			switch ma.state {
   325  			case maState_initial:
   326  				// carry on
   327  			case maState_midKey:
   328  				panic("invalid state: AssembleEntry cannot be called when in the middle of assembling another key")
   329  			case maState_expectValue:
   330  				panic("invalid state: AssembleEntry cannot be called when expecting start of value assembly")
   331  			case maState_midValue:
   332  				if !ma.valueFinishTidy() {
   333  					panic("invalid state: AssembleEntry cannot be called when in the middle of assembling a value")
   334  				} // if tidy success: carry on for the moment, but we'll still be erroring shortly.
   335  			case maState_finished:
   336  				panic("invalid state: AssembleEntry cannot be called on an assembler that's already finished")
   337  			}
   338  			if ma.ca != 0 {
   339  				return nil, schema.ErrNotUnionStructure{TypeName:"{{ .PkgName }}.{{ .Type.Name }}.Repr", Detail: "cannot add another entry -- a union can only contain one thing!"}
   340  			}
   341  			{{- if .Type.Members }}
   342  			switch k {
   343  			{{- range $i, $member := .Type.Members }}
   344  			case "{{ $member | dot.Type.RepresentationStrategy.GetDiscriminant }}":
   345  				ma.state = maState_midValue
   346  				ma.ca = {{ add $i 1 }}
   347  				{{- if (eq (dot.AdjCfg.UnionMemlayout dot.Type) "embedAll") }}
   348  				ma.w.tag = {{ add $i 1 }}
   349  				ma.ca{{ add $i 1 }}.w = &ma.w.x{{ add $i 1 }}
   350  				ma.ca{{ add $i 1 }}.m = &ma.cm
   351  				return &ma.ca{{ add $i 1 }}, nil
   352  				{{- else if (eq (dot.AdjCfg.UnionMemlayout dot.Type) "interface") }}
   353  				x := &_{{ $member | TypeSymbol }}{}
   354  				ma.w.x = x
   355  				if ma.ca{{ add $i 1 }} == nil {
   356  					ma.ca{{ add $i 1 }} = &_{{ $member | TypeSymbol }}__ReprAssembler{}
   357  				}
   358  				ma.ca{{ add $i 1 }}.w = x
   359  				ma.ca{{ add $i 1 }}.m = &ma.cm
   360  				return ma.ca{{ add $i 1 }}, nil
   361  				{{- end}}
   362  			{{- end}}
   363  			}
   364  			{{- end}}
   365  			return nil, schema.ErrInvalidKey{TypeName:"{{ .PkgName }}.{{ .Type.Name }}.Repr", Key:&_String{k}}
   366  		}
   367  	`, w, g.AdjCfg, g)
   368  
   369  	doTemplate(`
   370  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) AssembleKey() datamodel.NodeAssembler {
   371  			switch ma.state {
   372  			case maState_initial:
   373  				// carry on
   374  			case maState_midKey:
   375  				panic("invalid state: AssembleKey cannot be called when in the middle of assembling another key")
   376  			case maState_expectValue:
   377  				panic("invalid state: AssembleKey cannot be called when expecting start of value assembly")
   378  			case maState_midValue:
   379  				if !ma.valueFinishTidy() {
   380  					panic("invalid state: AssembleKey cannot be called when in the middle of assembling a value")
   381  				} // if tidy success: carry on for the moment, but we'll still be erroring shortly... or rather, the keyassembler will be.
   382  			case maState_finished:
   383  				panic("invalid state: AssembleKey cannot be called on an assembler that's already finished")
   384  			}
   385  			ma.state = maState_midKey
   386  			return (*_{{ .Type | TypeSymbol }}__ReprKeyAssembler)(ma)
   387  		}
   388  	`, w, g.AdjCfg, g)
   389  
   390  	doTemplate(`
   391  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) AssembleValue() datamodel.NodeAssembler {
   392  			switch ma.state {
   393  			case maState_initial:
   394  				panic("invalid state: AssembleValue cannot be called when no key is primed")
   395  			case maState_midKey:
   396  				panic("invalid state: AssembleValue cannot be called when in the middle of assembling a key")
   397  			case maState_expectValue:
   398  				// carry on
   399  			case maState_midValue:
   400  				panic("invalid state: AssembleValue cannot be called when in the middle of assembling another value")
   401  			case maState_finished:
   402  				panic("invalid state: AssembleValue cannot be called on an assembler that's already finished")
   403  			}
   404  			ma.state = maState_midValue
   405  			switch ma.ca {
   406  			{{- range $i, $member := .Type.Members }}
   407  			case {{ add $i 1 }}:
   408  				{{- if (eq (dot.AdjCfg.UnionMemlayout dot.Type) "embedAll") }}
   409  				ma.ca{{ add $i 1 }}.w = &ma.w.x{{ add $i 1 }}
   410  				ma.ca{{ add $i 1 }}.m = &ma.cm
   411  				return &ma.ca{{ add $i 1 }}
   412  				{{- else if (eq (dot.AdjCfg.UnionMemlayout dot.Type) "interface") }}
   413  				x := &_{{ $member | TypeSymbol }}{}
   414  				ma.w.x = x
   415  				if ma.ca{{ add $i 1 }} == nil {
   416  					ma.ca{{ add $i 1 }} = &_{{ $member | TypeSymbol }}__ReprAssembler{}
   417  				}
   418  				ma.ca{{ add $i 1 }}.w = x
   419  				ma.ca{{ add $i 1 }}.m = &ma.cm
   420  				return ma.ca{{ add $i 1 }}
   421  				{{- end}}
   422  			{{- end}}
   423  			default:
   424  				panic("unreachable")
   425  			}
   426  		}
   427  	`, w, g.AdjCfg, g)
   428  
   429  	doTemplate(`
   430  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) Finish() error {
   431  			switch ma.state {
   432  			case maState_initial:
   433  				// carry on
   434  			case maState_midKey:
   435  				panic("invalid state: Finish cannot be called when in the middle of assembling a key")
   436  			case maState_expectValue:
   437  				panic("invalid state: Finish cannot be called when expecting start of value assembly")
   438  			case maState_midValue:
   439  				if !ma.valueFinishTidy() {
   440  					panic("invalid state: Finish cannot be called when in the middle of assembling a value")
   441  				} // if tidy success: carry on
   442  			case maState_finished:
   443  				panic("invalid state: Finish cannot be called on an assembler that's already finished")
   444  			}
   445  			if ma.ca == 0 {
   446  				return schema.ErrNotUnionStructure{TypeName:"{{ .PkgName }}.{{ .Type.Name }}.Repr", Detail: "a union must have exactly one entry (not none)!"}
   447  			}
   448  			ma.state = maState_finished
   449  			*ma.m = schema.Maybe_Value
   450  			return nil
   451  		}
   452  	`, w, g.AdjCfg, g)
   453  
   454  	doTemplate(`
   455  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) KeyPrototype() datamodel.NodePrototype {
   456  			return _String__Prototype{}
   457  		}
   458  		func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) ValuePrototype(k string) datamodel.NodePrototype {
   459  			switch k {
   460  			{{- range $i, $member := .Type.Members }}
   461  			case "{{ $member.Name }}":
   462  				return _{{ $member | TypeSymbol }}__ReprPrototype{}
   463  			{{- end}}
   464  			default:
   465  				return nil
   466  			}
   467  		}
   468  	`, w, g.AdjCfg, g)
   469  }
   470  func (g unionReprKeyedReprBuilderGenerator) emitKeyAssembler(w io.Writer) {
   471  	doTemplate(`
   472  		type _{{ .Type | TypeSymbol }}__ReprKeyAssembler _{{ .Type | TypeSymbol }}__ReprAssembler
   473  	`, w, g.AdjCfg, g)
   474  	stubs := mixins.StringAssemblerTraits{
   475  		PkgName:       g.PkgName,
   476  		TypeName:      g.TypeName + ".KeyAssembler", // ".Repr" is already in `g.TypeName`, so don't stutter the "Repr" part.
   477  		AppliedPrefix: "_" + g.AdjCfg.TypeSymbol(g.Type) + "__ReprKey",
   478  	}
   479  	// This key assembler can disregard any idea of complex keys because we know that our discriminants are just strings!
   480  	stubs.EmitNodeAssemblerMethodBeginMap(w)
   481  	stubs.EmitNodeAssemblerMethodBeginList(w)
   482  	stubs.EmitNodeAssemblerMethodAssignNull(w)
   483  	stubs.EmitNodeAssemblerMethodAssignBool(w)
   484  	stubs.EmitNodeAssemblerMethodAssignInt(w)
   485  	stubs.EmitNodeAssemblerMethodAssignFloat(w)
   486  	doTemplate(`
   487  		func (ka *_{{ .Type | TypeSymbol }}__ReprKeyAssembler) AssignString(k string) error {
   488  			if ka.state != maState_midKey {
   489  				panic("misuse: KeyAssembler held beyond its valid lifetime")
   490  			}
   491  			if ka.ca != 0 {
   492  				return schema.ErrNotUnionStructure{TypeName:"{{ .PkgName }}.{{ .Type.Name }}.Repr", Detail: "cannot add another entry -- a union can only contain one thing!"}
   493  			}
   494  			switch k {
   495  			{{- range $i, $member := .Type.Members }}
   496  			case "{{ $member | dot.Type.RepresentationStrategy.GetDiscriminant }}":
   497  				ka.ca = {{ add $i 1 }}
   498  				{{- if (eq (dot.AdjCfg.UnionMemlayout dot.Type) "embedAll") }}
   499  				ka.w.tag = {{ add $i 1 }}
   500  				{{- end}}
   501  				ka.state = maState_expectValue
   502  				return nil
   503  			{{- end}}
   504  			}
   505  			return schema.ErrInvalidKey{TypeName:"{{ .PkgName }}.{{ .Type.Name }}.Repr", Key:&_String{k}} // TODO: error quality: ErrInvalidUnionDiscriminant ?
   506  		}
   507  	`, w, g.AdjCfg, g)
   508  	stubs.EmitNodeAssemblerMethodAssignBytes(w)
   509  	stubs.EmitNodeAssemblerMethodAssignLink(w)
   510  	doTemplate(`
   511  		func (ka *_{{ .Type | TypeSymbol }}__ReprKeyAssembler) AssignNode(v datamodel.Node) error {
   512  			if v2, err := v.AsString(); err != nil {
   513  				return err
   514  			} else {
   515  				return ka.AssignString(v2)
   516  			}
   517  		}
   518  		func (_{{ .Type | TypeSymbol }}__ReprKeyAssembler) Prototype() datamodel.NodePrototype {
   519  			return _String__Prototype{}
   520  		}
   521  	`, w, g.AdjCfg, g)
   522  }