wrapStruct.go 7.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
package typed

import (
	"fmt"

	ipld "github.com/ipld/go-ipld-prime"
	ipldfree "github.com/ipld/go-ipld-prime/impl/free"
	"github.com/ipld/go-ipld-prime/schema"
)

var _ Node = wrapnodeStruct{}

type wrapnodeStruct struct {
	ipld.Node
	typ schema.TypeStruct
}

// Most of the 'nope' methods from the inner node are fine;
// we add the extra things required for typed.Node;
// we decorate the getters and iterators to handle the distinct path around optionals
// and return a different error for missing fields;
// length becomes fixed to a constant;
// and we replace the builder with a complete wrapper that maintains type rules.

// (We could override more of the Node methods to return errors with accurate type name, though.)

func (tn wrapnodeStruct) Type() schema.Type {
	return tn.typ
}

31
func (tn wrapnodeStruct) LookupString(key string) (ipld.Node, error) {
32 33 34 35
	for _, field := range tn.typ.Fields() {
		if field.Name() != key {
			continue
		}
36
		v, e1 := tn.Node.LookupString(key)
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
		if e1 == nil {
			return v, nil // null or set both flow through here
		}
		if _, ok := e1.(ipld.ErrNotExists); ok {
			return ipld.Undef, nil // we assume the type allows this, or this node shouldn't have been possible to construct in the first place
		}
		return nil, e1
	}
	return nil, ErrNoSuchField{Type: tn.typ, FieldName: key}
}

func (tn wrapnodeStruct) MapIterator() ipld.MapIterator {
	return &wrapnodeStruct_Iterator{&tn, 0}
}

type wrapnodeStruct_Iterator struct {
	node *wrapnodeStruct
	idx  int
}

func (itr *wrapnodeStruct_Iterator) Next() (k ipld.Node, v ipld.Node, _ error) {
	if itr.idx >= itr.node.Length() {
		return nil, nil, ipld.ErrIteratorOverread{}
	}
	field := itr.node.typ.Fields()[itr.idx]
	k = ipldfree.String(field.Name())
63
	v, e1 := itr.node.LookupString(field.Name())
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
	if e1 != nil {
		if _, ok := e1.(ipld.ErrNotExists); ok {
			v = ipld.Undef // we assume the type allows this, or this node shouldn't have been possible to construct in the first place
		} else {
			return k, nil, e1
		}
	}
	itr.idx++
	return
}
func (itr *wrapnodeStruct_Iterator) Done() bool {
	return itr.idx >= itr.node.Length()
}

func (tn wrapnodeStruct) Length() int {
	return len(tn.typ.Fields())
}

func (tn wrapnodeStruct) NodeBuilder() ipld.NodeBuilder {
	panic("todo")
}

func (tn wrapnodeStruct) Representation() ipld.Node {
	switch rs := tn.typ.RepresentationStrategy().(type) {
	case schema.StructRepresentation_Map:
		panic("todo") // TODO: add new source file for each of these.
	case schema.StructRepresentation_Tuple:
		panic("todo") // TODO: add new source file for each of these.
	case schema.StructRepresentation_StringPairs:
		panic("todo") // TODO: add new source file for each of these.
	case schema.StructRepresentation_StringJoin:
		panic("todo") // TODO: add new source file for each of these.
	default:
		_ = rs
		panic("unreachable (schema.StructRepresentation sum type)")
	}
}

// The builder is a more complete straightjacket; it wouldn't be correct to
// assume that the builder we're delegating internal storage to would reject
// other kinds (e.g. CreateString) entirely, and our type requires that.

type wrapnodeStruct_Builder struct {
	utnb ipld.NodeBuilder
	typ  schema.TypeStruct
}

func (nb wrapnodeStruct_Builder) CreateMap() (ipld.MapBuilder, error) {
	mb, err := nb.utnb.CreateMap()
	if err != nil {
		return nil, err
	}
	needs := make(map[string]struct{}, len(nb.typ.Fields()))
	for _, field := range nb.typ.Fields() {
		if !field.IsOptional() {
			needs[field.Name()] = struct{}{}
		}
	}
	return &wrapnodeStruct_MapBuilder{mb, nb.typ, needs}, nil
}
func (nb wrapnodeStruct_Builder) AmendMap() (ipld.MapBuilder, error) {
	panic("TODO") // TODO
}
func (nb wrapnodeStruct_Builder) CreateList() (ipld.ListBuilder, error) {
128
	return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Map}
129 130
}
func (nb wrapnodeStruct_Builder) AmendList() (ipld.ListBuilder, error) {
131
	return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.AmendList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Map}
132 133
}
func (nb wrapnodeStruct_Builder) CreateNull() (ipld.Node, error) {
134
	return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateNull", AppropriateKind: ipld.ReprKindSet_JustNull, ActualKind: ipld.ReprKind_Map}
135 136
}
func (nb wrapnodeStruct_Builder) CreateBool(v bool) (ipld.Node, error) {
137
	return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: ipld.ReprKind_Map}
138 139
}
func (nb wrapnodeStruct_Builder) CreateInt(v int) (ipld.Node, error) {
140
	return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateInt", AppropriateKind: ipld.ReprKindSet_JustInt, ActualKind: ipld.ReprKind_Map}
141 142
}
func (nb wrapnodeStruct_Builder) CreateFloat(v float64) (ipld.Node, error) {
143
	return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_Map}
144 145
}
func (nb wrapnodeStruct_Builder) CreateString(v string) (ipld.Node, error) {
146
	return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateString", AppropriateKind: ipld.ReprKindSet_JustString, ActualKind: ipld.ReprKind_Map}
147 148
}
func (nb wrapnodeStruct_Builder) CreateBytes(v []byte) (ipld.Node, error) {
149
	return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: ipld.ReprKind_Map}
150 151
}
func (nb wrapnodeStruct_Builder) CreateLink(v ipld.Link) (ipld.Node, error) {
152
	return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: ipld.ReprKind_Map}
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
}

type wrapnodeStruct_MapBuilder struct {
	utmb  ipld.MapBuilder
	typ   schema.TypeStruct
	needs map[string]struct{}
	// We have to remember if anything was intentionally unset so we can check at the end.
	// Or, initialize with the set of things that need to be set, and decrement it; easier.
}

func (mb *wrapnodeStruct_MapBuilder) Insert(k, v ipld.Node) error {
	ks, err := k.AsString()
	if err != nil {
		return err
	}
	// Check that the field exists at all.
	field := mb.typ.Field(ks)
	if field == nil {
		return ErrNoSuchField{Type: mb.typ, FieldName: ks}
	}
	// Check that the value is assignable to this field, or return error.
	vt, ok := v.(Node)
	switch {
	case v.IsNull():
		if !field.IsNullable() {
			return fmt.Errorf("type mismatch on struct field assignment: cannot assign null to non-nullable field")
		}
		// if null and nullable: carry on.
	case ok:
		if mb.typ.Field(ks).Type() != vt.Type() {
			return fmt.Errorf("type mismatch on struct field assignment")
		}
		// if typed node, and it matches: carry on.
	default:
		return fmt.Errorf("need typed.Node for insertion into struct") // FUTURE: maybe if it's a basic enough thing we sholud attempt coerce?
	}
	// Insert the value, and note it's now been set.
	if err := mb.utmb.Insert(k, v); err != nil {
		return err
	}
	delete(mb.needs, ks)
	return nil
}
func (mb *wrapnodeStruct_MapBuilder) Delete(k ipld.Node) error {
	panic("delete not supported on this type") // I have serious questions about whether the delete method deserves to exist.
}
func (mb *wrapnodeStruct_MapBuilder) Build() (ipld.Node, error) {
	if len(mb.needs) > 0 {
		return nil, fmt.Errorf("missing required fields") // TODO say which
	}
	n, err := mb.Build()
	if err != nil {
		return nil, err
	}
	return wrapnodeStruct{n, mb.typ}, nil
}

// TODO and soon the nb methods for getting child builders.
// also those will have fun methods for handling the ability to have undefined..?
//     no they shouldn't actually that's important -- those features really only occur in Insert methods.