node.go 24.5 KB
Newer Older
1 2 3 4 5 6
package bindnode

import (
	"fmt"
	"reflect"

tavit ohanian's avatar
tavit ohanian committed
7
	ld "gitlab.dms3.io/ld/go-ld-prime"
tavit ohanian's avatar
tavit ohanian committed
8 9
	basicnode "gitlab.dms3.io/ld/go-ld-prime/node/basic"
	"gitlab.dms3.io/ld/go-ld-prime/schema"
10 11
)

12 13
// Assert that we implement all the interfaces as expected.
// Grouped by the interfaces to implement, roughly.
14
var (
tavit ohanian's avatar
tavit ohanian committed
15
	_ ld.NodePrototype      = (*_prototype)(nil)
Daniel Martí's avatar
Daniel Martí committed
16
	_ schema.TypedPrototype = (*_prototype)(nil)
tavit ohanian's avatar
tavit ohanian committed
17
	_ ld.NodePrototype      = (*_prototypeRepr)(nil)
18

tavit ohanian's avatar
tavit ohanian committed
19
	_ ld.Node          = (*_node)(nil)
20
	_ schema.TypedNode = (*_node)(nil)
tavit ohanian's avatar
tavit ohanian committed
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
	_ ld.Node          = (*_nodeRepr)(nil)

	_ ld.NodeBuilder   = (*_builder)(nil)
	_ ld.NodeAssembler = (*_assembler)(nil)
	_ ld.NodeBuilder   = (*_builderRepr)(nil)
	_ ld.NodeAssembler = (*_assemblerRepr)(nil)

	_ ld.MapAssembler = (*_structAssembler)(nil)
	_ ld.MapIterator  = (*_structIterator)(nil)
	_ ld.MapAssembler = (*_structAssemblerRepr)(nil)
	_ ld.MapIterator  = (*_structIteratorRepr)(nil)

	_ ld.ListAssembler = (*_listAssembler)(nil)
	_ ld.ListIterator  = (*_listIterator)(nil)
	_ ld.ListAssembler = (*_listAssemblerRepr)(nil)

	_ ld.MapAssembler = (*_unionAssembler)(nil)
	_ ld.MapIterator  = (*_unionIterator)(nil)
	_ ld.MapAssembler = (*_unionAssemblerRepr)(nil)
	_ ld.MapIterator  = (*_unionIteratorRepr)(nil)
41 42 43 44 45 46 47
)

type _prototype struct {
	schemaType schema.Type
	goType     reflect.Type // non-pointer
}

tavit ohanian's avatar
tavit ohanian committed
48
func (w *_prototype) NewBuilder() ld.NodeBuilder {
49 50 51 52 53 54
	return &_builder{_assembler{
		schemaType: w.schemaType,
		val:        reflect.New(w.goType).Elem(),
	}}
}

Daniel Martí's avatar
Daniel Martí committed
55 56
func (w *_prototype) Type() schema.Type {
	return w.schemaType
57 58
}

tavit ohanian's avatar
tavit ohanian committed
59
func (w *_prototype) Representation() ld.NodePrototype {
60 61 62 63
	return (*_prototypeRepr)(w)
}

var (
64
	goTypeBool   = reflect.TypeOf(false)
65
	goTypeInt    = reflect.TypeOf(int(0))
66 67
	goTypeFloat  = reflect.TypeOf(0.0)
	goTypeString = reflect.TypeOf("")
68
	goTypeBytes  = reflect.TypeOf([]byte{})
tavit ohanian's avatar
tavit ohanian committed
69
	goTypeLink   = reflect.TypeOf((*ld.Link)(nil)).Elem()
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

	schemaTypeFieldName = schema.SpawnString("fieldNameString")
)

type _node struct {
	schemaType schema.Type

	val reflect.Value // non-pointer
}

// TODO: only expose TypedNode methods if the schema was explicit.
// type _typedNode struct {
// 	_node
// }

func (w *_node) Type() schema.Type {
	return w.schemaType
}

tavit ohanian's avatar
tavit ohanian committed
89
func (w *_node) Representation() ld.Node {
90 91 92
	return (*_nodeRepr)(w)
}

tavit ohanian's avatar
tavit ohanian committed
93
func (w *_node) Kind() ld.Kind {
94 95 96
	return w.schemaType.TypeKind().ActsLike()
}

tavit ohanian's avatar
tavit ohanian committed
97
func (w *_node) LookupByString(key string) (ld.Node, error) {
98 99 100 101
	switch typ := w.schemaType.(type) {
	case *schema.TypeStruct:
		field := typ.Field(key)
		if field == nil {
tavit ohanian's avatar
tavit ohanian committed
102
			return nil, ld.ErrInvalidKey{
103 104 105 106 107 108 109 110 111 112
				TypeName: typ.Name().String(),
				Key:      basicnode.NewString(key),
			}
		}
		fval := w.val.FieldByName(fieldNameFromSchema(key))
		if !fval.IsValid() {
			panic("TODO: go-schema mismatch")
		}
		if field.IsOptional() {
			if fval.IsNil() {
tavit ohanian's avatar
tavit ohanian committed
113
				return ld.Absent, nil
114 115 116 117 118
			}
			fval = fval.Elem()
		}
		if field.IsNullable() {
			if fval.IsNil() {
tavit ohanian's avatar
tavit ohanian committed
119
				return ld.Null, nil
120 121 122 123 124 125 126 127 128 129
			}
			fval = fval.Elem()
		}
		node := &_node{
			schemaType: field.Type(),
			val:        fval,
		}
		return node, nil
	case *schema.TypeMap:
		var kval reflect.Value
130
		valuesVal := w.val.FieldByName("Values")
131 132 133 134 135 136
		switch ktyp := typ.KeyType().(type) {
		case *schema.TypeString:
			kval = reflect.ValueOf(key)
		default:
			asm := &_assembler{
				schemaType: ktyp,
137
				val:        reflect.New(valuesVal.Type().Key()).Elem(),
138 139 140 141 142 143
			}
			if err := (*_assemblerRepr)(asm).AssignString(key); err != nil {
				return nil, err
			}
			kval = asm.val
		}
144
		fval := valuesVal.MapIndex(kval)
145
		if !fval.IsValid() { // not found
tavit ohanian's avatar
tavit ohanian committed
146
			return nil, ld.ErrNotExists{Segment: ld.PathSegmentOfString(key)}
147 148 149
		}
		// TODO: Error/panic if fval.IsNil() && !typ.ValueIsNullable()?
		// Otherwise we could have two non-equal Go values (nil map,
150
		// non-nil-but-empty map) which represent the exact same LD
151 152 153
		// node when the field is not nullable.
		if typ.ValueIsNullable() {
			if fval.IsNil() {
tavit ohanian's avatar
tavit ohanian committed
154
				return ld.Null, nil
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
			}
			fval = fval.Elem()
		}
		node := &_node{
			schemaType: typ.ValueType(),
			val:        fval,
		}
		return node, nil
	case *schema.TypeUnion:
		var idx int
		var mtyp schema.Type
		for i, member := range typ.Members() {
			if member.Name().String() == key {
				idx = i
				mtyp = member
				break
			}
		}
		if mtyp == nil { // not found
tavit ohanian's avatar
tavit ohanian committed
174
			return nil, ld.ErrNotExists{Segment: ld.PathSegmentOfString(key)}
175 176 177
		}
		haveIdx := int(w.val.FieldByName("Index").Int())
		if haveIdx != idx { // mismatching type
tavit ohanian's avatar
tavit ohanian committed
178
			return nil, ld.ErrNotExists{Segment: ld.PathSegmentOfString(key)}
179 180 181 182 183 184 185 186
		}
		mval := w.val.FieldByName("Value").Elem()
		node := &_node{
			schemaType: mtyp,
			val:        mval,
		}
		return node, nil
	}
tavit ohanian's avatar
tavit ohanian committed
187
	return nil, ld.ErrWrongKind{
188 189 190 191 192 193
		TypeName:   w.schemaType.Name().String(),
		MethodName: "LookupByString",
		// TODO
	}
}

tavit ohanian's avatar
tavit ohanian committed
194
func (w *_node) LookupByIndex(idx int64) (ld.Node, error) {
195 196 197
	switch typ := w.schemaType.(type) {
	case *schema.TypeList:
		if idx < 0 || int(idx) >= w.val.Len() {
tavit ohanian's avatar
tavit ohanian committed
198
			return nil, ld.ErrNotExists{Segment: ld.PathSegmentOfInt(idx)}
199 200 201 202
		}
		val := w.val.Index(int(idx))
		if typ.ValueIsNullable() {
			if val.IsNil() {
tavit ohanian's avatar
tavit ohanian committed
203
				return ld.Null, nil
204 205 206 207 208
			}
			val = val.Elem()
		}
		return &_node{schemaType: typ.ValueType(), val: val}, nil
	}
tavit ohanian's avatar
tavit ohanian committed
209
	return nil, ld.ErrWrongKind{
210 211 212 213 214 215
		TypeName:   w.schemaType.Name().String(),
		MethodName: "LookupByIndex",
		// TODO
	}
}

tavit ohanian's avatar
tavit ohanian committed
216
func (w *_node) LookupBySegment(seg ld.PathSegment) (ld.Node, error) {
217
	switch w.Kind() {
tavit ohanian's avatar
tavit ohanian committed
218
	case ld.Kind_Map:
219
		return w.LookupByString(seg.String())
tavit ohanian's avatar
tavit ohanian committed
220
	case ld.Kind_List:
221 222 223 224 225 226
		idx, err := seg.Index()
		if err != nil {
			return nil, err
		}
		return w.LookupByIndex(idx)
	}
tavit ohanian's avatar
tavit ohanian committed
227
	return nil, ld.ErrWrongKind{
228 229 230 231 232 233
		TypeName:   w.schemaType.Name().String(),
		MethodName: "LookupBySegment",
		// TODO
	}
}

tavit ohanian's avatar
tavit ohanian committed
234
func (w *_node) LookupByNode(key ld.Node) (ld.Node, error) {
235
	switch w.Kind() {
tavit ohanian's avatar
tavit ohanian committed
236
	case ld.Kind_Map:
237 238 239 240 241
		s, err := key.AsString()
		if err != nil {
			return nil, err
		}
		return w.LookupByString(s)
tavit ohanian's avatar
tavit ohanian committed
242
	case ld.Kind_List:
243 244 245 246 247 248
		i, err := key.AsInt()
		if err != nil {
			return nil, err
		}
		return w.LookupByIndex(i)
	}
tavit ohanian's avatar
tavit ohanian committed
249
	return nil, ld.ErrWrongKind{
250 251 252 253
		TypeName:   w.schemaType.Name().String(),
		MethodName: "LookupByNode",
		// TODO
	}
254 255
}

tavit ohanian's avatar
tavit ohanian committed
256
func (w *_node) MapIterator() ld.MapIterator {
257 258 259 260 261 262 263 264 265 266 267 268 269 270
	switch typ := w.schemaType.(type) {
	case *schema.TypeStruct:
		return &_structIterator{
			schemaType: typ,
			fields:     typ.Fields(),
			val:        w.val,
		}
	case *schema.TypeUnion:
		return &_unionIterator{
			schemaType: typ,
			members:    typ.Members(),
			val:        w.val,
		}
	case *schema.TypeMap:
271 272 273 274 275
		return &_mapIterator{
			schemaType: typ,
			keysVal:    w.val.FieldByName("Keys"),
			valuesVal:  w.val.FieldByName("Values"),
		}
276 277 278 279
	}
	return nil
}

tavit ohanian's avatar
tavit ohanian committed
280
func (w *_node) ListIterator() ld.ListIterator {
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
	val := w.val
	if val.Type().Kind() == reflect.Ptr {
		if !val.IsNil() {
			val = val.Elem()
		}
	}
	switch typ := w.schemaType.(type) {
	case *schema.TypeList:
		return &_listIterator{schemaType: typ, val: val}
	}
	return nil
}

func (w *_node) Length() int64 {
	switch w.Kind() {
tavit ohanian's avatar
tavit ohanian committed
296
	case ld.Kind_Map:
297 298 299 300 301 302
		switch typ := w.schemaType.(type) {
		case *schema.TypeStruct:
			return int64(len(typ.Fields()))
		case *schema.TypeUnion:
			return 1
		}
303
		return int64(w.val.FieldByName("Keys").Len())
tavit ohanian's avatar
tavit ohanian committed
304
	case ld.Kind_List:
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
		return int64(w.val.Len())
	}
	return -1
}

// TODO: better story around pointers and absent/null

func (w *_node) IsAbsent() bool {
	return false
}

func (w *_node) IsNull() bool {
	return false
}

func (w *_node) AsBool() (bool, error) {
tavit ohanian's avatar
tavit ohanian committed
321 322
	if w.Kind() != ld.Kind_Bool {
		return false, ld.ErrWrongKind{
323 324 325 326 327 328
			TypeName:   w.schemaType.Name().String(),
			MethodName: "AsBool",
			// TODO
		}
	}
	return w.val.Bool(), nil
329 330 331
}

func (w *_node) AsInt() (int64, error) {
tavit ohanian's avatar
tavit ohanian committed
332 333
	if w.Kind() != ld.Kind_Int {
		return 0, ld.ErrWrongKind{
334 335 336 337 338 339 340 341 342
			TypeName:   w.schemaType.Name().String(),
			MethodName: "AsInt",
			// TODO
		}
	}
	return w.val.Int(), nil
}

func (w *_node) AsFloat() (float64, error) {
tavit ohanian's avatar
tavit ohanian committed
343 344
	if w.Kind() != ld.Kind_Float {
		return 0, ld.ErrWrongKind{
345 346 347 348 349 350
			TypeName:   w.schemaType.Name().String(),
			MethodName: "AsFloat",
			// TODO
		}
	}
	return w.val.Float(), nil
351 352 353
}

func (w *_node) AsString() (string, error) {
tavit ohanian's avatar
tavit ohanian committed
354 355
	if w.Kind() != ld.Kind_String {
		return "", ld.ErrWrongKind{
356 357 358 359 360 361 362 363 364
			TypeName:   w.schemaType.Name().String(),
			MethodName: "AsString",
			// TODO
		}
	}
	return w.val.String(), nil
}

func (w *_node) AsBytes() ([]byte, error) {
tavit ohanian's avatar
tavit ohanian committed
365 366
	if w.Kind() != ld.Kind_Bytes {
		return nil, ld.ErrWrongKind{
367 368 369 370 371 372 373 374
			TypeName:   w.schemaType.Name().String(),
			MethodName: "AsBytes",
			// TODO
		}
	}
	return w.val.Bytes(), nil
}

tavit ohanian's avatar
tavit ohanian committed
375 376 377
func (w *_node) AsLink() (ld.Link, error) {
	if w.Kind() != ld.Kind_Link {
		return nil, ld.ErrWrongKind{
378 379 380 381 382
			TypeName:   w.schemaType.Name().String(),
			MethodName: "AsLink",
			// TODO
		}
	}
tavit ohanian's avatar
tavit ohanian committed
383
	link, _ := w.val.Interface().(ld.Link)
384 385 386
	return link, nil
}

tavit ohanian's avatar
tavit ohanian committed
387
func (w *_node) Prototype() ld.NodePrototype {
388 389 390 391 392 393 394
	panic("TODO: Prototype")
}

type _builder struct {
	_assembler
}

tavit ohanian's avatar
tavit ohanian committed
395
func (w *_builder) Build() ld.Node {
396
	// TODO: should we panic if no Assign call was made, just like codegen?
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
	return &_node{schemaType: w.schemaType, val: w.val}
}

func (w *_builder) Reset() {
	panic("TODO: Reset")
}

type _assembler struct {
	schemaType schema.Type
	val        reflect.Value // non-pointer
	finish     func() error

	// kinded   bool // true if val is interface{} for a kinded union
	nullable bool // true if field or map value is nullable
}

func (w *_assembler) nonPtrVal() reflect.Value {
	val := w.val
	if w.nullable {
		val.Set(reflect.New(val.Type().Elem()))
		val = val.Elem()
	}
	return val
}

tavit ohanian's avatar
tavit ohanian committed
422
func (w *_assembler) kind() ld.Kind {
423 424 425
	return w.schemaType.TypeKind().ActsLike()
}

tavit ohanian's avatar
tavit ohanian committed
426
func (w *_assembler) Representation() ld.NodeAssembler {
427 428 429
	return (*_assemblerRepr)(w)
}

tavit ohanian's avatar
tavit ohanian committed
430
func (w *_assembler) BeginMap(sizeHint int64) (ld.MapAssembler, error) {
431 432 433 434 435 436 437 438 439 440 441 442
	switch typ := w.schemaType.(type) {
	case *schema.TypeStruct:
		val := w.nonPtrVal()
		doneFields := make([]bool, val.NumField())
		return &_structAssembler{
			schemaType: typ,
			val:        val,
			doneFields: doneFields,
			finish:     w.finish,
		}, nil
	case *schema.TypeMap:
		val := w.nonPtrVal()
443 444 445 446
		keysVal := val.FieldByName("Keys")
		valuesVal := val.FieldByName("Values")
		if valuesVal.IsNil() {
			valuesVal.Set(reflect.MakeMap(valuesVal.Type()))
447 448 449
		}
		return &_mapAssembler{
			schemaType: typ,
450 451
			keysVal:    keysVal,
			valuesVal:  valuesVal,
452 453 454 455 456 457 458 459 460 461
			finish:     w.finish,
		}, nil
	case *schema.TypeUnion:
		val := w.nonPtrVal()
		return &_unionAssembler{
			schemaType: typ,
			val:        val,
			finish:     w.finish,
		}, nil
	}
tavit ohanian's avatar
tavit ohanian committed
462
	return nil, ld.ErrWrongKind{
463 464 465 466 467 468
		TypeName:   w.schemaType.Name().String(),
		MethodName: "BeginMap",
		// TODO
	}
}

tavit ohanian's avatar
tavit ohanian committed
469
func (w *_assembler) BeginList(sizeHint int64) (ld.ListAssembler, error) {
470 471 472 473 474 475 476 477 478
	switch typ := w.schemaType.(type) {
	case *schema.TypeList:
		val := w.nonPtrVal()
		return &_listAssembler{
			schemaType: typ,
			val:        val,
			finish:     w.finish,
		}, nil
	}
tavit ohanian's avatar
tavit ohanian committed
479
	return nil, ld.ErrWrongKind{
480 481 482 483 484 485 486 487
		TypeName:   w.schemaType.Name().String(),
		MethodName: "BeginList",
		// TODO
	}
}

func (w *_assembler) AssignNull() error {
	if !w.nullable {
tavit ohanian's avatar
tavit ohanian committed
488
		return ld.ErrWrongKind{
489 490 491 492 493 494 495 496 497 498 499 500 501 502
			TypeName:   w.schemaType.Name().String(),
			MethodName: "AssignNull",
			// TODO
		}
	}
	w.val.Set(reflect.Zero(w.val.Type()))
	if w.finish != nil {
		if err := w.finish(); err != nil {
			return err
		}
	}
	return nil
}

503
func (w *_assembler) AssignBool(b bool) error {
tavit ohanian's avatar
tavit ohanian committed
504 505
	if w.kind() != ld.Kind_Bool {
		return ld.ErrWrongKind{
506 507
			TypeName:        w.schemaType.Name().String(),
			MethodName:      "AssignBool",
tavit ohanian's avatar
tavit ohanian committed
508
			AppropriateKind: ld.KindSet{ld.Kind_Bool},
509 510 511 512 513 514 515 516 517 518
			ActualKind:      w.kind(),
		}
	}
	w.nonPtrVal().SetBool(b)
	if w.finish != nil {
		if err := w.finish(); err != nil {
			return err
		}
	}
	return nil
519 520 521
}

func (w *_assembler) AssignInt(i int64) error {
tavit ohanian's avatar
tavit ohanian committed
522 523
	if w.kind() != ld.Kind_Int {
		return ld.ErrWrongKind{
524 525
			TypeName:        w.schemaType.Name().String(),
			MethodName:      "AssignInt",
tavit ohanian's avatar
tavit ohanian committed
526
			AppropriateKind: ld.KindSet{ld.Kind_Int},
527 528 529 530 531 532 533 534 535 536 537 538
			ActualKind:      w.kind(),
		}
	}
	w.nonPtrVal().SetInt(i)
	if w.finish != nil {
		if err := w.finish(); err != nil {
			return err
		}
	}
	return nil
}

539
func (w *_assembler) AssignFloat(f float64) error {
tavit ohanian's avatar
tavit ohanian committed
540 541
	if w.kind() != ld.Kind_Float {
		return ld.ErrWrongKind{
542 543
			TypeName:        w.schemaType.Name().String(),
			MethodName:      "AssignFloat",
tavit ohanian's avatar
tavit ohanian committed
544
			AppropriateKind: ld.KindSet{ld.Kind_Float},
545 546 547 548 549 550 551 552 553 554
			ActualKind:      w.kind(),
		}
	}
	w.nonPtrVal().SetFloat(f)
	if w.finish != nil {
		if err := w.finish(); err != nil {
			return err
		}
	}
	return nil
555 556 557
}

func (w *_assembler) AssignString(s string) error {
tavit ohanian's avatar
tavit ohanian committed
558 559
	if w.kind() != ld.Kind_String {
		return ld.ErrWrongKind{
560 561
			TypeName:        w.schemaType.Name().String(),
			MethodName:      "AssignString",
tavit ohanian's avatar
tavit ohanian committed
562
			AppropriateKind: ld.KindSet{ld.Kind_String},
563 564 565 566 567 568 569 570 571 572 573 574 575
			ActualKind:      w.kind(),
		}
	}
	w.nonPtrVal().SetString(s)
	if w.finish != nil {
		if err := w.finish(); err != nil {
			return err
		}
	}
	return nil
}

func (w *_assembler) AssignBytes(p []byte) error {
tavit ohanian's avatar
tavit ohanian committed
576 577
	if w.kind() != ld.Kind_Bytes {
		return ld.ErrWrongKind{
578 579
			TypeName:        w.schemaType.Name().String(),
			MethodName:      "AssignBytes",
tavit ohanian's avatar
tavit ohanian committed
580
			AppropriateKind: ld.KindSet{ld.Kind_Bytes},
581 582 583 584 585 586 587
			ActualKind:      w.kind(),
		}
	}
	w.nonPtrVal().SetBytes(p)
	return nil
}

tavit ohanian's avatar
tavit ohanian committed
588
func (w *_assembler) AssignLink(link ld.Link) error {
589 590
	newVal := reflect.ValueOf(link)
	if !newVal.Type().AssignableTo(w.val.Type()) {
tavit ohanian's avatar
tavit ohanian committed
591
		return ld.ErrWrongKind{
592 593 594 595 596 597 598 599 600 601 602 603 604 605
			TypeName:   w.schemaType.Name().String(),
			MethodName: "AssignLink",
			// TODO
		}
	}
	w.nonPtrVal().Set(newVal)
	if w.finish != nil {
		if err := w.finish(); err != nil {
			return err
		}
	}
	return nil
}

tavit ohanian's avatar
tavit ohanian committed
606
func (w *_assembler) AssignNode(node ld.Node) error {
607 608 609 610 611 612 613
	// TODO: does this ever trigger?
	// newVal := reflect.ValueOf(node)
	// if newVal.Type().AssignableTo(w.val.Type()) {
	// 	w.val.Set(newVal)
	// 	return nil
	// }
	switch node.Kind() {
tavit ohanian's avatar
tavit ohanian committed
614
	case ld.Kind_Map:
615 616
		itr := node.MapIterator()
		// TODO: consider reusing this code from elsewhere,
tavit ohanian's avatar
tavit ohanian committed
617
		// via something like ld.BlindCopyMap.
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
		am, err := w.BeginMap(-1) // TODO: length?
		if err != nil {
			return err
		}
		for !itr.Done() {
			k, v, err := itr.Next()
			if err != nil {
				return err
			}
			if err := am.AssembleKey().AssignNode(k); err != nil {
				return err
			}
			if err := am.AssembleValue().AssignNode(v); err != nil {
				return err
			}
		}
		return am.Finish()
tavit ohanian's avatar
tavit ohanian committed
635
	case ld.Kind_List:
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
		itr := node.ListIterator()
		am, err := w.BeginList(-1) // TODO: length?
		if err != nil {
			return err
		}
		for !itr.Done() {
			_, v, err := itr.Next()
			if err != nil {
				return err
			}
			if err := am.AssembleValue().AssignNode(v); err != nil {
				return err
			}
		}
		return am.Finish()

tavit ohanian's avatar
tavit ohanian committed
652
	case ld.Kind_Bool:
653 654 655 656 657
		b, err := node.AsBool()
		if err != nil {
			return err
		}
		return w.AssignBool(b)
tavit ohanian's avatar
tavit ohanian committed
658
	case ld.Kind_Int:
659 660 661 662 663
		i, err := node.AsInt()
		if err != nil {
			return err
		}
		return w.AssignInt(i)
tavit ohanian's avatar
tavit ohanian committed
664
	case ld.Kind_Float:
665 666 667 668 669
		f, err := node.AsFloat()
		if err != nil {
			return err
		}
		return w.AssignFloat(f)
tavit ohanian's avatar
tavit ohanian committed
670
	case ld.Kind_String:
671 672 673 674 675
		s, err := node.AsString()
		if err != nil {
			return err
		}
		return w.AssignString(s)
tavit ohanian's avatar
tavit ohanian committed
676
	case ld.Kind_Bytes:
677 678 679 680 681
		p, err := node.AsBytes()
		if err != nil {
			return err
		}
		return w.AssignBytes(p)
tavit ohanian's avatar
tavit ohanian committed
682
	case ld.Kind_Link:
683 684 685 686 687
		l, err := node.AsLink()
		if err != nil {
			return err
		}
		return w.AssignLink(l)
tavit ohanian's avatar
tavit ohanian committed
688
	case ld.Kind_Null:
689 690 691 692 693 694
		return w.AssignNull()
	}
	// fmt.Println(w.val.Type(), reflect.TypeOf(node))
	panic(fmt.Sprintf("TODO: %v %v", w.val.Type(), node.Kind()))
}

tavit ohanian's avatar
tavit ohanian committed
695
func (w *_assembler) Prototype() ld.NodePrototype {
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
	panic("TODO: Assembler.Prototype")
}

type _structAssembler struct {
	// TODO: embed _assembler?

	schemaType *schema.TypeStruct
	val        reflect.Value // non-pointer
	finish     func() error

	// TODO: more state checks

	// TODO: Consider if we could do this in a cheaper way,
	// such as looking at the reflect.Value directly.
	// If not, at least avoid an extra alloc.
	doneFields []bool

	// TODO: optimize for structs

	curKey _assembler

	nextIndex int // only used by repr.go
}

tavit ohanian's avatar
tavit ohanian committed
720
func (w *_structAssembler) AssembleKey() ld.NodeAssembler {
721 722 723 724 725 726 727
	w.curKey = _assembler{
		schemaType: schemaTypeFieldName,
		val:        reflect.New(goTypeString).Elem(),
	}
	return &w.curKey
}

tavit ohanian's avatar
tavit ohanian committed
728
func (w *_structAssembler) AssembleValue() ld.NodeAssembler {
729 730 731 732 733
	// TODO: optimize this to do one lookup by name
	name := w.curKey.val.String()
	field := w.schemaType.Field(name)
	if field == nil {
		panic(name)
tavit ohanian's avatar
tavit ohanian committed
734
		// return nil, ld.ErrInvalidKey{
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
		// 	TypeName: w.schemaType.Name().String(),
		// 	Key:      basicnode.NewString(name),
		// }
	}
	ftyp, ok := w.val.Type().FieldByName(fieldNameFromSchema(name))
	if !ok {
		panic("TODO: go-schema mismatch")
	}
	if len(ftyp.Index) > 1 {
		panic("TODO: embedded fields")
	}
	w.doneFields[ftyp.Index[0]] = true
	fval := w.val.FieldByIndex(ftyp.Index)
	if field.IsOptional() {
		fval.Set(reflect.New(fval.Type().Elem()))
		fval = fval.Elem()
	}
	// TODO: reuse same assembler for perf?
	return &_assembler{
		schemaType: field.Type(),
		val:        fval,
		nullable:   field.IsNullable(),
	}
}

tavit ohanian's avatar
tavit ohanian committed
760
func (w *_structAssembler) AssembleEntry(k string) (ld.NodeAssembler, error) {
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
	if err := w.AssembleKey().AssignString(k); err != nil {
		return nil, err
	}
	am := w.AssembleValue()
	return am, nil
}

func (w *_structAssembler) Finish() error {
	fields := w.schemaType.Fields()
	var missing []string
	for i, field := range fields {
		if !field.IsOptional() && !w.doneFields[i] {
			missing = append(missing, field.Name())
		}
	}
	if len(missing) > 0 {
tavit ohanian's avatar
tavit ohanian committed
777
		return ld.ErrMissingRequiredField{Missing: missing}
778 779 780 781 782 783 784 785 786
	}
	if w.finish != nil {
		if err := w.finish(); err != nil {
			return err
		}
	}
	return nil
}

tavit ohanian's avatar
tavit ohanian committed
787
func (w *_structAssembler) KeyPrototype() ld.NodePrototype {
788 789 790
	return &_prototype{schemaType: schemaTypeFieldName, goType: goTypeString}
}

tavit ohanian's avatar
tavit ohanian committed
791
func (w *_structAssembler) ValuePrototype(k string) ld.NodePrototype {
792 793 794 795 796
	panic("TODO: struct ValuePrototype")
}

type _mapAssembler struct {
	schemaType *schema.TypeMap
797 798
	keysVal    reflect.Value // non-pointer
	valuesVal  reflect.Value // non-pointer
799 800 801 802 803 804 805 806 807
	finish     func() error

	// TODO: more state checks

	curKey _assembler

	nextIndex int // only used by repr.go
}

tavit ohanian's avatar
tavit ohanian committed
808
func (w *_mapAssembler) AssembleKey() ld.NodeAssembler {
809 810
	w.curKey = _assembler{
		schemaType: w.schemaType.KeyType(),
811
		val:        reflect.New(w.valuesVal.Type().Key()).Elem(),
812 813 814 815
	}
	return &w.curKey
}

tavit ohanian's avatar
tavit ohanian committed
816
func (w *_mapAssembler) AssembleValue() ld.NodeAssembler {
817
	kval := w.curKey.val
818
	val := reflect.New(w.valuesVal.Type().Elem()).Elem()
819 820
	finish := func() error {
		// fmt.Println(kval.Interface(), val.Interface())
821 822 823 824 825

		// TODO: check for duplicates in keysVal
		w.keysVal.Set(reflect.Append(w.keysVal, kval))

		w.valuesVal.SetMapIndex(kval, val)
826 827 828 829 830 831 832 833 834 835
		return nil
	}
	return &_assembler{
		schemaType: w.schemaType.ValueType(),
		val:        val,
		nullable:   w.schemaType.ValueIsNullable(),
		finish:     finish,
	}
}

tavit ohanian's avatar
tavit ohanian committed
836
func (w *_mapAssembler) AssembleEntry(k string) (ld.NodeAssembler, error) {
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
	if err := w.AssembleKey().AssignString(k); err != nil {
		return nil, err
	}
	am := w.AssembleValue()
	return am, nil
}

func (w *_mapAssembler) Finish() error {
	if w.finish != nil {
		if err := w.finish(); err != nil {
			return err
		}
	}
	return nil
}

tavit ohanian's avatar
tavit ohanian committed
853
func (w *_mapAssembler) KeyPrototype() ld.NodePrototype {
854
	return &_prototype{schemaType: w.schemaType.KeyType(), goType: w.valuesVal.Type().Key()}
855 856
}

tavit ohanian's avatar
tavit ohanian committed
857
func (w *_mapAssembler) ValuePrototype(k string) ld.NodePrototype {
858 859 860 861 862 863 864 865 866
	panic("TODO: struct ValuePrototype")
}

type _listAssembler struct {
	schemaType *schema.TypeList
	val        reflect.Value // non-pointer
	finish     func() error
}

tavit ohanian's avatar
tavit ohanian committed
867
func (w *_listAssembler) AssembleValue() ld.NodeAssembler {
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
	goType := w.val.Type().Elem()
	// TODO: use a finish func to append
	w.val.Set(reflect.Append(w.val, reflect.New(goType).Elem()))
	return &_assembler{
		schemaType: w.schemaType.ValueType(),
		val:        w.val.Index(w.val.Len() - 1),
		nullable:   w.schemaType.ValueIsNullable(),
	}
}

func (w *_listAssembler) Finish() error {
	if w.finish != nil {
		if err := w.finish(); err != nil {
			return err
		}
	}
	return nil
}

tavit ohanian's avatar
tavit ohanian committed
887
func (w *_listAssembler) ValuePrototype(idx int64) ld.NodePrototype {
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
	panic("TODO: list ValuePrototype")
}

type _unionAssembler struct {
	schemaType *schema.TypeUnion
	val        reflect.Value // non-pointer
	finish     func() error

	// TODO: more state checks

	curKey _assembler

	nextIndex int // only used by repr.go
}

tavit ohanian's avatar
tavit ohanian committed
903
func (w *_unionAssembler) AssembleKey() ld.NodeAssembler {
904 905 906 907 908 909 910
	w.curKey = _assembler{
		schemaType: schemaTypeFieldName,
		val:        reflect.New(goTypeString).Elem(),
	}
	return &w.curKey
}

tavit ohanian's avatar
tavit ohanian committed
911
func (w *_unionAssembler) AssembleValue() ld.NodeAssembler {
912 913 914 915 916 917 918 919 920 921 922 923
	name := w.curKey.val.String()
	var idx int
	var mtyp schema.Type
	for i, member := range w.schemaType.Members() {
		if member.Name().String() == name {
			idx = i
			mtyp = member
			break
		}
	}
	if mtyp == nil {
		panic("TODO: missing member")
tavit ohanian's avatar
tavit ohanian committed
924
		// return nil, ld.ErrInvalidKey{
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
		// 	TypeName: w.schemaType.Name().String(),
		// 	Key:      basicnode.NewString(name),
		// }
	}
	goType := inferGoType(mtyp) // TODO: do this upfront
	val := reflect.New(goType).Elem()
	finish := func() error {
		// fmt.Println(kval.Interface(), val.Interface())
		w.val.FieldByName("Index").SetInt(int64(idx))
		w.val.FieldByName("Value").Set(val)
		return nil
	}
	return &_assembler{
		schemaType: mtyp,
		val:        val,
		finish:     finish,
	}
}

tavit ohanian's avatar
tavit ohanian committed
944
func (w *_unionAssembler) AssembleEntry(k string) (ld.NodeAssembler, error) {
945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960
	if err := w.AssembleKey().AssignString(k); err != nil {
		return nil, err
	}
	am := w.AssembleValue()
	return am, nil
}

func (w *_unionAssembler) Finish() error {
	if w.finish != nil {
		if err := w.finish(); err != nil {
			return err
		}
	}
	return nil
}

tavit ohanian's avatar
tavit ohanian committed
961
func (w *_unionAssembler) KeyPrototype() ld.NodePrototype {
962 963 964
	return &_prototype{schemaType: schemaTypeFieldName, goType: goTypeString}
}

tavit ohanian's avatar
tavit ohanian committed
965
func (w *_unionAssembler) ValuePrototype(k string) ld.NodePrototype {
966 967 968 969 970 971 972 973 974 975 976 977 978 979
	panic("TODO: struct ValuePrototype")
}

type _structIterator struct {
	// TODO: support embedded fields?
	schemaType *schema.TypeStruct
	fields     []schema.StructField
	val        reflect.Value // non-pointer
	nextIndex  int

	// these are only used in repr.go
	reprEnd int
}

tavit ohanian's avatar
tavit ohanian committed
980
func (w *_structIterator) Next() (key, value ld.Node, _ error) {
981
	if w.Done() {
tavit ohanian's avatar
tavit ohanian committed
982
		return nil, nil, ld.ErrIteratorOverread{}
983 984 985 986 987 988 989
	}
	field := w.fields[w.nextIndex]
	val := w.val.Field(w.nextIndex)
	w.nextIndex++
	key = basicnode.NewString(field.Name())
	if field.IsOptional() {
		if val.IsNil() {
tavit ohanian's avatar
tavit ohanian committed
990
			return key, ld.Absent, nil
991 992 993 994 995
		}
		val = val.Elem()
	}
	if field.IsNullable() {
		if val.IsNil() {
tavit ohanian's avatar
tavit ohanian committed
996
			return key, ld.Null, nil
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
		}
		val = val.Elem()
	}
	node := &_node{
		schemaType: field.Type(),
		val:        val,
	}
	return key, node, nil
}

func (w *_structIterator) Done() bool {
	return w.nextIndex >= len(w.fields)
}

1011 1012 1013 1014 1015 1016 1017
type _mapIterator struct {
	schemaType *schema.TypeMap
	keysVal    reflect.Value // non-pointer
	valuesVal  reflect.Value // non-pointer
	nextIndex  int
}

tavit ohanian's avatar
tavit ohanian committed
1018
func (w *_mapIterator) Next() (key, value ld.Node, _ error) {
1019
	if w.Done() {
tavit ohanian's avatar
tavit ohanian committed
1020
		return nil, nil, ld.ErrIteratorOverread{}
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
	}
	goKey := w.keysVal.Index(w.nextIndex)
	val := w.valuesVal.MapIndex(goKey)
	w.nextIndex++

	key = &_node{
		schemaType: w.schemaType.KeyType(),
		val:        goKey,
	}
	if w.schemaType.ValueIsNullable() {
		if val.IsNil() {
tavit ohanian's avatar
tavit ohanian committed
1032
			return key, ld.Null, nil
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
		}
		val = val.Elem()
	}
	node := &_node{
		schemaType: w.schemaType.ValueType(),
		val:        val,
	}
	return key, node, nil
}

func (w *_mapIterator) Done() bool {
	return w.nextIndex >= w.keysVal.Len()
}

1047 1048 1049 1050 1051 1052
type _listIterator struct {
	schemaType *schema.TypeList
	val        reflect.Value // non-pointer
	nextIndex  int
}

tavit ohanian's avatar
tavit ohanian committed
1053
func (w *_listIterator) Next() (index int64, value ld.Node, _ error) {
1054
	if w.Done() {
tavit ohanian's avatar
tavit ohanian committed
1055
		return 0, nil, ld.ErrIteratorOverread{}
1056 1057 1058 1059
	}
	idx := int64(w.nextIndex)
	val := w.val.Index(w.nextIndex)
	w.nextIndex++
1060 1061
	if w.schemaType.ValueIsNullable() {
		if val.IsNil() {
tavit ohanian's avatar
tavit ohanian committed
1062
			return idx, ld.Null, nil
1063 1064 1065
		}
		val = val.Elem()
	}
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
	return idx, &_node{schemaType: w.schemaType.ValueType(), val: val}, nil
}

func (w *_listIterator) Done() bool {
	return w.nextIndex >= w.val.Len()
}

type _unionIterator struct {
	// TODO: support embedded fields?
	schemaType *schema.TypeUnion
	members    []schema.Type
	val        reflect.Value // non-pointer

	done bool
}

tavit ohanian's avatar
tavit ohanian committed
1082
func (w *_unionIterator) Next() (key, value ld.Node, _ error) {
1083
	if w.Done() {
tavit ohanian's avatar
tavit ohanian committed
1084
		return nil, nil, ld.ErrIteratorOverread{}
1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
	}
	w.done = true

	haveIdx := int(w.val.FieldByName("Index").Int())
	mtyp := w.members[haveIdx]
	mval := w.val.FieldByName("Value").Elem()

	node := &_node{
		schemaType: mtyp,
		val:        mval,
	}
	key = basicnode.NewString(mtyp.Name().String())
	return key, node, nil
}

func (w *_unionIterator) Done() bool {
	return w.done
}

// TODO: consider making our own Node interface, like:
//
// type WrappedNode interface {
tavit ohanian's avatar
tavit ohanian committed
1107
//     ld.Node
1108 1109 1110
//     Unwrap() (ptr interface)
// }
//
tavit ohanian's avatar
tavit ohanian committed
1111 1112
// Pros: API is easier to understand, harder to mix up with other ld.Nodes.
// Cons: One usually only has an ld.Node, and type assertions can be weird.