Commit 0827921d authored by Eric Myhre's avatar Eric Myhre

List gen support, and tests.

These are getting increasingly straightforward as most of the hard
problems have been ironed out already when getting the other recursives
(maps and structs) sorted.

Now, we just need to stamp out the rest of the scalars, and I think
this codegen stuff is at least early-alpha level usable!

Also in this commit: added some new error types and fixed up the
basicnode.List implementation to use it, removing some todos there.
parent 7975725a
......@@ -99,6 +99,29 @@ func (e ErrInvalidKey) Error() string {
}
}
// ErrInvalidSegmentForList is returned when using Node.LookupSegment and the
// given PathSegment can't be applied to a list because it's unparsable as a number.
type ErrInvalidSegmentForList struct {
// TypeName may indicate the named type of a node the function was called on,
// or be empty string if working on untyped data.
TypeName string
// TroubleSegment is the segment we couldn't use.
TroubleSegment PathSegment
// Reason may explain more about why the PathSegment couldn't be used;
// in practice, it's probably a 'strconv.NumError'.
Reason error
}
func (e ErrInvalidSegmentForList) Error() string {
v := "invalid segment for lookup on a list"
if e.TypeName != "" {
v += " of type " + e.TypeName
}
return v + fmt.Sprintf(": %q: %s", e.TroubleSegment.s, e)
}
// ErrUnmatchable is the catch-all type for parse errors in schema representation work.
//
// REVIEW: are builders at type level ever going to return this? i don't think so.
......
......@@ -39,7 +39,7 @@ func (n *plainList) LookupIndex(idx int) (ipld.Node, error) {
func (n *plainList) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) {
idx, err := seg.Index()
if err != nil {
panic("todo name this kind of error")
return nil, ipld.ErrInvalidSegmentForList{TroubleSegment: seg, Reason: err}
}
return n.LookupIndex(idx)
}
......
package gengo
import (
"io"
"github.com/ipld/go-ipld-prime/schema"
"github.com/ipld/go-ipld-prime/schema/gen/go/mixins"
)
type listGenerator struct {
AdjCfg *AdjunctCfg
mixins.ListTraits
PkgName string
Type schema.TypeList
}
// --- native content and specializations --->
func (g listGenerator) EmitNativeType(w io.Writer) {
// Lists are a pretty straightforward struct enclosing a slice.
doTemplate(`
type _{{ .Type | TypeSymbol }} struct {
x []_{{ .Type.ValueType | TypeSymbol }}{{if .Type.ValueIsNullable }}__Maybe{{end}}
}
type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}
`, w, g.AdjCfg, g)
}
func (g listGenerator) EmitNativeAccessors(w io.Writer) {
// TODO: come back to this
}
func (g listGenerator) EmitNativeBuilder(w io.Writer) {
// Not yet clear what exactly might be most worth emitting here.
}
func (g listGenerator) EmitNativeMaybe(w io.Writer) {
emitNativeMaybe(w, g.AdjCfg, g)
}
// --- type info --->
func (g listGenerator) EmitTypeConst(w io.Writer) {
doTemplate(`
// TODO EmitTypeConst
`, w, g.AdjCfg, g)
}
// --- TypedNode interface satisfaction --->
func (g listGenerator) EmitTypedNodeMethodType(w io.Writer) {
doTemplate(`
func ({{ .Type | TypeSymbol }}) Type() schema.Type {
return nil /*TODO:typelit*/
}
`, w, g.AdjCfg, g)
}
func (g listGenerator) EmitTypedNodeMethodRepresentation(w io.Writer) {
// Perhaps surprisingly, the way to get the representation node pointer
// does not actually depend on what the representation strategy is.
// REVIEW: this appears to be standard even across kinds; can we extract it?
doTemplate(`
func (n {{ .Type | TypeSymbol }}) Representation() ipld.Node {
return (*_{{ .Type | TypeSymbol }}__Repr)(n)
}
`, w, g.AdjCfg, g)
}
// --- Node interface satisfaction --->
func (g listGenerator) EmitNodeType(w io.Writer) {
// No additional types needed. Methods all attach to the native type.
}
func (g listGenerator) EmitNodeTypeAssertions(w io.Writer) {
doTemplate(`
var _ ipld.Node = ({{ .Type | TypeSymbol }})(&_{{ .Type | TypeSymbol }}{})
var _ schema.TypedNode = ({{ .Type | TypeSymbol }})(&_{{ .Type | TypeSymbol }}{})
`, w, g.AdjCfg, g)
}
func (g listGenerator) EmitNodeMethodLookupIndex(w io.Writer) {
doTemplate(`
func (n {{ .Type | TypeSymbol }}) LookupIndex(idx int) (ipld.Node, error) {
if n.Length() <= idx {
return nil, ipld.ErrNotExists{ipld.PathSegmentOfInt(idx)}
}
v := &n.x[idx]
{{- if .Type.ValueIsNullable }}
if v.m == schema.Maybe_Null {
return ipld.Null, nil
}
return {{ if not (MaybeUsesPtr .Type.ValueType) }}&{{end}}v.v, nil
{{- else}}
return v, nil
{{- end}}
}
`, w, g.AdjCfg, g)
}
func (g listGenerator) EmitNodeMethodLookup(w io.Writer) {
// LookupNode will procede by coercing to int if it can; or fail; those are really the only options.
// REVIEW: how much coercion is done by other types varies quite wildly. so we should figure out if that inconsistency is acceptable, and at least document it if so.
doTemplate(`
func (n {{ .Type | TypeSymbol }}) Lookup(k ipld.Node) (ipld.Node, error) {
idx, err := k.AsInt()
if err != nil {
return nil, err
}
return n.LookupIndex(idx)
}
`, w, g.AdjCfg, g)
}
func (g listGenerator) EmitNodeMethodListIterator(w io.Writer) {
doTemplate(`
func (n {{ .Type | TypeSymbol }}) ListIterator() ipld.ListIterator {
return &_{{ .Type | TypeSymbol }}__ListItr{n, 0}
}
type _{{ .Type | TypeSymbol }}__ListItr struct {
n {{ .Type | TypeSymbol }}
idx int
}
func (itr *_{{ .Type | TypeSymbol }}__ListItr) Next() (idx int, v ipld.Node, _ error) {
if itr.idx >= len(itr.n.x) {
return -1, nil, ipld.ErrIteratorOverread{}
}
idx = itr.idx
x := &itr.n.x[itr.idx]
{{- if .Type.ValueIsNullable }}
switch x.m {
case schema.Maybe_Null:
v = ipld.Null
case schema.Maybe_Value:
v = {{ if not (MaybeUsesPtr .Type.ValueType) }}&{{end}}x.v
}
{{- else}}
v = x
{{- end}}
itr.idx++
return
}
func (itr *_{{ .Type | TypeSymbol }}__ListItr) Done() bool {
return itr.idx >= len(itr.n.x)
}
`, w, g.AdjCfg, g)
}
func (g listGenerator) EmitNodeMethodLength(w io.Writer) {
doTemplate(`
func (n {{ .Type | TypeSymbol }}) Length() int {
return len(n.x)
}
`, w, g.AdjCfg, g)
}
func (g listGenerator) EmitNodeMethodStyle(w io.Writer) {
// REVIEW: this appears to be standard even across kinds; can we extract it?
doTemplate(`
func ({{ .Type | TypeSymbol }}) Style() ipld.NodeStyle {
return _{{ .Type | TypeSymbol }}__Style{}
}
`, w, g.AdjCfg, g)
}
func (g listGenerator) EmitNodeStyleType(w io.Writer) {
// REVIEW: this appears to be standard even across kinds; can we extract it?
doTemplate(`
type _{{ .Type | TypeSymbol }}__Style struct{}
func (_{{ .Type | TypeSymbol }}__Style) NewBuilder() ipld.NodeBuilder {
var nb _{{ .Type | TypeSymbol }}__Builder
nb.Reset()
return &nb
}
`, w, g.AdjCfg, g)
}
// --- NodeBuilder and NodeAssembler --->
func (g listGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator {
return listBuilderGenerator{
g.AdjCfg,
mixins.ListAssemblerTraits{
g.PkgName,
g.TypeName,
"_" + g.AdjCfg.TypeSymbol(g.Type) + "__",
},
g.PkgName,
g.Type,
}
}
type listBuilderGenerator struct {
AdjCfg *AdjunctCfg
mixins.ListAssemblerTraits
PkgName string
Type schema.TypeList
}
func (g listBuilderGenerator) EmitNodeBuilderType(w io.Writer) {
doTemplate(`
type _{{ .Type | TypeSymbol }}__Builder struct {
_{{ .Type | TypeSymbol }}__Assembler
}
`, w, g.AdjCfg, g)
}
func (g listBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) {
doTemplate(`
func (nb *_{{ .Type | TypeSymbol }}__Builder) Build() ipld.Node {
if nb.state != laState_finished {
panic("invalid state: assembler for {{ .PkgName }}.{{ .Type.Name }} must be 'finished' before Build can be called!")
}
return nb.w
}
func (nb *_{{ .Type | TypeSymbol }}__Builder) Reset() {
var w _{{ .Type | TypeSymbol }}
var m schema.Maybe
*nb = _{{ .Type | TypeSymbol }}__Builder{_{{ .Type | TypeSymbol }}__Assembler{w: &w, m: &m, state: laState_initial}}
}
`, w, g.AdjCfg, g)
}
func (g listBuilderGenerator) EmitNodeAssemblerType(w io.Writer) {
// - 'w' is the "**w**ip" pointer.
// - 'm' is the **m**aybe which communicates our completeness to the parent if we're a child assembler.
// - 'state' is what it says on the tin. this is used for the list state (the broad transitions between null, start-list, and finish are handled by 'm' for consistency with other types).
//
// - 'cm' is **c**hild **m**aybe and is used for the completion message from children.
// It's only present if list values *aren't* allowed to be nullable, since otherwise they have their own per-value maybe slot we can use.
// - 'va' is the embedded child value assembler.
doTemplate(`
type _{{ .Type | TypeSymbol }}__Assembler struct {
w *_{{ .Type | TypeSymbol }}
m *schema.Maybe
state laState
{{ if not .Type.ValueIsNullable }}cm schema.Maybe{{end}}
va _{{ .Type.ValueType | TypeSymbol }}__Assembler
}
func (na *_{{ .Type | TypeSymbol }}__Assembler) reset() {
na.state = laState_initial
na.va.reset()
}
`, w, g.AdjCfg, g)
}
func (g listBuilderGenerator) EmitNodeAssemblerMethodBeginList(w io.Writer) {
// This method contains a branch to support MaybeUsesPtr because new memory may need to be allocated.
// This allocation only happens if the 'w' ptr is nil, which means we're being used on a Maybe;
// otherwise, the 'w' ptr should already be set, and we fill that memory location without allocating, as usual.
doTemplate(`
func (na *_{{ .Type | TypeSymbol }}__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) {
switch *na.m {
case schema.Maybe_Value, schema.Maybe_Null:
panic("invalid state: cannot assign into assembler that's already finished")
case midvalue:
panic("invalid state: it makes no sense to 'begin' twice on the same assembler!")
}
*na.m = midvalue
if sizeHint < 0 {
sizeHint = 0
}
{{- if .Type | MaybeUsesPtr }}
if na.w == nil {
na.w = &_{{ .Type | TypeSymbol }}{}
}
{{- end}}
if sizeHint > 0 {
na.w.x = make([]_{{ .Type.ValueType | TypeSymbol }}{{if .Type.ValueIsNullable }}__Maybe{{end}}, 0, sizeHint)
}
return na, nil
}
`, w, g.AdjCfg, g)
}
func (g listBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) {
// DRY: this seems awfully similar -- almost exact, even -- *even* to anything mapoid (except the mixin type name contains 'List' instead of 'Map').
doTemplate(`
func (na *_{{ .Type | TypeSymbol }}__Assembler) AssignNull() error {
switch *na.m {
case allowNull:
*na.m = schema.Maybe_Null
return nil
case schema.Maybe_Absent:
return mixins.ListAssembler{"{{ .PkgName }}.{{ .TypeName }}"}.AssignNull()
case schema.Maybe_Value, schema.Maybe_Null:
panic("invalid state: cannot assign into assembler that's already finished")
case midvalue:
panic("invalid state: cannot assign null into an assembler that's already begun working on recursive structures!")
}
panic("unreachable")
}
`, w, g.AdjCfg, g)
}
func (g listBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) {
// AssignNode goes through three phases:
// 1. is it null? Jump over to AssignNull (which may or may not reject it).
// 2. is it our own type? Handle specially -- we might be able to do efficient things.
// 3. is it the right kind to morph into us? Do so.
//
// We do not set m=midvalue in phase 3 -- it shouldn't matter unless you're trying to pull off concurrent access, which is wrong and unsafe regardless.
doTemplate(`
func (na *_{{ .Type | TypeSymbol }}__Assembler) AssignNode(v ipld.Node) error {
if v.IsNull() {
return na.AssignNull()
}
if v2, ok := v.(*_{{ .Type | TypeSymbol }}); ok {
switch *na.m {
case schema.Maybe_Value, schema.Maybe_Null:
panic("invalid state: cannot assign into assembler that's already finished")
case midvalue:
panic("invalid state: cannot assign null into an assembler that's already begun working on recursive structures!")
}
{{- if .Type | MaybeUsesPtr }}
if na.w == nil {
na.w = v2
*na.m = schema.Maybe_Value
return nil
}
{{- end}}
*na.w = *v2
*na.m = schema.Maybe_Value
return nil
}
if v.ReprKind() != ipld.ReprKind_List {
return ipld.ErrWrongKind{TypeName: "{{ .PkgName }}.{{ .Type.Name }}", MethodName: "AssignNode", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: v.ReprKind()}
}
itr := v.ListIterator()
for !itr.Done() {
_, v, err := itr.Next()
if err != nil {
return err
}
if err := na.AssembleValue().AssignNode(v); err != nil {
return err
}
}
return na.Finish()
}
`, w, g.AdjCfg, g)
}
func (g listBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) {
g.emitListAssemblerValueTidyHelper(w)
g.emitListAssemblerMethods(w)
}
func (g listBuilderGenerator) emitListAssemblerValueTidyHelper(w io.Writer) {
// This function attempts to clean up the state machine to acknolwedge child value assembly finish.
// If the child was finished and we just collected it, return true and update state to laState_initial.
// Otherwise, if it wasn't done, return false;
// and the caller is almost certain to emit an error momentarily.
// The function will only be called when the current state is laState_midValue.
// (In general, the idea is that if the user is doing things correctly,
// this function will only be called when the child is in fact finished.)
// If 'cm' is used, we reset it to its initial condition of Maybe_Absent here.
// At the same time, we nil the 'w' pointer for the child assembler; otherwise its own state machine would probably let it modify 'w' again!
doTemplate(`
func (la *_{{ .Type | TypeSymbol }}__Assembler) valueFinishTidy() bool {
{{- if .Type.ValueIsNullable }}
row := &la.w.x[len(la.w.x)-1]
switch row.m {
case schema.Maybe_Value:
{{- if (MaybeUsesPtr .Type.ValueType) }}
row.v = la.va.w
{{- end}}
fallthrough
case schema.Maybe_Null:
la.state = laState_initial
la.va.reset()
return true
{{- else}}
switch la.cm {
case schema.Maybe_Value:
la.va.w = nil
la.cm = schema.Maybe_Absent
la.state = laState_initial
la.va.reset()
return true
{{- end}}
default:
return false
}
}
`, w, g.AdjCfg, g)
}
func (g listBuilderGenerator) emitListAssemblerMethods(w io.Writer) {
doTemplate(`
func (la *_{{ .Type | TypeSymbol }}__Assembler) AssembleValue() ipld.NodeAssembler {
switch la.state {
case laState_initial:
// carry on
case laState_midValue:
if !la.valueFinishTidy() {
panic("invalid state: AssembleValue cannot be called when still in the middle of assembling the previous value")
} // if tidy success: carry on
case laState_finished:
panic("invalid state: AssembleValue cannot be called on an assembler that's already finished")
}
la.w.x = append(la.w.x, _{{ .Type.ValueType | TypeSymbol }}{{if .Type.ValueIsNullable }}__Maybe{{end}}{})
la.state = laState_midValue
row := &la.w.x[len(la.w.x)-1]
{{- if .Type.ValueIsNullable }}
{{- if not (MaybeUsesPtr .Type.ValueType) }}
la.va.w = &row.v
{{- end}}
la.va.m = &row.m
row.m = allowNull
{{- else}}
la.va.w = row
la.va.m = &la.cm
{{- end}}
return &la.va
}
`, w, g.AdjCfg, g)
doTemplate(`
func (la *_{{ .Type | TypeSymbol }}__Assembler) Finish() error {
switch la.state {
case laState_initial:
// carry on
case laState_midValue:
if !la.valueFinishTidy() {
panic("invalid state: Finish cannot be called when in the middle of assembling a value")
} // if tidy success: carry on
case laState_finished:
panic("invalid state: Finish cannot be called on an assembler that's already finished")
}
la.state = laState_finished
*la.m = schema.Maybe_Value
return nil
}
`, w, g.AdjCfg, g)
doTemplate(`
func (la *_{{ .Type | TypeSymbol }}__Assembler) ValueStyle(_ int) ipld.NodeStyle {
return _{{ .Type.ValueType | TypeSymbol }}__Style{}
}
`, w, g.AdjCfg, g)
}
package gengo
import (
"io"
"github.com/ipld/go-ipld-prime/schema"
"github.com/ipld/go-ipld-prime/schema/gen/go/mixins"
)
var _ TypeGenerator = &listReprListGenerator{}
func NewListReprListGenerator(pkgName string, typ schema.TypeList, adjCfg *AdjunctCfg) TypeGenerator {
return listReprListGenerator{
listGenerator{
adjCfg,
mixins.ListTraits{
pkgName,
string(typ.Name()),
adjCfg.TypeSymbol(typ),
},
pkgName,
typ,
},
}
}
type listReprListGenerator struct {
listGenerator
}
func (g listReprListGenerator) GetRepresentationNodeGen() NodeGenerator {
return listReprListReprGenerator{
g.AdjCfg,
mixins.ListTraits{
g.PkgName,
string(g.Type.Name()) + ".Repr",
"_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr",
},
g.PkgName,
g.Type,
}
}
type listReprListReprGenerator struct {
AdjCfg *AdjunctCfg
mixins.ListTraits
PkgName string
Type schema.TypeList
}
func (g listReprListReprGenerator) EmitNodeType(w io.Writer) {
// Since this is a "natural" representation... there's just a type alias here.
// No new functions are necessary.
doTemplate(`
type _{{ .Type | TypeSymbol }}__Repr = _{{ .Type | TypeSymbol }}
`, w, g.AdjCfg, g)
}
func (g listReprListReprGenerator) EmitNodeTypeAssertions(w io.Writer) {
doTemplate(`
var _ ipld.Node = &_{{ .Type | TypeSymbol }}__Repr{}
`, w, g.AdjCfg, g)
}
func (listReprListReprGenerator) EmitNodeMethodReprKind(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodLookupString(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodLookup(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodLookupIndex(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodLookupSegment(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodMapIterator(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodListIterator(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodLength(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodIsUndefined(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodIsNull(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodAsBool(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodAsInt(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodAsFloat(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodAsString(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodAsBytes(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodAsLink(io.Writer) {}
func (listReprListReprGenerator) EmitNodeMethodStyle(io.Writer) {}
func (g listReprListReprGenerator) EmitNodeStyleType(w io.Writer) {
// Since this is a "natural" representation... there's just a type alias here.
// No new functions are necessary.
doTemplate(`
type _{{ .Type | TypeSymbol }}__ReprStyle = _{{ .Type | TypeSymbol }}__Style
`, w, g.AdjCfg, g)
}
func (g listReprListReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator {
return nil // TODO
}
......@@ -38,5 +38,13 @@ func EmitInternalEnums(packageName string, w io.Writer) {
maState_midValue
maState_finished
)
type laState uint8
const (
laState_initial laState = iota
laState_midValue
laState_finished
)
`))
}
......@@ -34,6 +34,8 @@ func Generate(pth string, pkgName string, ts schema.TypeSystem, adjCfg *AdjunctC
}
case schema.TypeMap:
EmitEntireType(NewMapReprMapGenerator(pkgName, t2, adjCfg), f)
case schema.TypeList:
EmitEntireType(NewListReprListGenerator(pkgName, t2, adjCfg), f)
default:
panic("add more type switches here :)")
}
......
package mixins
import (
"io"
ipld "github.com/ipld/go-ipld-prime"
)
type ListTraits struct {
PkgName string
TypeName string // see doc in kindTraitsGenerator
TypeSymbol string // see doc in kindTraitsGenerator
}
func (g ListTraits) EmitNodeMethodReprKind(w io.Writer) {
doTemplate(`
func ({{ .TypeSymbol }}) ReprKind() ipld.ReprKind {
return ipld.ReprKind_List
}
`, w, g)
}
func (g ListTraits) EmitNodeMethodLookupString(w io.Writer) {
kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_List}.emitNodeMethodLookupString(w)
}
func (g ListTraits) EmitNodeMethodLookupSegment(w io.Writer) {
doTemplate(`
func (n {{ .TypeSymbol }}) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) {
i, err := seg.Index()
if err != nil {
return nil, ipld.ErrInvalidSegmentForList{TypeName: "{{ .PkgName }}.{{ .TypeName }}", TroubleSegment: seg, Reason: err}
}
return n.LookupIndex(i)
}
`, w, g)
}
func (g ListTraits) EmitNodeMethodMapIterator(w io.Writer) {
kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_List}.emitNodeMethodMapIterator(w)
}
func (g ListTraits) EmitNodeMethodIsUndefined(w io.Writer) {
kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_List}.emitNodeMethodIsUndefined(w)
}
func (g ListTraits) EmitNodeMethodIsNull(w io.Writer) {
kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_List}.emitNodeMethodIsNull(w)
}
func (g ListTraits) EmitNodeMethodAsBool(w io.Writer) {
kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_List}.emitNodeMethodAsBool(w)
}
func (g ListTraits) EmitNodeMethodAsInt(w io.Writer) {
kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_List}.emitNodeMethodAsInt(w)
}
func (g ListTraits) EmitNodeMethodAsFloat(w io.Writer) {
kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_List}.emitNodeMethodAsFloat(w)
}
func (g ListTraits) EmitNodeMethodAsString(w io.Writer) {
kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_List}.emitNodeMethodAsString(w)
}
func (g ListTraits) EmitNodeMethodAsBytes(w io.Writer) {
kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_List}.emitNodeMethodAsBytes(w)
}
func (g ListTraits) EmitNodeMethodAsLink(w io.Writer) {
kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_List}.emitNodeMethodAsLink(w)
}
type ListAssemblerTraits struct {
PkgName string
TypeName string // see doc in kindAssemblerTraitsGenerator
AppliedPrefix string // see doc in kindAssemblerTraitsGenerator
}
func (g ListAssemblerTraits) EmitNodeAssemblerMethodBeginMap(w io.Writer) {
kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_List}.emitNodeAssemblerMethodBeginMap(w)
}
func (g ListAssemblerTraits) EmitNodeAssemblerMethodAssignNull(w io.Writer) {
kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_List}.emitNodeAssemblerMethodAssignNull(w)
}
func (g ListAssemblerTraits) EmitNodeAssemblerMethodAssignBool(w io.Writer) {
kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_List}.emitNodeAssemblerMethodAssignBool(w)
}
func (g ListAssemblerTraits) EmitNodeAssemblerMethodAssignInt(w io.Writer) {
kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_List}.emitNodeAssemblerMethodAssignInt(w)
}
func (g ListAssemblerTraits) EmitNodeAssemblerMethodAssignFloat(w io.Writer) {
kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_List}.emitNodeAssemblerMethodAssignFloat(w)
}
func (g ListAssemblerTraits) EmitNodeAssemblerMethodAssignString(w io.Writer) {
kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_List}.emitNodeAssemblerMethodAssignString(w)
}
func (g ListAssemblerTraits) EmitNodeAssemblerMethodAssignBytes(w io.Writer) {
kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_List}.emitNodeAssemblerMethodAssignBytes(w)
}
func (g ListAssemblerTraits) EmitNodeAssemblerMethodAssignLink(w io.Writer) {
kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_List}.emitNodeAssemblerMethodAssignLink(w)
}
func (g ListAssemblerTraits) EmitNodeAssemblerMethodStyle(w io.Writer) {
kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_List}.emitNodeAssemblerMethodStyle(w)
}
package gengo
import (
"testing"
. "github.com/warpfork/go-wish"
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/fluent"
"github.com/ipld/go-ipld-prime/must"
"github.com/ipld/go-ipld-prime/schema"
)
func TestListsContainingMaybe(t *testing.T) {
ts := schema.TypeSystem{}
ts.Init()
adjCfg := &AdjunctCfg{
maybeUsesPtr: map[schema.TypeName]bool{},
}
ts.Accumulate(schema.SpawnString("String"))
ts.Accumulate(schema.SpawnList("List__String",
ts.TypeByName("String"), false))
ts.Accumulate(schema.SpawnList("List__nullableString",
ts.TypeByName("String"), true))
test := func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) {
t.Run("non-nullable", func(t *testing.T) {
ns := getStyleByName("List__String")
nsr := getStyleByName("List__String.Repr")
var n schema.TypedNode
t.Run("typed-create", func(t *testing.T) {
n = fluent.MustBuildList(ns, 2, func(la fluent.ListAssembler) {
la.AssembleValue().AssignString("1")
la.AssembleValue().AssignString("2")
}).(schema.TypedNode)
t.Run("typed-read", func(t *testing.T) {
Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_List)
Wish(t, n.Length(), ShouldEqual, 2)
Wish(t, must.String(must.Node(n.LookupIndex(0))), ShouldEqual, "1")
Wish(t, must.String(must.Node(n.LookupIndex(1))), ShouldEqual, "2")
_, err := n.LookupIndex(3)
Wish(t, err, ShouldBeSameTypeAs, ipld.ErrNotExists{})
})
t.Run("repr-read", func(t *testing.T) {
nr := n.Representation()
Require(t, nr.ReprKind(), ShouldEqual, ipld.ReprKind_List)
Wish(t, nr.Length(), ShouldEqual, 2)
Wish(t, must.String(must.Node(nr.LookupIndex(0))), ShouldEqual, "1")
Wish(t, must.String(must.Node(nr.LookupIndex(1))), ShouldEqual, "2")
_, err := n.LookupIndex(3)
Wish(t, err, ShouldBeSameTypeAs, ipld.ErrNotExists{})
})
})
t.Run("repr-create", func(t *testing.T) {
nr := fluent.MustBuildList(nsr, 2, func(la fluent.ListAssembler) {
la.AssembleValue().AssignString("1")
la.AssembleValue().AssignString("2")
})
Wish(t, n, ShouldEqual, nr)
})
})
t.Run("nullable", func(t *testing.T) {
ns := getStyleByName("List__nullableString")
nsr := getStyleByName("List__nullableString.Repr")
var n schema.TypedNode
t.Run("typed-create", func(t *testing.T) {
n = fluent.MustBuildList(ns, 2, func(la fluent.ListAssembler) {
la.AssembleValue().AssignString("1")
la.AssembleValue().AssignNull()
}).(schema.TypedNode)
t.Run("typed-read", func(t *testing.T) {
Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_List)
Wish(t, n.Length(), ShouldEqual, 2)
Wish(t, must.String(must.Node(n.LookupIndex(0))), ShouldEqual, "1")
Wish(t, must.Node(n.LookupIndex(1)), ShouldEqual, ipld.Null)
_, err := n.LookupIndex(3)
Wish(t, err, ShouldBeSameTypeAs, ipld.ErrNotExists{})
})
t.Run("repr-read", func(t *testing.T) {
nr := n.Representation()
Require(t, nr.ReprKind(), ShouldEqual, ipld.ReprKind_List)
Wish(t, nr.Length(), ShouldEqual, 2)
Wish(t, must.String(must.Node(n.LookupIndex(0))), ShouldEqual, "1")
Wish(t, must.Node(n.LookupIndex(1)), ShouldEqual, ipld.Null)
_, err := n.LookupIndex(3)
Wish(t, err, ShouldBeSameTypeAs, ipld.ErrNotExists{})
})
})
t.Run("repr-create", func(t *testing.T) {
nr := fluent.MustBuildList(nsr, 2, func(la fluent.ListAssembler) {
la.AssembleValue().AssignString("1")
la.AssembleValue().AssignNull()
})
Wish(t, n, ShouldEqual, nr)
})
})
}
t.Run("maybe-using-embed", func(t *testing.T) {
adjCfg.maybeUsesPtr["String"] = false
prefix := "lists-embed"
pkgName := "main"
genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) {
test(t, getStyleByName)
})
})
t.Run("maybe-using-ptr", func(t *testing.T) {
adjCfg.maybeUsesPtr["String"] = true
prefix := "lists-mptr"
pkgName := "main"
genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) {
test(t, getStyleByName)
})
})
}
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