Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a typed link node to allow traversal with code gen'd builders across links #41

Merged
merged 3 commits into from
Jan 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions impl/typed/typedLinkNode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package typed

import "github.com/ipld/go-ipld-prime"

// typed.LinkNode is a superset of the typed.Node interface, and has one additional behavior.
//
// A typed.LinkNode contains a hint for the appropriate node builder to use for loading data
// on the other side of the link contained within the node, so that it can be assembled
// into a node representation and validated against the schema as quickly as possible
//
// So, for example, if you wanted to support loading the other side of a link
// with a code-gen'd node builder while utilizing the automatic loading facilities
// of the traversal package, you could write a LinkNodeBuilderChooser as follows:
//
// func LinkNodeBuilderChooser(lnk ipld.Link, lnkCtx ipld.LinkContext) ipld.NodeBuilder {
// if tlnkNd, ok := lnkCtx.LinkNode.(typed.LinkNode); ok {
// return tlnkNd.SuggestedNodeBuilder()
// }
// return ipldfree.NodeBuilder()
// }
//
type LinkNode interface {
ReferencedNodeBuilder() ipld.NodeBuilder
}
13 changes: 13 additions & 0 deletions schema/gen/go/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ type typedNodeGenerator interface {
GetRepresentationNodeGen() nodeGenerator // includes transitively the matched nodebuilderGenerator
}

type typedLinkNodeGenerator interface {
// all methods in typedNodeGenerator
typedNodeGenerator

// as typed.LinkNode.ReferencedNodeBuilder generator
EmitTypedLinkNodeMethodReferencedNodeBuilder(io.Writer)
}

type nodeGenerator interface {
EmitNodeType(io.Writer)
EmitNodeMethodReprKind(io.Writer)
Expand Down Expand Up @@ -140,6 +148,11 @@ func EmitEntireType(tg typedNodeGenerator, w io.Writer) {
tnbg.EmitNodebuilderMethodCreateBytes(w)
tnbg.EmitNodebuilderMethodCreateLink(w)

tlg, ok := tg.(typedLinkNodeGenerator)
if ok {
tlg.EmitTypedLinkNodeMethodReferencedNodeBuilder(w)
}

tg.EmitTypedNodeMethodRepresentation(w)
rng := tg.GetRepresentationNodeGen()
if rng == nil { // FIXME: hack to save me from stubbing tons right now, remove when done
Expand Down
10 changes: 10 additions & 0 deletions schema/gen/go/genKindLinkNode.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ func (gk generateKindLink) EmitTypedNodeMethodRepresentation(w io.Writer) {
`, w, gk)
}

func (gk generateKindLink) EmitTypedLinkNodeMethodReferencedNodeBuilder(w io.Writer) {
if gk.Type.HasReferencedType() {
doTemplate(`
func ({{ .Type | mungeTypeNodeIdent }}) ReferencedNodeBuilder() ipld.NodeBuilder {
return {{ .Type.ReferencedType | mungeTypeNodebuilderIdent }}{}
}
`, w, gk)
}
}

func (gk generateKindLink) GetRepresentationNodeGen() nodeGenerator {
return nil // TODO of course
}
5 changes: 5 additions & 0 deletions schema/gen/go/gen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func TestNuevo(t *testing.T) {
tInt := schema.SpawnInt("Int")
tBytes := schema.SpawnBytes("Bytes")
tLink := schema.SpawnLink("Link")
tIntLink := schema.SpawnLinkReference("IntLink", tInt)
tIntList := schema.SpawnList("IntList", tInt, false)
tNullableIntList := schema.SpawnList("NullableIntList", tInt, true)

Expand Down Expand Up @@ -81,6 +82,10 @@ func TestNuevo(t *testing.T) {
EmitFileHeader("whee", f)
EmitEntireType(NewGeneratorForKindLink(tLink), f)

f = openOrPanic("_test/tIntLink.go")
EmitFileHeader("whee", f)
EmitEntireType(NewGeneratorForKindLink(tIntLink), f)

f = openOrPanic("_test/tIntList.go")
EmitFileHeader("whee", f)
EmitEntireType(NewGeneratorForKindList(tIntList), f)
Expand Down
5 changes: 4 additions & 1 deletion schema/tmpBuilders.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ func SpawnBytes(name TypeName) TypeBytes {
}

func SpawnLink(name TypeName) TypeLink {
return TypeLink{anyType{name, nil}}
return TypeLink{anyType{name, nil}, nil, false}
}

func SpawnLinkReference(name TypeName, referenceType Type) TypeLink {
return TypeLink{anyType{name, nil}, referenceType, true}
}
func SpawnList(name TypeName, typ Type, nullable bool) TypeList {
return TypeList{anyType{name, nil}, false, typ, nullable}
}
Expand Down
2 changes: 2 additions & 0 deletions schema/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ type TypeList struct {

type TypeLink struct {
anyType
referencedType Type
hasReferencedType bool
// ...?
}

Expand Down
15 changes: 15 additions & 0 deletions schema/typeMethods.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,18 @@ func (t TypeEnum) Members() []string {
}
return a
}

// Links can keep a referenced type, which is a hint only about the data on the
// other side of the link, no something that can be explicitly validated without
// loading the link

// HasReferencedType returns true if the link has a hint about the type it references
// false if it's generic
func (t TypeLink) HasReferencedType() bool {
return t.hasReferencedType
}

// ReferencedType returns the type hint for the node on the other side of the link
func (t TypeLink) ReferencedType() Type {
return t.referencedType
}
6 changes: 5 additions & 1 deletion traversal/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

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

// init sets all the values in TraveralConfig to reasonable defaults
Expand All @@ -21,7 +22,10 @@ func (tc *Config) init() {
}
}
if tc.LinkNodeBuilderChooser == nil {
tc.LinkNodeBuilderChooser = func(ipld.Link, ipld.LinkContext) ipld.NodeBuilder {
tc.LinkNodeBuilderChooser = func(lnk ipld.Link, lnkCtx ipld.LinkContext) ipld.NodeBuilder {
if tlnkNd, ok := lnkCtx.LinkNode.(typed.LinkNode); ok {
return tlnkNd.ReferencedNodeBuilder()
}
return ipldfree.NodeBuilder()
}
}
Expand Down