nodeBuilder.go 6.21 KB
Newer Older
tavit ohanian's avatar
tavit ohanian committed
1
package ld
2

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// NodeAssembler is the interface that describes all the ways we can set values
// in a node that's under construction.
//
// To create a Node, you should start with a NodeBuilder (which contains a
// superset of the NodeAssembler methods, and can return the finished Node
// from its `Build` method).
//
// Why do both this and the NodeBuilder interface exist?
// When creating trees of nodes, recursion works over the NodeAssembler interface.
// This is important to efficient library internals, because avoiding the
// requirement to be able to return a Node at any random point in the process
// relieves internals from needing to implement 'freeze' features.
// (This is useful in turn because implementing those 'freeze' features in a
// language without first-class/compile-time support for them (as golang is)
// would tend to push complexity and costs to execution time; we'd rather not.)
type NodeAssembler interface {
19 20
	BeginMap(sizeHint int64) (MapAssembler, error)
	BeginList(sizeHint int64) (ListAssembler, error)
21 22
	AssignNull() error
	AssignBool(bool) error
23
	AssignInt(int64) error
24 25 26 27 28
	AssignFloat(float64) error
	AssignString(string) error
	AssignBytes([]byte) error
	AssignLink(Link) error

29
	AssignNode(Node) error // if you already have a completely constructed subtree, this method puts the whole thing in place at once.
30

31
	// Prototype returns a NodePrototype describing what kind of value we're assembling.
32 33 34 35
	//
	// You often don't need this (because you should be able to
	// just feed data and check errors), but it's here.
	//
36
	// Using `this.Prototype().NewBuilder()` to produce a new `Node`,
37
	// then giving that node to `this.AssignNode(n)` should always work.
38
	// (Note that this is not necessarily an _exclusive_ statement on what
39
	// sort of values will be accepted by `this.AssignNode(n)`.)
40
	Prototype() NodePrototype
41 42
}

43
// MapAssembler assembles a map node!  (You guessed it.)
44
//
45
// Methods on MapAssembler must be called in a valid order:
46 47 48 49 50 51 52 53 54 55 56 57
// assemble a key, then assemble a value, then loop as long as desired;
// when finished, call 'Finish'.
//
// Incorrect order invocations will panic.
// Calling AssembleKey twice in a row will panic;
// calling AssembleValue before finishing using the NodeAssembler from AssembleKey will panic;
// calling AssembleValue twice in a row will panic;
// etc.
//
// Note that the NodeAssembler yielded from AssembleKey has additional behavior:
// if the node assembled there matches a key already present in the map,
// that assembler will emit the error!
58
type MapAssembler interface {
59 60 61
	AssembleKey() NodeAssembler   // must be followed by call to AssembleValue.
	AssembleValue() NodeAssembler // must be called immediately after AssembleKey.

62
	AssembleEntry(k string) (NodeAssembler, error) // shortcut combining AssembleKey and AssembleValue into one step; valid when the key is a string kind.
63

64
	Finish() error
65

66
	// KeyPrototype returns a NodePrototype that knows how to build keys of a type this map uses.
67
	//
68 69 70 71 72
	// You often don't need this (because you should be able to
	// just feed data and check errors), but it's here.
	//
	// For all Data Model maps, this will answer with a basic concept of "string".
	// For Schema typed maps, this may answer with a more complex type (potentially even a struct type).
73
	KeyPrototype() NodePrototype
74

75
	// ValuePrototype returns a NodePrototype that knows how to build values this map can contain.
76 77 78 79
	//
	// You often don't need this (because you should be able to
	// just feed data and check errors), but it's here.
	//
80 81
	// ValuePrototype requires a parameter describing the key in order to say what
	// NodePrototype will be acceptable as a value for that key, because when using
82 83 84
	// struct types (or union types) from the Schemas system, they behave as maps
	// but have different acceptable types for each field (or member, for unions).
	// For plain maps (that is, not structs or unions masquerading as maps),
85
	// the empty string can be used as a parameter, and the returned NodePrototype
86
	// can be assumed applicable for all values.
87 88 89
	// Using an empty string for a struct or union will return nil,
	// as will using any string which isn't a field or member of those types.
	//
90 91 92
	// (Design note: a string is sufficient for the parameter here rather than
	// a full Node, because the only cases where the value types vary are also
	// cases where the keys may not be complex.)
93
	ValuePrototype(k string) NodePrototype
94 95
}

96
type ListAssembler interface {
97
	AssembleValue() NodeAssembler
98

99
	Finish() error
100

101
	// ValuePrototype returns a NodePrototype that knows how to build values this map can contain.
102 103 104 105
	//
	// You often don't need this (because you should be able to
	// just feed data and check errors), but it's here.
	//
106
	// ValuePrototype, much like the matching method on the MapAssembler interface,
107
	// requires a parameter specifying the index in the list in order to say
108
	// what NodePrototype will be acceptable as a value at that position.
109
	// For many lists (and *all* lists which operate exclusively at the Data Model level),
110
	// this will return the same NodePrototype regardless of the value of 'idx';
111 112 113 114
	// the only time this value will vary is when operating with a Schema,
	// and handling the representation NodeAssembler for a struct type with
	// a representation of a list kind.
	// If you know you are operating in a situation that won't have varying
115 116
	// NodePrototypes, it is acceptable to call `ValuePrototype(0)` and use the
	// resulting NodePrototype for all reasoning.
117
	ValuePrototype(idx int64) NodePrototype
118 119
}

120 121
type NodeBuilder interface {
	NodeAssembler
122

123 124 125 126 127 128 129 130 131
	// Build returns the new value after all other assembly has been completed.
	//
	// A method on the NodeAssembler that finishes assembly of the data must
	// be called first (e.g., any of the "Assign*" methods, or "Finish" if
	// the assembly was for a map or a list); that finishing method still has
	// all responsibility for validating the assembled data and returning
	// any errors from that process.
	// (Correspondingly, there is no error return from this method.)
	Build() Node
132

133 134 135 136 137 138 139
	// Resets the builder.  It can hereafter be used again.
	// Reusing a NodeBuilder can reduce allocations and improve performance.
	//
	// Only call this if you're going to reuse the builder.
	// (Otherwise, it's unnecessary, and may cause an unwanted allocation).
	Reset()
}