Commit 14083ea0 authored by Eric Myhre's avatar Eric Myhre

List implementation.

This... just about completes the pantheon of basic stuff in ipldfree
that's getting a reimplementation.

Maps need to be updated to also support recursing into a list, still.

Tests needed badly.  Next couple of commits will start handling that.
Need to reorganize some of the existing ones as well.  This is clearly
going to be one of those things that needs a large corpus... and for
bonus fun, we need to make a corpus that's ready to go for both working
with schemas and without.  Fun fun fun.
parent a52af3ba
package impls
import (
ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution"
"github.com/ipld/go-ipld-prime/_rsrch/nodesolution/impls/mixins"
)
var (
_ ipld.Node = &plainList{}
_ ipld.NodeStyle = Style__List{}
_ ipld.NodeBuilder = &plainList__Builder{}
_ ipld.NodeAssembler = &plainList__Assembler{}
)
// plainList is a concrete type that provides a list-kind ipld.Node.
// It can contain any kind of value.
// plainList is also embedded in the 'any' struct and usable from there.
type plainList struct {
x []ipld.Node
}
// -- Node interface methods -->
func (plainList) ReprKind() ipld.ReprKind {
return ipld.ReprKind_List
}
func (plainList) LookupString(string) (ipld.Node, error) {
return mixins.List{"list"}.LookupString("")
}
func (plainList) Lookup(ipld.Node) (ipld.Node, error) {
return mixins.List{"list"}.Lookup(nil)
}
func (n *plainList) LookupIndex(idx int) (ipld.Node, error) {
if n.Length() <= idx {
return nil, ipld.ErrNotExists{ipld.PathSegmentOfInt(idx)}
}
return n.x[idx], nil
}
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 n.LookupIndex(idx)
}
func (plainList) MapIterator() ipld.MapIterator {
return nil
}
func (n *plainList) ListIterator() ipld.ListIterator {
return &plainList_ListIterator{n, 0}
}
func (n *plainList) Length() int {
return len(n.x)
}
func (plainList) IsUndefined() bool {
return false
}
func (plainList) IsNull() bool {
return false
}
func (plainList) AsBool() (bool, error) {
return mixins.List{"list"}.AsBool()
}
func (plainList) AsInt() (int, error) {
return mixins.List{"list"}.AsInt()
}
func (plainList) AsFloat() (float64, error) {
return mixins.List{"list"}.AsFloat()
}
func (plainList) AsString() (string, error) {
return mixins.List{"list"}.AsString()
}
func (plainList) AsBytes() ([]byte, error) {
return mixins.List{"list"}.AsBytes()
}
func (plainList) AsLink() (ipld.Link, error) {
return mixins.List{"list"}.AsLink()
}
func (plainList) Style() ipld.NodeStyle {
return Style__List{}
}
type plainList_ListIterator struct {
n *plainList
idx int
}
func (itr *plainList_ListIterator) Next() (idx int, v ipld.Node, _ error) {
if itr.Done() {
return -1, nil, ipld.ErrIteratorOverread{}
}
v = itr.n.x[itr.idx]
idx = itr.idx
itr.idx++
return
}
func (itr *plainList_ListIterator) Done() bool {
return itr.idx >= len(itr.n.x)
}
// -- NodeStyle -->
type Style__List struct{}
func (Style__List) NewBuilder() ipld.NodeBuilder {
return &plainList__Builder{plainList__Assembler{w: &plainList{}}}
}
// -- NodeBuilder -->
type plainList__Builder struct {
plainList__Assembler
}
func (nb *plainList__Builder) Build() ipld.Node {
if nb.state != laState_finished {
panic("invalid state: assembler must be 'finished' before Build can be called!")
}
return nb.w
}
func (nb *plainList__Builder) Reset() {
*nb = plainList__Builder{}
nb.w = &plainList{}
}
// -- NodeAssembler -->
type plainList__Assembler struct {
w *plainList
va plainList__ValueAssembler
state laState
}
type plainList__ValueAssembler struct {
la *plainList__Assembler
}
// laState is an enum of the state machine for a list assembler.
// (this might be something to export reusably, but it's also very much an impl detail that need not be seen, so, dubious.)
// it's similar to maState for maps, but has fewer states because we never have keys to assemble.
type laState uint8
const (
laState_initial laState = iota // also the 'expect value or finish' state
laState_midValue // waiting for a 'finished' state in the ValueAssembler.
laState_finished // 'w' will also be nil, but this is a politer statement
)
func (plainList__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) {
return mixins.ListAssembler{"list"}.BeginMap(0)
}
func (na *plainList__Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) {
// Allocate storage space.
na.w.x = make([]ipld.Node, 0, sizeHint)
// That's it; return self as the ListNodeAssembler. We already have all the right methods on this structure.
return na, nil
}
func (plainList__Assembler) AssignNull() error {
return mixins.ListAssembler{"list"}.AssignNull()
}
func (plainList__Assembler) AssignBool(bool) error {
return mixins.ListAssembler{"list"}.AssignBool(false)
}
func (plainList__Assembler) AssignInt(int) error {
return mixins.ListAssembler{"list"}.AssignInt(0)
}
func (plainList__Assembler) AssignFloat(float64) error {
return mixins.ListAssembler{"list"}.AssignFloat(0)
}
func (plainList__Assembler) AssignString(string) error {
return mixins.ListAssembler{"list"}.AssignString("")
}
func (plainList__Assembler) AssignBytes([]byte) error {
return mixins.ListAssembler{"list"}.AssignBytes(nil)
}
func (plainList__Assembler) AssignLink(ipld.Link) error {
return mixins.ListAssembler{"list"}.AssignLink(nil)
}
func (na *plainList__Assembler) AssignNode(v ipld.Node) error {
// todo: apply a generic 'copy' function.
// todo: probably can also shortcut to copying na.t and na.m if it's our same concrete type?
// (can't quite just `na.w = v`, because we don't have 'freeze' features, and we don't wanna open door to mutation of 'v'.)
// (wait... actually, probably we can? 'Assign' is a "finish" method. we can&should invalidate the wip pointer here.)
panic("later")
}
func (plainList__Assembler) Style() ipld.NodeStyle {
return Style__List{}
}
// -- ListNodeAssembler -->
// AssembleValue is part of conforming to ListNodeAssembler, which we do on
// plainList__Assembler so that BeginList can just return a retyped pointer rather than new object.
func (la *plainList__Assembler) AssembleValue() ipld.NodeAssembler {
// Sanity check, then update, assembler state.
if la.state != laState_initial {
panic("misuse")
}
la.state = laState_midValue
// Make value assembler valid by giving it pointer back to whole 'la'; yield it.
la.va.la = la
return &la.va
}
// Finish is part of conforming to ListNodeAssembler, which we do on
// plainList__Assembler so that BeginList can just return a retyped pointer rather than new object.
func (la *plainList__Assembler) Finish() error {
// Sanity check, then update, assembler state.
if la.state != laState_initial {
panic("misuse")
}
la.state = laState_finished
// validators could run and report errors promptly, if this type had any.
return nil
}
func (plainList__Assembler) ValueStyle() ipld.NodeStyle { panic("later") }
// -- ListNodeAssembler.ValueAssembler -->
func (lva *plainList__ValueAssembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) {
ma := plainList__ValueAssemblerMap{}
ma.ca.w = &plainMap{}
ma.p = lva.la
_, err := ma.ca.BeginMap(sizeHint)
return &ma, err
}
func (lva *plainList__ValueAssembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) {
la := plainList__ValueAssemblerList{}
la.ca.w = &plainList{}
la.p = lva.la
_, err := la.ca.BeginList(sizeHint)
return &la, err
}
func (lva *plainList__ValueAssembler) AssignNull() error { panic("todo") }
func (lva *plainList__ValueAssembler) AssignBool(bool) error { panic("todo") }
func (lva *plainList__ValueAssembler) AssignInt(v int) error {
vb := plainInt(v)
return lva.AssignNode(&vb)
}
func (lva *plainList__ValueAssembler) AssignFloat(float64) error { panic("todo") }
func (lva *plainList__ValueAssembler) AssignString(v string) error {
vb := plainString(v)
return lva.AssignNode(&vb)
}
func (lva *plainList__ValueAssembler) AssignBytes([]byte) error { panic("todo") }
func (lva *plainList__ValueAssembler) AssignLink(ipld.Link) error { panic("todo") }
func (lva *plainList__ValueAssembler) AssignNode(v ipld.Node) error {
lva.la.w.x = append(lva.la.w.x, v)
lva.la.state = laState_initial
lva.la = nil // invalidate self to prevent further incorrect use.
return nil
}
func (plainList__ValueAssembler) Style() ipld.NodeStyle { panic("later") }
type plainList__ValueAssemblerMap struct {
ca plainMap__Assembler
p *plainList__Assembler // pointer back to parent, for final insert and state bump
}
// we briefly state only the methods we need to delegate here.
// just embedding plainMap__Assembler also behaves correctly,
// but causes a lot of unnecessary autogenerated functions in the final binary.
func (ma *plainList__ValueAssemblerMap) AssembleDirectly(k string) (ipld.NodeAssembler, error) {
return ma.ca.AssembleDirectly(k)
}
func (ma *plainList__ValueAssemblerMap) AssembleKey() ipld.NodeAssembler {
return ma.ca.AssembleKey()
}
func (ma *plainList__ValueAssemblerMap) AssembleValue() ipld.NodeAssembler {
return ma.ca.AssembleValue()
}
func (plainList__ValueAssemblerMap) KeyStyle() ipld.NodeStyle { panic("later") }
func (plainList__ValueAssemblerMap) ValueStyle() ipld.NodeStyle { panic("later") }
func (ma *plainList__ValueAssemblerMap) Finish() error {
if err := ma.ca.Finish(); err != nil {
return err
}
w := ma.ca.w
ma.ca.w = nil
return ma.p.va.AssignNode(w)
}
type plainList__ValueAssemblerList struct {
ca plainList__Assembler
p *plainList__Assembler // pointer back to parent, for final insert and state bump
}
// we briefly state only the methods we need to delegate here.
// just embedding plainList__Assembler also behaves correctly,
// but causes a lot of unnecessary autogenerated functions in the final binary.
func (la *plainList__ValueAssemblerList) AssembleValue() ipld.NodeAssembler {
return la.ca.AssembleValue()
}
func (plainList__ValueAssemblerList) ValueStyle() ipld.NodeStyle { panic("later") }
func (la *plainList__ValueAssemblerList) Finish() error {
if err := la.ca.Finish(); err != nil {
return err
}
w := la.ca.w
la.ca.w = nil
return la.p.va.AssignNode(w)
}
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