github.com/ipld/go-ipld-prime@v0.21.0/schema/gen/go/genStruct.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 type structGenerator struct { 11 AdjCfg *AdjunctCfg 12 mixins.MapTraits 13 PkgName string 14 Type *schema.TypeStruct 15 } 16 17 func (structGenerator) IsRepr() bool { return false } // hint used in some generalized templates. 18 19 // --- native content and specializations ---> 20 21 func (g structGenerator) EmitNativeType(w io.Writer) { 22 doTemplate(` 23 {{- if Comments -}} 24 // {{ .Type | TypeSymbol }} matches the IPLD Schema type "{{ .Type.Name }}". It has {{ .Type.TypeKind }} type-kind, and may be interrogated like {{ .Kind }} kind. 25 {{- end}} 26 type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }} 27 type _{{ .Type | TypeSymbol }} struct { 28 {{- range $field := .Type.Fields}} 29 {{ $field | FieldSymbolLower }} _{{ $field.Type | TypeSymbol }}{{if $field.IsMaybe }}__Maybe{{end}} 30 {{- end}} 31 } 32 `, w, g.AdjCfg, g) 33 } 34 35 func (g structGenerator) EmitNativeAccessors(w io.Writer) { 36 doTemplate(` 37 {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} 38 {{- range $field := .Type.Fields }} 39 func (n _{{ $type | TypeSymbol }}) Field{{ $field | FieldSymbolUpper }}() {{ if $field.IsMaybe }}Maybe{{end}}{{ $field.Type | TypeSymbol }} { 40 return &n.{{ $field | FieldSymbolLower }} 41 } 42 {{- end}} 43 `, w, g.AdjCfg, g) 44 } 45 46 func (g structGenerator) EmitNativeBuilder(w io.Writer) { 47 // Unclear what, if anything, goes here. 48 } 49 50 func (g structGenerator) EmitNativeMaybe(w io.Writer) { 51 emitNativeMaybe(w, g.AdjCfg, g) 52 } 53 54 // --- type info ---> 55 56 func (g structGenerator) EmitTypeConst(w io.Writer) { 57 doTemplate(` 58 // TODO EmitTypeConst 59 `, w, g.AdjCfg, g) 60 } 61 62 // --- TypedNode interface satisfaction ---> 63 64 func (g structGenerator) EmitTypedNodeMethodType(w io.Writer) { 65 doTemplate(` 66 func ({{ .Type | TypeSymbol }}) Type() schema.Type { 67 return nil /*TODO:typelit*/ 68 } 69 `, w, g.AdjCfg, g) 70 } 71 72 func (g structGenerator) EmitTypedNodeMethodRepresentation(w io.Writer) { 73 emitTypicalTypedNodeMethodRepresentation(w, g.AdjCfg, g) 74 } 75 76 // --- Node interface satisfaction ---> 77 78 func (g structGenerator) EmitNodeType(w io.Writer) { 79 // No additional types needed. Methods all attach to the native type. 80 // We do, however, want some constants for our fields; 81 // they'll make iterators able to work faster. So let's emit those. 82 doTemplate(` 83 var ( 84 {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} 85 {{- range $field := .Type.Fields }} 86 fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} = _String{"{{ $field.Name }}"} 87 {{- end }} 88 ) 89 `, w, g.AdjCfg, g) 90 } 91 92 func (g structGenerator) EmitNodeTypeAssertions(w io.Writer) { 93 emitNodeTypeAssertions_typical(w, g.AdjCfg, g) 94 } 95 96 func (g structGenerator) EmitNodeMethodLookupByString(w io.Writer) { 97 doTemplate(` 98 func (n {{ .Type | TypeSymbol }}) LookupByString(key string) (datamodel.Node, error) { 99 switch key { 100 {{- range $field := .Type.Fields }} 101 case "{{ $field.Name }}": 102 {{- if $field.IsOptional }} 103 if n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Absent { 104 return datamodel.Absent, nil 105 } 106 {{- end}} 107 {{- if $field.IsNullable }} 108 if n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Null { 109 return datamodel.Null, nil 110 } 111 {{- end}} 112 {{- if $field.IsMaybe }} 113 return {{if not (MaybeUsesPtr $field.Type) }}&{{end}}n.{{ $field | FieldSymbolLower }}.v, nil 114 {{- else}} 115 return &n.{{ $field | FieldSymbolLower }}, nil 116 {{- end}} 117 {{- end}} 118 default: 119 return nil, schema.ErrNoSuchField{Type: nil /*TODO*/, Field: datamodel.PathSegmentOfString(key)} 120 } 121 } 122 `, w, g.AdjCfg, g) 123 } 124 125 func (g structGenerator) EmitNodeMethodLookupByNode(w io.Writer) { 126 doTemplate(` 127 func (n {{ .Type | TypeSymbol }}) LookupByNode(key datamodel.Node) (datamodel.Node, error) { 128 ks, err := key.AsString() 129 if err != nil { 130 return nil, err 131 } 132 return n.LookupByString(ks) 133 } 134 `, w, g.AdjCfg, g) 135 } 136 137 func (g structGenerator) EmitNodeMethodMapIterator(w io.Writer) { 138 // Note that the typed iterator will report absent fields. 139 // The representation iterator (if has one) however will skip those. 140 doTemplate(` 141 func (n {{ .Type | TypeSymbol }}) MapIterator() datamodel.MapIterator { 142 return &_{{ .Type | TypeSymbol }}__MapItr{n, 0} 143 } 144 145 type _{{ .Type | TypeSymbol }}__MapItr struct { 146 n {{ .Type | TypeSymbol }} 147 idx int 148 } 149 150 func (itr *_{{ .Type | TypeSymbol }}__MapItr) Next() (k datamodel.Node, v datamodel.Node, _ error) { 151 {{- if not .Type.Fields }} 152 return nil, nil, datamodel.ErrIteratorOverread{} 153 {{ else -}} 154 if itr.idx >= {{ len .Type.Fields }} { 155 return nil, nil, datamodel.ErrIteratorOverread{} 156 } 157 switch itr.idx { 158 {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} 159 {{- range $i, $field := .Type.Fields }} 160 case {{ $i }}: 161 k = &fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} 162 {{- if $field.IsOptional }} 163 if itr.n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Absent { 164 v = datamodel.Absent 165 break 166 } 167 {{- end}} 168 {{- if $field.IsNullable }} 169 if itr.n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Null { 170 v = datamodel.Null 171 break 172 } 173 {{- end}} 174 {{- if $field.IsMaybe }} 175 v = {{if not (MaybeUsesPtr $field.Type) }}&{{end}}itr.n.{{ $field | FieldSymbolLower}}.v 176 {{- else}} 177 v = &itr.n.{{ $field | FieldSymbolLower}} 178 {{- end}} 179 {{- end}} 180 default: 181 panic("unreachable") 182 } 183 itr.idx++ 184 return 185 {{- end}} 186 } 187 func (itr *_{{ .Type | TypeSymbol }}__MapItr) Done() bool { 188 return itr.idx >= {{ len .Type.Fields }} 189 } 190 191 `, w, g.AdjCfg, g) 192 } 193 194 func (g structGenerator) EmitNodeMethodLength(w io.Writer) { 195 doTemplate(` 196 func ({{ .Type | TypeSymbol }}) Length() int64 { 197 return {{ len .Type.Fields }} 198 } 199 `, w, g.AdjCfg, g) 200 } 201 202 func (g structGenerator) EmitNodeMethodPrototype(w io.Writer) { 203 emitNodeMethodPrototype_typical(w, g.AdjCfg, g) 204 } 205 206 func (g structGenerator) EmitNodePrototypeType(w io.Writer) { 207 emitNodePrototypeType_typical(w, g.AdjCfg, g) 208 } 209 210 // --- NodeBuilder and NodeAssembler ---> 211 212 func (g structGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { 213 return structBuilderGenerator{ 214 g.AdjCfg, 215 mixins.MapAssemblerTraits{ 216 PkgName: g.PkgName, 217 TypeName: g.TypeName, 218 AppliedPrefix: "_" + g.AdjCfg.TypeSymbol(g.Type) + "__", 219 }, 220 g.PkgName, 221 g.Type, 222 } 223 } 224 225 type structBuilderGenerator struct { 226 AdjCfg *AdjunctCfg 227 mixins.MapAssemblerTraits 228 PkgName string 229 Type *schema.TypeStruct 230 } 231 232 func (structBuilderGenerator) IsRepr() bool { return false } // hint used in some generalized templates. 233 234 func (g structBuilderGenerator) EmitNodeBuilderType(w io.Writer) { 235 emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) 236 } 237 func (g structBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { 238 emitNodeBuilderMethods_typical(w, g.AdjCfg, g) 239 } 240 func (g structBuilderGenerator) EmitNodeAssemblerType(w io.Writer) { 241 // - 'w' is the "**w**ip" pointer. 242 // - 'm' is the **m**aybe which communicates our completeness to the parent if we're a child assembler. 243 // - '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.) 244 // - 's' is a bitfield for what's been **s**et. 245 // - 'f' is the **f**ocused field that will be assembled next. 246 // 247 // - '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). 248 // ('cm' could be elided for structs where all fields are maybes. trivial but not yet implemented.) 249 // - the 'ca_*' fields embed **c**hild **a**ssemblers -- these are embedded so we can yield pointers to them without causing new allocations. 250 doTemplate(` 251 type _{{ .Type | TypeSymbol }}__Assembler struct { 252 w *_{{ .Type | TypeSymbol }} 253 m *schema.Maybe 254 state maState 255 s int 256 f int 257 258 cm schema.Maybe 259 {{range $field := .Type.Fields -}} 260 ca_{{ $field | FieldSymbolLower }} _{{ $field.Type | TypeSymbol }}__Assembler 261 {{end -}} 262 } 263 264 func (na *_{{ .Type | TypeSymbol }}__Assembler) reset() { 265 na.state = maState_initial 266 na.s = 0 267 {{- range $field := .Type.Fields }} 268 na.ca_{{ $field | FieldSymbolLower }}.reset() 269 {{- end}} 270 } 271 272 var ( 273 {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} 274 {{- range $i, $field := .Type.Fields }} 275 fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} = 1 << {{ $i }} 276 {{- end}} 277 fieldBits__{{ $type | TypeSymbol }}_sufficient = 0 {{- range $i, $field := .Type.Fields }}{{if not $field.IsOptional }} + 1 << {{ $i }}{{end}}{{end}} 278 ) 279 `, w, g.AdjCfg, g) 280 } 281 func (g structBuilderGenerator) EmitNodeAssemblerMethodBeginMap(w io.Writer) { 282 emitNodeAssemblerMethodBeginMap_strictoid(w, g.AdjCfg, g) 283 } 284 func (g structBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { 285 emitNodeAssemblerMethodAssignNull_recursive(w, g.AdjCfg, g) 286 } 287 func (g structBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) { 288 // AssignNode goes through three phases: 289 // 1. is it null? Jump over to AssignNull (which may or may not reject it). 290 // 2. is it our own type? Handle specially -- we might be able to do efficient things. 291 // 3. is it the right kind to morph into us? Do so. 292 // 293 // 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. 294 doTemplate(` 295 func (na *_{{ .Type | TypeSymbol }}__Assembler) AssignNode(v datamodel.Node) error { 296 if v.IsNull() { 297 return na.AssignNull() 298 } 299 if v2, ok := v.(*_{{ .Type | TypeSymbol }}); ok { 300 switch *na.m { 301 case schema.Maybe_Value, schema.Maybe_Null: 302 panic("invalid state: cannot assign into assembler that's already finished") 303 case midvalue: 304 panic("invalid state: cannot assign null into an assembler that's already begun working on recursive structures!") 305 } 306 {{- if .Type | MaybeUsesPtr }} 307 if na.w == nil { 308 na.w = v2 309 *na.m = schema.Maybe_Value 310 return nil 311 } 312 {{- end}} 313 *na.w = *v2 314 *na.m = schema.Maybe_Value 315 return nil 316 } 317 if v.Kind() != datamodel.Kind_Map { 318 return datamodel.ErrWrongKind{TypeName: "{{ .PkgName }}.{{ .Type.Name }}", MethodName: "AssignNode", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: v.Kind()} 319 } 320 itr := v.MapIterator() 321 for !itr.Done() { 322 k, v, err := itr.Next() 323 if err != nil { 324 return err 325 } 326 if err := na.AssembleKey().AssignNode(k); err != nil { 327 return err 328 } 329 if err := na.AssembleValue().AssignNode(v); err != nil { 330 return err 331 } 332 } 333 return na.Finish() 334 } 335 `, w, g.AdjCfg, g) 336 } 337 func (g structBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { 338 g.emitMapAssemblerChildTidyHelper(w) 339 g.emitMapAssemblerMethods(w) 340 g.emitKeyAssembler(w) 341 } 342 func (g structBuilderGenerator) emitMapAssemblerChildTidyHelper(w io.Writer) { 343 // This function attempts to clean up the state machine to acknolwedge child assembly finish. 344 // If the child was finished and we just collected it, return true and update state to maState_initial. 345 // Otherwise, if it wasn't done, return false; 346 // and the caller is almost certain to emit an error momentarily. 347 // The function will only be called when the current state is maState_midValue. 348 // (In general, the idea is that if the user is doing things correctly, 349 // this function will only be called when the child is in fact finished.) 350 // Most of the logic here is about nullables and not optionals, 351 // because if you're an optional that's absent, you never got to value assembly. 352 // There's still one branch for optionals, though, because they have a different residence for 'm' just as nullables do. 353 // Child assemblers are expected to control their own state machines; 354 // for values that have maybes, we never change their maybe state again, so the usual logic should hold; 355 // for values that don't have maybes (and thus share 'cm')... 356 // We don't bother to nil their 'm' pointer; the worst that can happen is an over-held assembler for that field 357 // can make a bizarre and broken transition for a subsequent field, which will result in very ugly errors, but isn't unsafe per se. 358 // We do nil their 'w' pointer, though: we don't want a set to that able to leak in later if we're on the way to Finish! 359 doTemplate(` 360 func (ma *_{{ .Type | TypeSymbol }}__Assembler) valueFinishTidy() bool { 361 switch ma.f { 362 {{- range $i, $field := .Type.Fields }} 363 case {{ $i }}: 364 {{- if $field.IsNullable }} 365 switch ma.w.{{ $field | FieldSymbolLower }}.m { 366 case schema.Maybe_Null: 367 ma.state = maState_initial 368 return true 369 case schema.Maybe_Value: 370 {{- if (MaybeUsesPtr $field.Type) }} 371 ma.w.{{ $field | FieldSymbolLower }}.v = ma.ca_{{ $field | FieldSymbolLower }}.w 372 {{- end}} 373 ma.state = maState_initial 374 return true 375 default: 376 return false 377 } 378 {{- else if $field.IsOptional }} 379 switch ma.w.{{ $field | FieldSymbolLower }}.m { 380 case schema.Maybe_Value: 381 {{- if (MaybeUsesPtr $field.Type) }} 382 ma.w.{{ $field | FieldSymbolLower }}.v = ma.ca_{{ $field | FieldSymbolLower }}.w 383 {{- end}} 384 ma.state = maState_initial 385 return true 386 default: 387 return false 388 } 389 {{- else}} 390 switch ma.cm { 391 case schema.Maybe_Value: 392 ma.ca_{{ $field | FieldSymbolLower }}.w = nil 393 ma.cm = schema.Maybe_Absent 394 ma.state = maState_initial 395 return true 396 default: 397 return false 398 } 399 {{- end}} 400 {{- end}} 401 default: 402 panic("unreachable") 403 } 404 } 405 `, w, g.AdjCfg, g) 406 } 407 func (g structBuilderGenerator) emitMapAssemblerMethods(w io.Writer) { 408 // FUTURE: some of the setup of the child assemblers could probably be DRY'd up. 409 doTemplate(` 410 func (ma *_{{ .Type | TypeSymbol }}__Assembler) AssembleEntry(k string) (datamodel.NodeAssembler, error) { 411 switch ma.state { 412 case maState_initial: 413 // carry on 414 case maState_midKey: 415 panic("invalid state: AssembleEntry cannot be called when in the middle of assembling another key") 416 case maState_expectValue: 417 panic("invalid state: AssembleEntry cannot be called when expecting start of value assembly") 418 case maState_midValue: 419 if !ma.valueFinishTidy() { 420 panic("invalid state: AssembleEntry cannot be called when in the middle of assembling a value") 421 } // if tidy success: carry on 422 case maState_finished: 423 panic("invalid state: AssembleEntry cannot be called on an assembler that's already finished") 424 } 425 {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} 426 {{- if .Type.Fields }} 427 switch k { 428 {{- range $i, $field := .Type.Fields }} 429 case "{{ $field.Name }}": 430 if ma.s & fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} != 0 { 431 return nil, datamodel.ErrRepeatedMapKey{Key: &fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}} 432 } 433 ma.s += fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} 434 ma.state = maState_midValue 435 ma.f = {{ $i }} 436 {{- if $field.IsMaybe }} 437 ma.ca_{{ $field | FieldSymbolLower }}.w = {{if not (MaybeUsesPtr $field.Type) }}&{{end}}ma.w.{{ $field | FieldSymbolLower }}.v 438 ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.w.{{ $field | FieldSymbolLower }}.m 439 {{- if $field.IsNullable }} 440 ma.w.{{ $field | FieldSymbolLower }}.m = allowNull 441 {{- end}} 442 {{- else}} 443 ma.ca_{{ $field | FieldSymbolLower }}.w = &ma.w.{{ $field | FieldSymbolLower }} 444 ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.cm 445 {{- end}} 446 return &ma.ca_{{ $field | FieldSymbolLower }}, nil 447 {{- end}} 448 } 449 {{- end}} 450 return nil, schema.ErrInvalidKey{TypeName:"{{ .PkgName }}.{{ .Type.Name }}", Key:&_String{k}} 451 } 452 func (ma *_{{ .Type | TypeSymbol }}__Assembler) AssembleKey() datamodel.NodeAssembler { 453 switch ma.state { 454 case maState_initial: 455 // carry on 456 case maState_midKey: 457 panic("invalid state: AssembleKey cannot be called when in the middle of assembling another key") 458 case maState_expectValue: 459 panic("invalid state: AssembleKey cannot be called when expecting start of value assembly") 460 case maState_midValue: 461 if !ma.valueFinishTidy() { 462 panic("invalid state: AssembleKey cannot be called when in the middle of assembling a value") 463 } // if tidy success: carry on 464 case maState_finished: 465 panic("invalid state: AssembleKey cannot be called on an assembler that's already finished") 466 } 467 ma.state = maState_midKey 468 return (*_{{ .Type | TypeSymbol }}__KeyAssembler)(ma) 469 } 470 func (ma *_{{ .Type | TypeSymbol }}__Assembler) AssembleValue() datamodel.NodeAssembler { 471 switch ma.state { 472 case maState_initial: 473 panic("invalid state: AssembleValue cannot be called when no key is primed") 474 case maState_midKey: 475 panic("invalid state: AssembleValue cannot be called when in the middle of assembling a key") 476 case maState_expectValue: 477 // carry on 478 case maState_midValue: 479 panic("invalid state: AssembleValue cannot be called when in the middle of assembling another value") 480 case maState_finished: 481 panic("invalid state: AssembleValue cannot be called on an assembler that's already finished") 482 } 483 ma.state = maState_midValue 484 switch ma.f { 485 {{- range $i, $field := .Type.Fields }} 486 case {{ $i }}: 487 {{- if $field.IsMaybe }} 488 ma.ca_{{ $field | FieldSymbolLower }}.w = {{if not (MaybeUsesPtr $field.Type) }}&{{end}}ma.w.{{ $field | FieldSymbolLower }}.v 489 ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.w.{{ $field | FieldSymbolLower }}.m 490 {{- if $field.IsNullable }} 491 ma.w.{{ $field | FieldSymbolLower }}.m = allowNull 492 {{- end}} 493 {{- else}} 494 ma.ca_{{ $field | FieldSymbolLower }}.w = &ma.w.{{ $field | FieldSymbolLower }} 495 ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.cm 496 {{- end}} 497 return &ma.ca_{{ $field | FieldSymbolLower }} 498 {{- end}} 499 default: 500 panic("unreachable") 501 } 502 } 503 func (ma *_{{ .Type | TypeSymbol }}__Assembler) Finish() error { 504 switch ma.state { 505 case maState_initial: 506 // carry on 507 case maState_midKey: 508 panic("invalid state: Finish cannot be called when in the middle of assembling a key") 509 case maState_expectValue: 510 panic("invalid state: Finish cannot be called when expecting start of value assembly") 511 case maState_midValue: 512 if !ma.valueFinishTidy() { 513 panic("invalid state: Finish cannot be called when in the middle of assembling a value") 514 } // if tidy success: carry on 515 case maState_finished: 516 panic("invalid state: Finish cannot be called on an assembler that's already finished") 517 } 518 if ma.s & fieldBits__{{ $type | TypeSymbol }}_sufficient != fieldBits__{{ $type | TypeSymbol }}_sufficient { 519 err := schema.ErrMissingRequiredField{Missing: make([]string, 0)} 520 {{- range $i, $field := .Type.Fields }} 521 {{- if not $field.IsMaybe}} 522 if ma.s & fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} == 0 { 523 err.Missing = append(err.Missing, "{{ $field.Name }}") 524 } 525 {{- end}} 526 {{- end}} 527 return err 528 } 529 ma.state = maState_finished 530 *ma.m = schema.Maybe_Value 531 return nil 532 } 533 func (ma *_{{ .Type | TypeSymbol }}__Assembler) KeyPrototype() datamodel.NodePrototype { 534 return _String__Prototype{} 535 } 536 func (ma *_{{ .Type | TypeSymbol }}__Assembler) ValuePrototype(k string) datamodel.NodePrototype { 537 panic("todo structbuilder mapassembler valueprototype") 538 } 539 `, w, g.AdjCfg, g) 540 } 541 func (g structBuilderGenerator) emitKeyAssembler(w io.Writer) { 542 doTemplate(` 543 type _{{ .Type | TypeSymbol }}__KeyAssembler _{{ .Type | TypeSymbol }}__Assembler 544 `, w, g.AdjCfg, g) 545 stubs := mixins.StringAssemblerTraits{ 546 PkgName: g.PkgName, 547 TypeName: g.TypeName + ".KeyAssembler", 548 AppliedPrefix: "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Key", 549 } 550 // This key assembler can disregard any idea of complex keys because it's a struct! 551 // Struct field names must be strings (and quite simple ones at that). 552 stubs.EmitNodeAssemblerMethodBeginMap(w) 553 stubs.EmitNodeAssemblerMethodBeginList(w) 554 stubs.EmitNodeAssemblerMethodAssignNull(w) 555 stubs.EmitNodeAssemblerMethodAssignBool(w) 556 stubs.EmitNodeAssemblerMethodAssignInt(w) 557 stubs.EmitNodeAssemblerMethodAssignFloat(w) 558 doTemplate(` 559 func (ka *_{{ .Type | TypeSymbol }}__KeyAssembler) AssignString(k string) error { 560 if ka.state != maState_midKey { 561 panic("misuse: KeyAssembler held beyond its valid lifetime") 562 } 563 switch k { 564 {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} 565 {{- range $i, $field := .Type.Fields }} 566 case "{{ $field.Name }}": 567 if ka.s & fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} != 0 { 568 return datamodel.ErrRepeatedMapKey{Key: &fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}} 569 } 570 ka.s += fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} 571 ka.state = maState_expectValue 572 ka.f = {{ $i }} 573 return nil 574 {{- end}} 575 default: 576 return schema.ErrInvalidKey{TypeName:"{{ .PkgName }}.{{ .Type.Name }}", Key:&_String{k}} 577 } 578 } 579 `, w, g.AdjCfg, g) 580 stubs.EmitNodeAssemblerMethodAssignBytes(w) 581 stubs.EmitNodeAssemblerMethodAssignLink(w) 582 doTemplate(` 583 func (ka *_{{ .Type | TypeSymbol }}__KeyAssembler) AssignNode(v datamodel.Node) error { 584 if v2, err := v.AsString(); err != nil { 585 return err 586 } else { 587 return ka.AssignString(v2) 588 } 589 } 590 func (_{{ .Type | TypeSymbol }}__KeyAssembler) Prototype() datamodel.NodePrototype { 591 return _String__Prototype{} 592 } 593 `, w, g.AdjCfg, g) 594 }