Unverified Commit c71d7fb2 authored by Eric Myhre's avatar Eric Myhre Committed by GitHub

Merge pull request #21 from ipld/schema-and-codegen-fun

Schema and codegen fun
parents f538ddd4 fcda4cc7
......@@ -29,3 +29,27 @@ type ErrWrongKind struct {
func (e ErrWrongKind) Error() string {
return fmt.Sprintf("func called on wrong kind: %s called on a %s node, but only makes sense on %s", e.MethodName, e.ActualKind, e.AppropriateKind)
}
// ErrNotExists may be returned from the traversal functions of the Node
// interface to indicate a missing value.
//
// Note that typed.ErrNoSuchField is another type of error which sometimes
// occurs in similar places as ErrNotExists. ErrNoSuchField is preferred
// when handling data with constraints provided by a schema that mean that
// a field can *never* exist (as differentiated from a map key which is
// simply absent in some data).
type ErrNotExists struct {
Segment string // REVIEW: might be better to use PathSegment, but depends on another refactor.
}
func (e ErrNotExists) Error() string {
return fmt.Sprintf("key not found: %q", e.Segment)
}
// ErrIteratorOverread is returned when calling 'Next' on a MapIterator or
// ListIterator when it is already done.
type ErrIteratorOverread struct{}
func (e ErrIteratorOverread) Error() string {
return "iterator overread"
}
......@@ -75,6 +75,9 @@ func (n Node) ReprKind() ipld.ReprKind {
return n.kind
}
func (Node) IsUndefined() bool {
return false
}
func (n Node) IsNull() bool {
return n.bound.IsNil()
}
......
......@@ -38,6 +38,9 @@ func (n *Node) ReprKind() ipld.ReprKind {
return n.kind
}
func (Node) IsUndefined() bool {
return false
}
func (n *Node) IsNull() bool {
return n.kind == ipld.ReprKind_Null
}
......
package typed
import (
"fmt"
"github.com/ipld/go-ipld-prime/schema"
)
// ErrNoSuchField may be returned from traversal functions on the Node
// interface when a field is requested which doesn't exist; it may also be
// returned during MapBuilder
type ErrNoSuchField struct {
Type schema.Type
FieldName string
}
func (e ErrNoSuchField) Error() string {
return fmt.Sprintf("no such field: %s.%s", e.Type.Name(), e.FieldName)
}
......@@ -54,12 +54,10 @@ type Node interface {
// Undefined nodes are returned when traversing a struct field that is
// defined by a schema but unset in the data. (Undefined nodes are not
// possible otherwise; you'll only see them from `typed.Node`.)
//
// REVIEW: unsure if this should use ReprKind_Invalid or another enum.
// Since ReprKind_Invalid is returned for UnionStyle_Kinded, confusing.
// (But since this is only relevant for `typed.Node`, we can make that
// choice locally to that package.)
//IsUndefined() bool
// The undefined flag is necessary so iterating over structs can
// unambiguously make the distinction between values that are
// present-and-null versus values that are absent.
IsUndefined() bool
IsNull() bool
AsBool() (bool, error)
......
......@@ -29,7 +29,7 @@ func TestScalarUnmarshal(t *testing.T) {
tb := &TokenSourceBucket{tokens: []tok.Token{
{Type: tok.TString, Str: "zooooom"},
}}
nb := Strang__NodeBuilder{}
nb := String__NodeBuilder{}
n, err := encoding.Unmarshal(nb, tb)
Wish(t, err, ShouldEqual, nil)
Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_String)
......
......@@ -43,6 +43,7 @@ type typeGenerator interface {
EmitNodeMethodMapIterator(io.Writer)
EmitNodeMethodListIterator(io.Writer)
EmitNodeMethodLength(io.Writer)
EmitNodeMethodIsUndefined(io.Writer)
EmitNodeMethodIsNull(io.Writer)
EmitNodeMethodAsBool(io.Writer)
EmitNodeMethodAsInt(io.Writer)
......@@ -61,6 +62,7 @@ func emitFileHeader(w io.Writer) {
fmt.Fprintf(w, "package whee\n\n")
fmt.Fprintf(w, "import (\n")
fmt.Fprintf(w, "\tipld \"github.com/ipld/go-ipld-prime\"\n")
fmt.Fprintf(w, "\t\"github.com/ipld/go-ipld-prime/impl/typed\"\n")
fmt.Fprintf(w, ")\n\n")
}
......
package gengo
import (
"io"
"github.com/ipld/go-ipld-prime/schema"
)
type generateKindedRejections struct{}
func (generateKindedRejections) emitNodeMethodTraverseField(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) TraverseField(string) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "{{ .Name }}.TraverseField", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: {{ .Kind.ActsLike | ReprKindConst }}}
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodTraverseIndex(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) TraverseIndex(idx int) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "{{ .Name }}.TraverseIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: {{ .Kind.ActsLike | ReprKindConst }}}
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodMapIterator(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) MapIterator() ipld.MapIterator {
return mapIteratorReject{ipld.ErrWrongKind{MethodName: "{{ .Name }}.MapIterator", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: {{ .Kind.ActsLike | ReprKindConst }}}}
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodListIterator(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) ListIterator() ipld.ListIterator {
return listIteratorReject{ipld.ErrWrongKind{MethodName: "{{ .Name }}.ListIterator", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: {{ .Kind.ActsLike | ReprKindConst }}}}
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodLength(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) Length() int {
return -1
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodIsUndefined(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) IsUndefined() bool {
return false
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodIsNull(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) IsNull() bool {
return false
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodAsBool(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) AsBool() (bool, error) {
return false, ipld.ErrWrongKind{MethodName: "{{ .Name }}.AsBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: {{ .Kind.ActsLike | ReprKindConst }}}
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodAsInt(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) AsInt() (int, error) {
return 0, ipld.ErrWrongKind{MethodName: "{{ .Name }}.AsInt", AppropriateKind: ipld.ReprKindSet_JustInt, ActualKind: {{ .Kind.ActsLike | ReprKindConst }}}
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodAsFloat(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) AsFloat() (float64, error) {
return 0, ipld.ErrWrongKind{MethodName: "{{ .Name }}.AsFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: {{ .Kind.ActsLike | ReprKindConst }}}
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodAsString(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) AsString() (string, error) {
return "", ipld.ErrWrongKind{MethodName: "{{ .Name }}.AsString", AppropriateKind: ipld.ReprKindSet_JustString, ActualKind: {{ .Kind.ActsLike | ReprKindConst }}}
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodAsBytes(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) AsBytes() ([]byte, error) {
return nil, ipld.ErrWrongKind{MethodName: "{{ .Name }}.AsBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: {{ .Kind.ActsLike | ReprKindConst }}}
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodAsLink(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) AsLink() (ipld.Link, error) {
return nil, ipld.ErrWrongKind{MethodName: "{{ .Name }}.AsLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: {{ .Kind.ActsLike | ReprKindConst }}}
}
`, w, t)
}
// Embeddable to do all the "nope" methods at once.
type generateKindedRejections_String struct {
Type schema.Type // used so we can generate error messages with the type name.
}
func (gk generateKindedRejections_String) EmitNodeMethodTraverseField(w io.Writer) {
generateKindedRejections{}.emitNodeMethodTraverseField(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodTraverseIndex(w io.Writer) {
generateKindedRejections{}.emitNodeMethodTraverseIndex(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodMapIterator(w io.Writer) {
generateKindedRejections{}.emitNodeMethodMapIterator(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodListIterator(w io.Writer) {
generateKindedRejections{}.emitNodeMethodListIterator(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodLength(w io.Writer) {
generateKindedRejections{}.emitNodeMethodLength(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodIsUndefined(w io.Writer) {
generateKindedRejections{}.emitNodeMethodIsUndefined(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodIsNull(w io.Writer) {
generateKindedRejections{}.emitNodeMethodIsNull(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodAsBool(w io.Writer) {
generateKindedRejections{}.emitNodeMethodAsBool(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodAsInt(w io.Writer) {
generateKindedRejections{}.emitNodeMethodAsInt(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodAsFloat(w io.Writer) {
generateKindedRejections{}.emitNodeMethodAsFloat(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodAsBytes(w io.Writer) {
generateKindedRejections{}.emitNodeMethodAsBytes(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodAsLink(w io.Writer) {
generateKindedRejections{}.emitNodeMethodAsLink(w, gk.Type)
}
// Embeddable to do all the "nope" methods at once.
//
// Used for anything that "acts like" map (so, also struct).
type generateKindedRejections_Map struct {
Type schema.Type // used so we can generate error messages with the type name.
}
func (gk generateKindedRejections_Map) EmitNodeMethodTraverseIndex(w io.Writer) {
generateKindedRejections{}.emitNodeMethodTraverseIndex(w, gk.Type)
}
func (gk generateKindedRejections_Map) EmitNodeMethodListIterator(w io.Writer) {
generateKindedRejections{}.emitNodeMethodListIterator(w, gk.Type)
}
func (gk generateKindedRejections_Map) EmitNodeMethodIsUndefined(w io.Writer) {
generateKindedRejections{}.emitNodeMethodIsUndefined(w, gk.Type)
}
func (gk generateKindedRejections_Map) EmitNodeMethodIsNull(w io.Writer) {
generateKindedRejections{}.emitNodeMethodIsNull(w, gk.Type)
}
func (gk generateKindedRejections_Map) EmitNodeMethodAsBool(w io.Writer) {
generateKindedRejections{}.emitNodeMethodAsBool(w, gk.Type)
}
func (gk generateKindedRejections_Map) EmitNodeMethodAsInt(w io.Writer) {
generateKindedRejections{}.emitNodeMethodAsInt(w, gk.Type)
}
func (gk generateKindedRejections_Map) EmitNodeMethodAsFloat(w io.Writer) {
generateKindedRejections{}.emitNodeMethodAsFloat(w, gk.Type)
}
func (gk generateKindedRejections_Map) EmitNodeMethodAsString(w io.Writer) {
generateKindedRejections{}.emitNodeMethodAsString(w, gk.Type)
}
func (gk generateKindedRejections_Map) EmitNodeMethodAsBytes(w io.Writer) {
generateKindedRejections{}.emitNodeMethodAsBytes(w, gk.Type)
}
func (gk generateKindedRejections_Map) EmitNodeMethodAsLink(w io.Writer) {
generateKindedRejections{}.emitNodeMethodAsLink(w, gk.Type)
}
......@@ -6,107 +6,24 @@ import (
"github.com/ipld/go-ipld-prime/schema"
)
func NewGeneratorForKindString(t schema.Type) typeGenerator {
return generateKindString{
t.(schema.TypeString),
generateKindedRejections_String{t},
}
}
type generateKindString struct {
Name schema.TypeName
Type schema.Type
Type schema.TypeString
generateKindedRejections_String
// FUTURE: probably some adjunct config data should come with here as well.
// FUTURE: perhaps both a global one (e.g. output package name) and a per-type one.
}
// FUTURE: quite a few of these "nope" methods can be reused widely.
func (gk generateKindString) EmitNodeMethodTraverseField(w io.Writer) {
doTemplate(`
func ({{ .Name }}) TraverseField(key string) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "TraverseField", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String}
}
`, w, gk)
}
func (gk generateKindString) EmitNodeMethodTraverseIndex(w io.Writer) {
doTemplate(`
func ({{ .Name }}) TraverseIndex(idx int) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "TraverseIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_String}
}
`, w, gk)
}
func (gk generateKindString) EmitNodeMethodMapIterator(w io.Writer) {
doTemplate(`
func ({{ .Name }}) MapIterator() ipld.MapIterator {
return mapIteratorReject{ipld.ErrWrongKind{MethodName: "MapIterator", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String}}
}
`, w, gk)
}
func (gk generateKindString) EmitNodeMethodListIterator(w io.Writer) {
doTemplate(`
func ({{ .Name }}) ListIterator() ipld.ListIterator {
return listIteratorReject{ipld.ErrWrongKind{MethodName: "ListIterator", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_String}}
}
`, w, gk)
}
func (gk generateKindString) EmitNodeMethodLength(w io.Writer) {
doTemplate(`
func ({{ .Name }}) Length() int {
return -1
}
`, w, gk)
}
func (gk generateKindString) EmitNodeMethodIsNull(w io.Writer) {
doTemplate(`
func ({{ .Name }}) IsNull() bool {
return false
}
`, w, gk)
}
func (gk generateKindString) EmitNodeMethodAsBool(w io.Writer) {
doTemplate(`
func ({{ .Name }}) AsBool() (bool, error) {
return false, ipld.ErrWrongKind{MethodName: "AsBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: ipld.ReprKind_String}
}
`, w, gk)
}
func (gk generateKindString) EmitNodeMethodAsInt(w io.Writer) {
doTemplate(`
func ({{ .Name }}) AsInt() (int, error) {
return 0, ipld.ErrWrongKind{MethodName: "AsInt", AppropriateKind: ipld.ReprKindSet_JustInt, ActualKind: ipld.ReprKind_String}
}
`, w, gk)
}
func (gk generateKindString) EmitNodeMethodAsFloat(w io.Writer) {
doTemplate(`
func ({{ .Name }}) AsFloat() (float64, error) {
return 0, ipld.ErrWrongKind{MethodName: "AsFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_String}
}
`, w, gk)
}
func (gk generateKindString) EmitNodeMethodAsString(w io.Writer) {
doTemplate(`
func (x {{ .Name }}) AsString() (string, error) {
func (x {{ .Type.Name }}) AsString() (string, error) {
return x.x, nil
}
`, w, gk)
}
func (gk generateKindString) EmitNodeMethodAsBytes(w io.Writer) {
doTemplate(`
func ({{ .Name }}) AsBytes() ([]byte, error) {
return nil, ipld.ErrWrongKind{MethodName: "AsBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: ipld.ReprKind_String}
}
`, w, gk)
}
func (gk generateKindString) EmitNodeMethodAsLink(w io.Writer) {
doTemplate(`
func ({{ .Name }}) AsLink() (ipld.Link, error) {
return nil, ipld.ErrWrongKind{MethodName: "AsLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: ipld.ReprKind_String}
}
`, w, gk)
}
......@@ -6,16 +6,17 @@ import (
func (gk generateKindString) EmitNodeType(w io.Writer) {
doTemplate(`
var _ ipld.Node = {{ .Name }}{}
var _ ipld.Node = {{ .Type.Name }}{}
var _ typed.Node = typed.Node(nil) // TODO
type {{ .Name }} struct{ x string }
type {{ .Type.Name }} struct{ x string }
`, w, gk)
}
func (gk generateKindString) EmitNodeMethodReprKind(w io.Writer) {
doTemplate(`
func ({{ .Name }}) ReprKind() ipld.ReprKind {
func ({{ .Type.Name }}) ReprKind() ipld.ReprKind {
return ipld.ReprKind_String
}
`, w, gk)
......@@ -24,43 +25,43 @@ func (gk generateKindString) EmitNodeMethodReprKind(w io.Writer) {
// FUTURE: consider breaking the nodebuilder methods down like the node methods are; a lot of the "nope" variants could be reused.
func (gk generateKindString) EmitNodeMethodNodeBuilder(w io.Writer) {
doTemplate(`
func ({{ .Name }}) NodeBuilder() ipld.NodeBuilder {
return {{ .Name }}__NodeBuilder{}
func ({{ .Type.Name }}) NodeBuilder() ipld.NodeBuilder {
return {{ .Type.Name }}__NodeBuilder{}
}
type {{ .Name }}__NodeBuilder struct{}
type {{ .Type.Name }}__NodeBuilder struct{}
func (nb {{ .Name }}__NodeBuilder) CreateMap() (ipld.MapBuilder, error) {
func (nb {{ .Type.Name }}__NodeBuilder) CreateMap() (ipld.MapBuilder, error) {
return nil, ipld.ErrWrongKind{MethodName: "CreateMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String}
}
func (nb {{ .Name }}__NodeBuilder) AmendMap() (ipld.MapBuilder, error) {
func (nb {{ .Type.Name }}__NodeBuilder) AmendMap() (ipld.MapBuilder, error) {
return nil, ipld.ErrWrongKind{MethodName: "AmendMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String}
}
func (nb {{ .Name }}__NodeBuilder) CreateList() (ipld.ListBuilder, error) {
func (nb {{ .Type.Name }}__NodeBuilder) CreateList() (ipld.ListBuilder, error) {
return nil, ipld.ErrWrongKind{MethodName: "CreateList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_String}
}
func (nb {{ .Name }}__NodeBuilder) AmendList() (ipld.ListBuilder, error) {
func (nb {{ .Type.Name }}__NodeBuilder) AmendList() (ipld.ListBuilder, error) {
return nil, ipld.ErrWrongKind{MethodName: "AmendList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_String}
}
func (nb {{ .Name }}__NodeBuilder) CreateNull() (ipld.Node, error) {
func (nb {{ .Type.Name }}__NodeBuilder) CreateNull() (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "CreateNull", AppropriateKind: ipld.ReprKindSet_JustNull, ActualKind: ipld.ReprKind_String}
}
func (nb {{ .Name }}__NodeBuilder) CreateBool(v bool) (ipld.Node, error) {
func (nb {{ .Type.Name }}__NodeBuilder) CreateBool(v bool) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "CreateBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: ipld.ReprKind_String}
}
func (nb {{ .Name }}__NodeBuilder) CreateInt(v int) (ipld.Node, error) {
func (nb {{ .Type.Name }}__NodeBuilder) CreateInt(v int) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "CreateInt", AppropriateKind: ipld.ReprKindSet_JustInt, ActualKind: ipld.ReprKind_String}
}
func (nb {{ .Name }}__NodeBuilder) CreateFloat(v float64) (ipld.Node, error) {
func (nb {{ .Type.Name }}__NodeBuilder) CreateFloat(v float64) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "CreateFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_String}
}
func (nb {{ .Name }}__NodeBuilder) CreateString(v string) (ipld.Node, error) {
return {{ .Name }}{v}, nil
func (nb {{ .Type.Name }}__NodeBuilder) CreateString(v string) (ipld.Node, error) {
return {{ .Type.Name }}{v}, nil
}
func (nb {{ .Name }}__NodeBuilder) CreateBytes(v []byte) (ipld.Node, error) {
func (nb {{ .Type.Name }}__NodeBuilder) CreateBytes(v []byte) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "CreateBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: ipld.ReprKind_String}
}
func (nb {{ .Name }}__NodeBuilder) CreateLink(v ipld.Link) (ipld.Node, error) {
func (nb {{ .Type.Name }}__NodeBuilder) CreateLink(v ipld.Link) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "CreateLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: ipld.ReprKind_String}
}
`, w, gk)
......
package gengo
import (
"io"
"github.com/ipld/go-ipld-prime/schema"
)
func NewGeneratorForKindStruct(t schema.Type) typeGenerator {
return generateKindStruct{
t.(schema.TypeStruct),
generateKindedRejections_Map{t},
}
}
type generateKindStruct struct {
Type schema.TypeStruct
generateKindedRejections_Map
// FUTURE: probably some adjunct config data should come with here as well.
// FUTURE: perhaps both a global one (e.g. output package name) and a per-type one.
}
func (gk generateKindStruct) EmitNodeType(w io.Writer) {
// Observe that we get a '*' if a field is *either* nullable *or* optional;
// and we get an extra bool for the second cardinality +1'er if both are true.
doTemplate(`
var _ ipld.Node = {{ .Type.Name }}{}
var _ typed.Node = typed.Node(nil) // TODO
type {{ .Type.Name }} struct{
{{- range $field := .Type.Fields }}
{{ $field.Name }} {{if or $field.IsOptional $field.IsNullable }}*{{end}}{{ $field.Type.Name }}
{{- end}}
{{ range $field := .Type.Fields }}
{{- if and $field.IsOptional $field.IsNullable }}
{{ $field.Name }}__exists bool
{{- end}}
{{- end}}
}
`, w, gk)
}
func (gk generateKindStruct) EmitNodeMethodReprKind(w io.Writer) {
doTemplate(`
func ({{ .Type.Name }}) ReprKind() ipld.ReprKind {
return ipld.ReprKind_Map
}
`, w, gk)
}
func (gk generateKindStruct) EmitNodeMethodTraverseField(w io.Writer) {
doTemplate(`
func (x {{ .Type.Name }}) TraverseField(key string) (ipld.Node, error) {
switch key {
{{- range $field := .Type.Fields }}
case "{{ $field.Name }}":
{{- if and $field.IsOptional $field.IsNullable }}
if !x.{{ $field.Name }}__exists {
return ipld.Undef, nil
}
if x.{{ $field.Name }} == nil {
return ipld.Null, nil
}
{{- else if $field.IsOptional }}
if x.{{ $field.Name }} == nil {
return ipld.Undef, nil
}
{{- else if $field.IsNullable }}
if x.{{ $field.Name }} == nil {
return ipld.Null, nil
}
{{- end}}
return x.{{ $field.Name }}, nil
{{- end}}
default:
return nil, typed.ErrNoSuchField{Type: nil /*TODO*/, FieldName: key}
}
}
`, w, gk)
}
func (gk generateKindStruct) EmitNodeMethodMapIterator(w io.Writer) {
doTemplate(`
func (x {{ .Type.Name }}) MapIterator() ipld.MapIterator {
return &_{{ .Type.Name }}__itr{&x, 0}
}
type _{{ .Type.Name }}__itr struct {
node *{{ .Type.Name }}
idx int
}
func (itr *_{{ .Type.Name }}__itr) Next() (k ipld.Node, v ipld.Node, _ error) {
if itr.idx >= {{ len .Type.Fields }} {
return nil, nil, ipld.ErrIteratorOverread{}
}
switch itr.idx {
{{- range $i, $field := .Type.Fields }}
case {{ $i }}:
k = String{"{{ $field.Name }}"}
{{- if and $field.IsOptional $field.IsNullable }}
if !itr.node.{{ $field.Name }}__exists {
v = ipld.Undef
break
}
if itr.node.{{ $field.Name }} == nil {
v = ipld.Null
break
}
{{- else if $field.IsOptional }}
if itr.node.{{ $field.Name }} == nil {
v = ipld.Undef
break
}
{{- else if $field.IsNullable }}
if itr.node.{{ $field.Name }} == nil {
v = ipld.Null
break
}
{{- end}}
v = itr.node.{{ $field.Name }}
{{- end}}
default:
panic("unreachable")
}
itr.idx++
return
}
func (itr *_{{ .Type.Name }}__itr) Done() bool {
return itr.idx >= {{ len .Type.Fields }}
}
`, w, gk)
}
func (gk generateKindStruct) EmitNodeMethodLength(w io.Writer) {
doTemplate(`
func ({{ .Type.Name }}) Length() int {
return {{ len .Type.Fields }}
}
`, w, gk)
}
func (gk generateKindStruct) EmitNodeMethodNodeBuilder(w io.Writer) {
doTemplate(`
func ({{ .Type.Name }}) NodeBuilder() ipld.NodeBuilder {
return nil // TODO EmitNodeMethodNodeBuilder
}
`, w, gk)
}
package gengo
import (
"io"
)
func emitMinima(f io.Writer) {
emitFileHeader(f)
// Avoid moderate annoyance keeping track of imports.
f.Write([]byte(`
var _ typed.Node = typed.Node(nil)
`))
// Iterator rejection thunks.
f.Write([]byte(`
type mapIteratorReject struct{ err error }
type listIteratorReject struct{ err error }
func (itr mapIteratorReject) Next() (ipld.Node, ipld.Node, error) { return nil, nil, itr.err }
func (itr mapIteratorReject) Done() bool { return false }
func (itr listIteratorReject) Next() (int, ipld.Node, error) { return -1, nil, itr.err }
func (itr listIteratorReject) Done() bool { return false }
`))
// Box type for map keys.
// f.Write([]byte(`
// type boxedString struct { x string }
// `))
//
// ... nevermind; we already need strings in the prelude. Use em.
}
......@@ -26,6 +26,7 @@ func TestNuevo(t *testing.T) {
tg.EmitNodeMethodMapIterator(w)
tg.EmitNodeMethodListIterator(w)
tg.EmitNodeMethodLength(w)
tg.EmitNodeMethodIsUndefined(w)
tg.EmitNodeMethodIsNull(w)
tg.EmitNodeMethodAsBool(w)
tg.EmitNodeMethodAsInt(w)
......@@ -36,20 +37,55 @@ func TestNuevo(t *testing.T) {
tg.EmitNodeMethodNodeBuilder(w)
}
f := openOrPanic("_test/thunks.go")
f := openOrPanic("_test/minima.go")
emitMinima(f)
tString := schema.SpawnString("String")
tStract := schema.SpawnStruct("Stract",
[]schema.StructField{schema.SpawnStructField(
"aField", tString, false, false,
)},
schema.StructRepresentation_Map{},
)
tStract2 := schema.SpawnStruct("Stract2",
[]schema.StructField{schema.SpawnStructField(
"nulble", tString, false, true,
)},
schema.StructRepresentation_Map{},
)
tStract3 := schema.SpawnStruct("Stract3",
[]schema.StructField{schema.SpawnStructField(
"noptble", tString, true, true,
)},
schema.StructRepresentation_Map{},
)
tStroct := schema.SpawnStruct("Stroct",
[]schema.StructField{
schema.SpawnStructField("f1", tString, false, false),
schema.SpawnStructField("f2", tString, true, false),
schema.SpawnStructField("f3", tString, true, true),
schema.SpawnStructField("f4", tString, false, false),
},
schema.StructRepresentation_Map{},
)
f = openOrPanic("_test/tString.go")
emitFileHeader(f)
emitType(NewGeneratorForKindString(tString), f)
f = openOrPanic("_test/Stract.go")
emitFileHeader(f)
doTemplate(`
type mapIteratorReject struct{ err error }
type listIteratorReject struct{ err error }
emitType(NewGeneratorForKindStruct(tStract), f)
func (itr mapIteratorReject) Next() (ipld.Node, ipld.Node, error) { return nil, nil, itr.err }
func (itr mapIteratorReject) Done() bool { return false }
f = openOrPanic("_test/Stract2.go")
emitFileHeader(f)
emitType(NewGeneratorForKindStruct(tStract2), f)
func (itr listIteratorReject) Next() (int, ipld.Node, error) { return -1, nil, itr.err }
func (itr listIteratorReject) Done() bool { return false }
`, f, nil)
f = openOrPanic("_test/Stract3.go")
emitFileHeader(f)
emitType(NewGeneratorForKindStruct(tStract3), f)
f = openOrPanic("_test/tStrang.go")
f = openOrPanic("_test/Stroct.go")
emitFileHeader(f)
emitType(generateKindString{"Strang", schema.TypeString{}}, f)
emitType(NewGeneratorForKindStruct(tStroct), f)
}
package gengo
import (
"testing"
"unsafe"
)
// This file contains some short informative tests that were useful to design.
// It's stuff that probably would be just as suited to life in the go playground.
func TestUnderstandingStructMemoryLayout(t *testing.T) {
t.Skip("This is for human information and not usually necessary to run.")
// This test informs why the additional bools for optional+nullable fields
// in structs are grouped together, even though it makes codegen more fiddly.
// https://go101.org/article/memory-layout.html also has a nice writeup.
t.Logf("%d\n", unsafe.Sizeof(struct {
x int32
y int32
}{})) // 8
t.Logf("%d\n", unsafe.Sizeof(struct {
x int32
a bool
y int32
b bool
}{})) // 16 ... ! word alignment.
t.Logf("%d\n", unsafe.Sizeof(struct {
a bool
b bool
x int32
y int32
}{})) // 12. consecutive bools get packed.
t.Logf("%d\n", unsafe.Sizeof(struct {
x int32
y int32
a bool
b bool
}{})) // 12. consecutive bool packing works anywhere.
t.Logf("%d\n", unsafe.Sizeof(struct {
x int32
y int32
a, b, c, d bool
}{})) // 12. bool packing works up to four.
t.Logf("%d\n", unsafe.Sizeof(struct {
x int32
y int32
a, b, c, d, e bool
}{})) // 16 ... ! bools take a byte; the fifth triggers a new word.
}
......@@ -4,11 +4,20 @@ import (
"io"
"text/template"
ipld "github.com/ipld/go-ipld-prime"
wish "github.com/warpfork/go-wish"
)
func doTemplate(tmplstr string, w io.Writer, data interface{}) {
tmpl := template.Must(template.New("").Parse(wish.Dedent(tmplstr)))
tmpl := template.Must(template.New("").
Funcs(template.FuncMap{
// 'ReprKindConst' returns the source-string for "ipld.ReprKind_{{Kind}}".
"ReprKindConst": func(k ipld.ReprKind) string {
return "ipld.ReprKind_" + k.String() // happens to be fairly trivial.
},
}).
Parse(wish.Dedent(tmplstr)))
if err := tmpl.Execute(w, data); err != nil {
panic(err)
}
......
package schema
import (
ipld "github.com/ipld/go-ipld-prime"
)
// Kind is an enum of kind in the IPLD Schema system.
//
// Note that schema.Kind is distinct from ipld.ReprKind!
// Schema kinds include concepts such as "struct" and "enum", which are
// concepts only introduced by the Schema layer, and not present in the
// Data Model layer.
type Kind uint8
const (
Kind_Invalid Kind = 0
Kind_Map Kind = '{'
Kind_List Kind = '['
Kind_Unit Kind = '1'
Kind_Bool Kind = 'b'
Kind_Int Kind = 'i'
Kind_Float Kind = 'f'
Kind_String Kind = 's'
Kind_Bytes Kind = 'x'
Kind_Link Kind = '/'
Kind_Struct Kind = '$'
Kind_Union Kind = '^'
Kind_Enum Kind = '%'
// FUTURE: Kind_Any = '?'?
)
func (k Kind) String() string {
switch k {
case Kind_Invalid:
return "Invalid"
case Kind_Map:
return "Map"
case Kind_List:
return "List"
case Kind_Unit:
return "Unit"
case Kind_Bool:
return "Bool"
case Kind_Int:
return "Int"
case Kind_Float:
return "Float"
case Kind_String:
return "String"
case Kind_Bytes:
return "Bytes"
case Kind_Link:
return "Link"
case Kind_Struct:
return "Struct"
case Kind_Union:
return "Union"
case Kind_Enum:
return "Enum"
default:
panic("invalid enumeration value!")
}
}
// ActsLike returns a constant from the ipld.ReprKind enum describing what
// this schema.Kind acts like at the Data Model layer.
//
// Things with similar names are generally conserved
// (e.g. "map" acts like "map");
// concepts added by the schema layer have to be mapped onto something
// (e.g. "struct" acts like "map").
//
// Note that this mapping describes how a typed Node will *act*, programmatically;
// it does not necessarily describe how it will be *serialized*
// (for example, a struct will always act like a map, even if it has a tuple
// representation strategy and thus becomes a list when serialized).
func (k Kind) ActsLike() ipld.ReprKind {
switch k {
case Kind_Invalid:
return ipld.ReprKind_Invalid
case Kind_Map:
return ipld.ReprKind_Map
case Kind_List:
return ipld.ReprKind_List
case Kind_Unit:
return ipld.ReprKind_Bool // maps to 'true'.
case Kind_Bool:
return ipld.ReprKind_Bool
case Kind_Int:
return ipld.ReprKind_Int
case Kind_Float:
return ipld.ReprKind_Float
case Kind_String:
return ipld.ReprKind_String
case Kind_Bytes:
return ipld.ReprKind_Bytes
case Kind_Link:
return ipld.ReprKind_Link
case Kind_Struct:
return ipld.ReprKind_Map // clear enough: fields are keys.
case Kind_Union:
return ipld.ReprKind_Map // REVIEW: unions are tricky.
case Kind_Enum:
return ipld.ReprKind_String // 'AsString' is the one clear thing to define.
default:
panic("invalid enumeration value!")
}
}
package schema
// Everything in this file is __a temporary hack__ and will be __removed__.
//
// These methods will only hang around until more of the "ast" packages are finished;
// thereafter, building schema.Type and schema.TypeSystem values will only be
// possible through first constructing a schema AST, and *then* using Reify(),
// which will validate things correctly, cycle-check, cross-link, etc.
//
// (Meanwhile, we're using these methods in the codegen prototypes.)
func SpawnString(name TypeName) TypeString {
return TypeString{anyType{name, nil}}
}
func SpawnStruct(name TypeName, fields []StructField, repr StructRepresentation) TypeStruct {
return TypeStruct{anyType{name, nil}, fields, repr}
}
func SpawnStructField(name string, typ Type, optional bool, nullable bool) StructField {
return StructField{name, typ, optional, nullable}
}
......@@ -43,7 +43,7 @@ type Type interface {
// Unexported marker method to force the union closed.
_Type()
// Returns a pointer to the typesystem.Universe this type is a member of.
// Returns a pointer to the TypeSystem this Type is a member of.
TypeSystem() *TypeSystem
// Returns the string name of the Type. This name is unique within the
......@@ -52,14 +52,24 @@ type Type interface {
// that string will not be required to be unique.
Name() TypeName
// Returns the Representation Kind in the IPLD Data Model that this type
// is expected to be serialized as.
// Returns the Kind of this Type.
//
// Note that in one case, this will return `ipld.ReprKind_Invalid` --
// TypeUnion with Style=Kinded may be serialized as different kinds
// depending on their value, so we can't say from the type definition
// alone what kind we expect.
ReprKind() ipld.ReprKind
// The returned value is a 1:1 association with which of the concrete
// "schema.Type*" structs this interface can be cast to.
//
// Note that a schema.Kind is a different enum than ipld.ReprKind;
// and furthermore, there's no strict relationship between them.
// typed.Node values can be described by *two* distinct ReprKinds:
// one which describes how the Node itself will act,
// and another which describes how the Node presents for serialization.
// For some combinations of Type and representation strategy, one or both
// of the ReprKinds can be determined statically; but not always:
// it can sometimes be necessary to inspect the value quite concretely
// (e.g., `typed.Node{}.Representation().ReprKind()`) in order to find
// out exactly how a node will be serialized! This is because some types
// can vary in representation kind based on their value (specifically,
// kinded-representation unions have this property).
Kind() Kind
}
var (
......@@ -141,8 +151,11 @@ var (
type TypeStruct struct {
anyType
tupleStyle bool // if true, ReprKind=Array instead of map (and optional fields are invalid!)
fields []StructField
// n.b. `Fields` is an (order-preserving!) map in the schema-schema;
// but it's a list here, with the keys denormalized into the value,
// because that's typically how we use it.
fields []StructField
representation StructRepresentation
}
type StructField struct {
name string
......@@ -151,6 +164,21 @@ type StructField struct {
nullable bool
}
type StructRepresentation interface{ _StructRepresentation() }
func (StructRepresentation_Map) _StructRepresentation() {}
func (StructRepresentation_Tuple) _StructRepresentation() {}
func (StructRepresentation_StringPairs) _StructRepresentation() {}
func (StructRepresentation_StringJoin) _StructRepresentation() {}
type StructRepresentation_Map struct {
renames map[string]string
implicits map[string]interface{}
}
type StructRepresentation_Tuple struct{}
type StructRepresentation_StringPairs struct{ sep1, sep2 string }
type StructRepresentation_StringJoin struct{ sep string }
type TypeEnum struct {
anyType
members []string
......
package schema
import (
"github.com/ipld/go-ipld-prime"
)
/* cookie-cutter standard interface stuff */
func (anyType) _Type() {}
func (t anyType) TypeSystem() *TypeSystem { return t.universe }
func (t anyType) Name() TypeName { return t.name }
func (TypeBool) ReprKind() ipld.ReprKind {
return ipld.ReprKind_Bool
}
func (TypeString) ReprKind() ipld.ReprKind {
return ipld.ReprKind_String
}
func (TypeBytes) ReprKind() ipld.ReprKind {
return ipld.ReprKind_Bytes
}
func (TypeInt) ReprKind() ipld.ReprKind {
return ipld.ReprKind_Int
}
func (TypeFloat) ReprKind() ipld.ReprKind {
return ipld.ReprKind_Float
}
func (TypeMap) ReprKind() ipld.ReprKind {
return ipld.ReprKind_Map
}
func (TypeList) ReprKind() ipld.ReprKind {
return ipld.ReprKind_List
}
func (TypeLink) ReprKind() ipld.ReprKind {
return ipld.ReprKind_Link
}
func (t TypeUnion) ReprKind() ipld.ReprKind {
if t.style == UnionStyle_Kinded {
return ipld.ReprKind_Invalid
} else {
return ipld.ReprKind_Map
}
}
func (t TypeStruct) ReprKind() ipld.ReprKind {
if t.tupleStyle {
return ipld.ReprKind_List
} else {
return ipld.ReprKind_Map
}
}
func (TypeEnum) ReprKind() ipld.ReprKind {
return ipld.ReprKind_String
}
func (TypeBool) Kind() Kind { return Kind_Bool }
func (TypeString) Kind() Kind { return Kind_String }
func (TypeBytes) Kind() Kind { return Kind_Bytes }
func (TypeInt) Kind() Kind { return Kind_Int }
func (TypeFloat) Kind() Kind { return Kind_Float }
func (TypeMap) Kind() Kind { return Kind_Map }
func (TypeList) Kind() Kind { return Kind_List }
func (TypeLink) Kind() Kind { return Kind_Link }
func (TypeUnion) Kind() Kind { return Kind_Union }
func (TypeStruct) Kind() Kind { return Kind_Struct }
func (TypeEnum) Kind() Kind { return Kind_Enum }
/* interesting methods per Type type */
......
package schema
import (
"fmt"
"path"
"github.com/ipld/go-ipld-prime"
)
// FUTURE: we also want something *almost* identical to this Validate method,
// but returning a `typed.Node` in the case of no error.
// (Such a method would go in the same package as `typed.Node`, presumably.)
// How do we avoid writing this method twice?
// Maybe both a Validate and Reify method belong in `typed` package,
// and Validate just returns less?
// No... Reify should probably short-circuit sooner?
// Unclear. Guess first step is that we need to decide the intended UX!
func Validate(ts TypeSystem, t Type, node ipld.Node) []error {
return validate(ts, t, node, "/")
}
// review: 'ts' param might not actually be necessary; everything relevant can be reached from t so far.
func validate(ts TypeSystem, t Type, node ipld.Node, pth string) []error {
switch t2 := t.(type) {
case TypeBool:
if node.ReprKind() != ipld.ReprKind_Bool {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name(), t.ReprKind(), pth, node.ReprKind())}
}
return nil
case TypeString:
if node.ReprKind() != ipld.ReprKind_String {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name(), t.ReprKind(), pth, node.ReprKind())}
}
return nil
case TypeBytes:
if node.ReprKind() != ipld.ReprKind_Bytes {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name(), t.ReprKind(), pth, node.ReprKind())}
}
return nil
case TypeInt:
if node.ReprKind() != ipld.ReprKind_Int {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name(), t.ReprKind(), pth, node.ReprKind())}
}
return nil
case TypeFloat:
if node.ReprKind() != ipld.ReprKind_Float {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name(), t.ReprKind(), pth, node.ReprKind())}
}
return nil
case TypeMap:
if node.ReprKind() != ipld.ReprKind_Map {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name(), t.ReprKind(), pth, node.ReprKind())}
}
errs := []error(nil)
for itr := node.MapIterator(); !itr.Done(); {
k, v, err := itr.Next()
if err != nil {
return []error{err}
}
// FUTURE: if KeyType is an enum rather than string, do membership check.
ks, _ := k.AsString()
if v.IsNull() {
if !t2.ValueIsNullable() {
errs = append(errs, fmt.Errorf("Schema match failed: map at path %q contains unpermitted null in key %q", pth, ks))
}
} else {
errs = append(errs, validate(ts, t2.ValueType(), v, path.Join(pth, ks))...)
}
}
return errs
case TypeList:
case TypeLink:
// TODO interesting case: would need resolver to keep checking.
case TypeUnion:
// TODO *several* interesting errors
case TypeStruct:
switch t2.tupleStyle {
case false: // as map!
if node.ReprKind() != ipld.ReprKind_Map {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name(), t.ReprKind(), pth, node.ReprKind())}
}
// TODO loop over em
// TODO REVIEW order strictness questions?
case true: // as array!
}
case TypeEnum:
// TODO another interesting error
}
return nil
}
/*
Okay, so. There are several fun considerations for a "validate" method.
---
There's two radically different approaches to "validate"/"reify":
- Option 1: Look at the schema.Type info and check if a data node seems
to match it -- recursing on the type info.
- Option 2: Use the schema.Type{}.RepresentationNodeBuilder() to feed data
into it -- recursing on what the nodebuilder already expresses.
(Option 2 also need to take a `memStorage ipld.NodeBuilder` param, btw,
for handling all the cases where we *aren't* doing codegen.)
Option 1 provides a little more opportunity for returning multiple errors.
Option 2 will generally have a hard time with that (nodebuilers are not
necessarily in a valid state after their first error encounter).
As a result of having these two options at all, we may indeed end up with
at least two very different functions -- despite seeming to do similar
things, their interior will radically diverge.
---
We may also need to consider distinct reification paths: we may want one
that returns a new node tree which is eagerly converted to typed.Node
recursively; and another that returns a lazyNode which wraps things
with their typed node constraints only as they're requested.
(Note that the latter would have interesting implications for any code
which has expectations about pointer equality consistency.)
---
A further fun issue which needs consideration: well, I'll just save a snip
of prospective docs I wrote while trying to iterate on these functions:
// Note that using Validate on a node that's already a typed.Node is likely
// to be nonsensical. In many schemas, the typed.Node tree is actually a
// different depth than its representational tree (e.g. unions can cause this),
... and that's ... that's a fairly sizable issue that needs resolving.
There's a couple of different ways to handle some of the behaviors around
unions, and some of them make the tradeoff described above, and I'm really
unsure if all the implications have been sussed out yet. We should defer
writing code that depends on this issue until gathering some more info.
---
One more note: about returning multiple errors from a Validate function:
there's an upper bound of the utility of the thing. Going farther than the
first parse error is nice, but it will still hit limits: for example,
upon encountering a union and failing to match it, we can't generally
produce further errors from anywhere deeper in the tree without them being
combinatorial "if previous juncture X was type Y, then..." nonsense.
(This applies to all recursive kinds to some degree, but it's especially
rough with unions. For most of the others, it's flatly a missing field,
or an excessive field, or a leaf error; with unions it can be hard to tell.)
---
And finally: both "Validate" and "Reify" methods might actually belong
in the typed.Node package -- if they make *any* reference to `typed.Node`,
then they have no choice (otherwise, cyclic imports would occur).
If we make a "Validate" that works purely on the schema.Type info, and
returns *only* errors: only then we can have it in the schema package.
*/
package schema
import (
"testing"
. "github.com/warpfork/go-wish"
"github.com/ipld/go-ipld-prime/impl/free"
)
func TestSimpleTypes(t *testing.T) {
t.Run("string alone", func(t *testing.T) {
n1, _ := ipldfree.NodeBuilder().CreateString("asdf")
t1 := TypeString{
anyType{name: "Foo"},
}
Wish(t,
Validate(TypeSystem{}, t1, n1),
ShouldEqual, []error(nil))
})
}
package ipld
var Null Node = nullNode{}
var Undef Node = undefNode{}
type nullNode struct{}
func (nullNode) ReprKind() ReprKind {
return ReprKind_Null
}
func (nullNode) TraverseField(key string) (Node, error) {
return nil, ErrWrongKind{MethodName: "<null>.TraverseField", AppropriateKind: ReprKindSet_JustMap, ActualKind: ReprKind_Null}
}
func (nullNode) TraverseIndex(idx int) (Node, error) {
return nil, ErrWrongKind{MethodName: "<null>.TraverseIndex", AppropriateKind: ReprKindSet_JustList, ActualKind: ReprKind_Null}
}
func (nullNode) MapIterator() MapIterator {
return mapIteratorReject{ErrWrongKind{MethodName: "<null>.MapIterator", AppropriateKind: ReprKindSet_JustMap, ActualKind: ReprKind_Null}}
}
func (nullNode) ListIterator() ListIterator {
return listIteratorReject{ErrWrongKind{MethodName: "<null>.ListIterator", AppropriateKind: ReprKindSet_JustList, ActualKind: ReprKind_Null}}
}
func (nullNode) Length() int {
return -1
}
func (nullNode) IsUndefined() bool {
return false
}
func (nullNode) IsNull() bool {
return true
}
func (nullNode) AsBool() (bool, error) {
return false, ErrWrongKind{MethodName: "<null>.AsBool", AppropriateKind: ReprKindSet_JustBool, ActualKind: ReprKind_Null}
}
func (nullNode) AsInt() (int, error) {
return 0, ErrWrongKind{MethodName: "<null>.AsInt", AppropriateKind: ReprKindSet_JustInt, ActualKind: ReprKind_Null}
}
func (nullNode) AsFloat() (float64, error) {
return 0, ErrWrongKind{MethodName: "<null>.AsFloat", AppropriateKind: ReprKindSet_JustFloat, ActualKind: ReprKind_Null}
}
func (nullNode) AsString() (string, error) {
return "", ErrWrongKind{MethodName: "<null>.AsString", AppropriateKind: ReprKindSet_JustString, ActualKind: ReprKind_Null}
}
func (nullNode) AsBytes() ([]byte, error) {
return nil, ErrWrongKind{MethodName: "<null>.AsBytes", AppropriateKind: ReprKindSet_JustBytes, ActualKind: ReprKind_Null}
}
func (nullNode) AsLink() (Link, error) {
return nil, ErrWrongKind{MethodName: "<null>.AsLink", AppropriateKind: ReprKindSet_JustLink, ActualKind: ReprKind_Null}
}
func (nullNode) NodeBuilder() NodeBuilder {
panic("cannot build null nodes")
}
type undefNode struct{ nullNode }
func (undefNode) IsUndefined() bool {
return true
}
func (undefNode) IsNull() bool {
return false
}
type mapIteratorReject struct{ err error }
type listIteratorReject struct{ err error }
func (itr mapIteratorReject) Next() (Node, Node, error) { return nil, nil, itr.err }
func (itr mapIteratorReject) Done() bool { return false }
func (itr listIteratorReject) Next() (int, Node, error) { return -1, nil, itr.err }
func (itr listIteratorReject) Done() bool { return false }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment