diff --git a/.travis.yml b/.travis.yml index fd016470..cb53e5cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ before_script: - go test -run xxxx ./... script: -- go test -race -short -coverprofile=coverage.txt ./... +- go test -short -coverprofile=coverage.txt ./... after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/_rsrch/methodsets/base.go b/_rsrch/methodsets/base.go new file mode 100644 index 00000000..ff849578 --- /dev/null +++ b/_rsrch/methodsets/base.go @@ -0,0 +1,15 @@ +package methodsets + +type Thing struct { + Alpha string + Beta string +} + +func (x *Thing) Pow() { + x.Alpha = "base" +} + +type ThingPrivate struct { + a string + b string +} diff --git a/_rsrch/methodsets/crosspkg/isomorphicCast.go b/_rsrch/methodsets/crosspkg/isomorphicCast.go new file mode 100644 index 00000000..b51f513d --- /dev/null +++ b/_rsrch/methodsets/crosspkg/isomorphicCast.go @@ -0,0 +1,26 @@ +package crosspkg + +import ( + "github.com/ipld/go-ipld-prime/_rsrch/methodsets" +) + +type What struct { + Alpha string + Beta string +} + +func FlipWhat(x *methodsets.Thing) *What { + return (*What)(x) +} + +type WhatPrivate struct { + a string + b string +} + +//func FlipWhatPrivate(x *methodsets.ThingPrivate) *What { +// return (*WhatPrivate)(x) +//} +// NOPE! +// (thank HEAVENS.) +// ./isomorphicCast.go:22:23: cannot convert x (type *methodsets.ThingPrivate) to type *WhatPrivate diff --git a/_rsrch/methodsets/viaAliases.go b/_rsrch/methodsets/viaAliases.go new file mode 100644 index 00000000..68e9b4ee --- /dev/null +++ b/_rsrch/methodsets/viaAliases.go @@ -0,0 +1,28 @@ +package methodsets + +type AliasThing = Thing + +// func (x *AliasThing) Pow() {} +// NOPE! +// ./viaAliases.go:5:22: (*Thing).Pow redeclared in this block +// previous declaration at ./base.go:8:6 + +type AliasPtr = *Thing + +// ^ Oddly, works. + +// func (x *AliasPtr) Pow() {} +// NOPE! +// ./aliases.go:14:6: invalid receiver type **Thing (*Thing is not a defined type) + +// func (x AliasPtr) Pow() {} +// NOPE! +// ./aliases.go:18:19: (*Thing).Pow redeclared in this block +// previous declaration at ./base.go:8:6 + +/* + Conclusion: no joy. + Aliases really are a syntactic sugar thing, and do not seem to enable + any interesting tricks that would not otherwise be possible, + and certainly don't appear to get us closer to the "methodsets" semantic I yearn for. +*/ diff --git a/_rsrch/methodsets/viaTypedef.go b/_rsrch/methodsets/viaTypedef.go new file mode 100644 index 00000000..90999813 --- /dev/null +++ b/_rsrch/methodsets/viaTypedef.go @@ -0,0 +1,15 @@ +package methodsets + +type Thing2ViaTypedef Thing + +// This also compiles and works if you longhand the entire struct defn again... +// as long as it's identical, it works. +// (This does not extend to replacing field types with other-named but structurally identical types.) + +func FlipTypedef(x *Thing) *Thing2ViaTypedef { + return (*Thing2ViaTypedef)(x) +} + +func (x *Thing2ViaTypedef) Pow() { + x.Alpha = "typedef" +} diff --git a/_rsrch/methodsets/viaTypedef_test.go b/_rsrch/methodsets/viaTypedef_test.go new file mode 100644 index 00000000..861c860e --- /dev/null +++ b/_rsrch/methodsets/viaTypedef_test.go @@ -0,0 +1,28 @@ +package methodsets + +import ( + "testing" + + . "github.com/warpfork/go-wish" +) + +func TestViaTypedef(t *testing.T) { + x := Thing{"alpha", "beta"} + + x.Pow() + Wish(t, x, ShouldEqual, Thing{"base", "beta"}) + + x2 := FlipTypedef(&x) + Wish(t, x2, ShouldEqual, &Thing2ViaTypedef{"base", "beta"}) + + x2.Pow() + Wish(t, x2, ShouldEqual, &Thing2ViaTypedef{"typedef", "beta"}) + + Wish(t, x, ShouldEqual, Thing{"typedef", "beta"}) // ! effects propagate back to original. + + x.Pow() + Wish(t, x2, ShouldEqual, &Thing2ViaTypedef{"base", "beta"}) // ! and still also vice versa. + + // it's not just that we care about retaining mutability (though that's sometimes useful); + // it's that a 'yes' to that directly implies 'yes' to "can we get this pov without any allocations". +} diff --git a/_rsrch/methodsets/viaUnsafe.go b/_rsrch/methodsets/viaUnsafe.go new file mode 100644 index 00000000..e14883c3 --- /dev/null +++ b/_rsrch/methodsets/viaUnsafe.go @@ -0,0 +1,22 @@ +package methodsets + +import ( + "unsafe" +) + +type Thing2ViaUnsafe struct { + Alpha string + Beta string +} + +func FlipUnsafe(x *Thing) *Thing2ViaUnsafe { + return (*Thing2ViaUnsafe)(unsafe.Pointer(x)) +} + +func UnflipUnsafe(x *Thing2ViaUnsafe) *Thing { + return (*Thing)(unsafe.Pointer(x)) +} + +func (x *Thing2ViaUnsafe) Pow() { + x.Alpha = "unsafe" +} diff --git a/_rsrch/methodsets/viaUnsafe_test.go b/_rsrch/methodsets/viaUnsafe_test.go new file mode 100644 index 00000000..f0489ee9 --- /dev/null +++ b/_rsrch/methodsets/viaUnsafe_test.go @@ -0,0 +1,28 @@ +package methodsets + +import ( + "testing" + + . "github.com/warpfork/go-wish" +) + +func TestViaUnsafe(t *testing.T) { + x := Thing{"alpha", "beta"} + + x.Pow() + Wish(t, x, ShouldEqual, Thing{"base", "beta"}) + + x2 := FlipUnsafe(&x) + Wish(t, x2, ShouldEqual, &Thing2ViaUnsafe{"base", "beta"}) + + x2.Pow() + Wish(t, x2, ShouldEqual, &Thing2ViaUnsafe{"unsafe", "beta"}) + + Wish(t, x, ShouldEqual, Thing{"unsafe", "beta"}) // ! effects propagate back to original. + + x.Pow() + Wish(t, x2, ShouldEqual, &Thing2ViaUnsafe{"base", "beta"}) // ! and still also vice versa. + + // it's not just that we care about retaining mutability (though that's sometimes useful); + // it's that a 'yes' to that directly implies 'yes' to "can we get this pov without any allocations". +} diff --git a/_rsrch/nozeros/crosspkg/zero_test.go b/_rsrch/nozeros/crosspkg/zero_test.go new file mode 100644 index 00000000..084b2e6f --- /dev/null +++ b/_rsrch/nozeros/crosspkg/zero_test.go @@ -0,0 +1,56 @@ +package crosspkg + +import ( + "testing" + + . "github.com/warpfork/go-wish" + + "github.com/ipld/go-ipld-prime/_rsrch/nozeros" +) + +func TestStuff(t *testing.T) { + _ = nozeros.ExportedAlias{} // undesirable + + // _ = nozeros.ExportedPtr{} // undesirable + // NOPE! (Success!) + // ./zero.go:14:25: invalid pointer type nozeros.ExportedPtr for composite literal (use &nozeros.internal instead) + + v := nozeros.NewExportedPtr("woo") + var typecheckMe interface{} = v + v2, ok := typecheckMe.(nozeros.ExportedPtr) + + Wish(t, ok, ShouldEqual, true) // well, duh. Mostly we wanted to know if even asking was allowed. + Wish(t, v2, ShouldEqual, v) // well, duh. But sanity check, I guess. + + v.Pow() // check that your IDE can autocomplete this, too. it should. + + // this is all the semantics we wanted; awesome. + // + // still unfortunate: Pow won't show up in docs, since it's on an unexported type. + + // exporting an alias of the non-pointer makes a hole in your guarantees, of course: + var hmm nozeros.ExportedPtr + hmm = &nozeros.ExportedAlias{} + _ = hmm +} + +func TestStranger(t *testing.T) { + var foo nozeros.Foo + // foo = &nozeros.FooData{} // undesirable + // NOPE! (Success!) + + foo = nozeros.Foo(nil) // possible, yes. but fairly irrelevant. + _ = foo + + v := nozeros.NewFoo("woo") + + Wish(t, v.Read(), ShouldEqual, "woo") + + v.Pow() + + Wish(t, v.Read(), ShouldEqual, "waht") + + v.Pow() + + t.Logf("%#v", v) // this will log the internal type name, not the exported alias. Arguably not ideal. +} diff --git a/_rsrch/nozeros/nozeros.go b/_rsrch/nozeros/nozeros.go new file mode 100644 index 00000000..8d3abc17 --- /dev/null +++ b/_rsrch/nozeros/nozeros.go @@ -0,0 +1,43 @@ +package nozeros + +type internal struct { + x string +} + +func (x *internal) Pow() {} + +// func (x ExportedPtr) Pow() {} // no, "invalid receiver type ExportedPtr (ExportedPtr is a pointer type)". +// wait, no, this was only illegal for `type ExportedPtr *internal` -- AHHAH + +type ExportedAlias = internal + +type ExportedPtr = *internal + +// type ExportedPtr *internal // ALMOST works... +// except somewhat bizarrely, 'Pow()' becomes undefined and invisible on the exported type. +// As far as I can tell, aside from that, it's identical to using the alias. + +func NewExportedPtr(v string) ExportedPtr { + return &internal{v} +} + +// --- + +type FooData struct { + x string +} + +func (x Foo) Pow() { + x.x = "waht" +} +func (x Foo) Read() string { + return x.x +} + +type foo FooData + +type Foo = *foo + +func NewFoo(v string) Foo { + return &foo{v} +} diff --git a/errors.go b/errors.go index 24511dfd..e468aff6 100644 --- a/errors.go +++ b/errors.go @@ -70,6 +70,78 @@ func (e ErrRepeatedMapKey) Error() string { return fmt.Sprintf("cannot repeat map key (\"%s\")", e.Key) } +// ErrInvalidKey indicates a key is invalid for some reason. +// +// This is only possible for typed nodes; specifically, it may show up when +// handling struct types, or maps with interesting key types. +// (Other kinds of key invalidity that happen for untyped maps +// fall under ErrRepeatedMapKey or ErrWrongKind.) +// (Union types use ErrInvalidUnionDiscriminant instead of ErrInvalidKey, +// even when their representation strategy is maplike.) +type ErrInvalidKey struct { + // TypeName will indicate the named type of a node the function was called on. + TypeName string + + // Key is the key that was rejected. + Key Node + + // Reason, if set, may provide details (for example, the reason a key couldn't be converted to a type). + // If absent, it'll be presumed "no such field". + // ErrUnmatchable may show up as a reason for typed maps with complex keys. + Reason error +} + +func (e ErrInvalidKey) Error() string { + if e.Reason == nil { + return fmt.Sprintf("invalid key for map %s: \"%s\": no such field", e.TypeName, e.Key) + } else { + return fmt.Sprintf("invalid key for map %s: \"%s\": %s", e.TypeName, e.Key, e.Reason) + } +} + +// 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. +// REVIEW: can this ever be triggered during the marshalling direction? perhaps not. +// REVIEW: do things like ErrWrongKind end up being wrapped by this? that doesn't seem pretty. +// REVIEW: do natural representations ever trigger this? i don't think so. maybe that's a hint towards a better name. +// REVIEW: are user validation functions encouraged to return this? or something else? +// +type ErrUnmatchable struct { + // TypeName will indicate the named type of a node the function was called on. + TypeName string + + // Reason must always be present. ErrUnmatchable doesn't say much otherwise. + Reason error +} + +func (e ErrUnmatchable) Error() string { + return fmt.Sprintf("parsing of %s rejected: %s", e.TypeName, e.Reason) +} + // ErrIteratorOverread is returned when calling 'Next' on a MapIterator or // ListIterator when it is already done. type ErrIteratorOverread struct{} @@ -80,7 +152,6 @@ func (e ErrIteratorOverread) Error() string { type ErrCannotBeNull struct{} // Review: arguably either ErrInvalidKindForNodeStyle. -type ErrInvalidStructKey struct{} // only possible for typed nodes -- specifically, struct types. type ErrMissingRequiredField struct{} // only possible for typed nodes -- specifically, struct types. type ErrListOverrun struct{} // only possible for typed nodes -- specifically, struct types with list (aka tuple) representations. type ErrInvalidUnionDiscriminant struct{} // only possible for typed nodes -- specifically, union types. diff --git a/exampleLinking_test.go b/exampleLinking_test.go index 0202ea49..412bf830 100644 --- a/exampleLinking_test.go +++ b/exampleLinking_test.go @@ -113,5 +113,5 @@ func ExampleLoadingLink() { fmt.Printf("we loaded a %s with %d entries\n", n.ReprKind(), n.Length()) // Output: - // we loaded a Map with 1 entries + // we loaded a map with 1 entries } diff --git a/examples_test.go b/examples_test.go index d2be8831..e24d6d51 100644 --- a/examples_test.go +++ b/examples_test.go @@ -42,6 +42,6 @@ func ExampleUnmarshalData() { fmt.Printf("the length of the node is %d\n", n.Length()) // Output: - // the data decoded was a Map kind + // the data decoded was a map kind // the length of the node is 2 } diff --git a/kind.go b/kind.go index 886c2382..9f53ae47 100644 --- a/kind.go +++ b/kind.go @@ -25,25 +25,25 @@ const ( func (k ReprKind) String() string { switch k { case ReprKind_Invalid: - return "Invalid" + return "INVALID" case ReprKind_Map: - return "Map" + return "map" case ReprKind_List: - return "List" + return "list" case ReprKind_Null: - return "Null" + return "null" case ReprKind_Bool: - return "Bool" + return "bool" case ReprKind_Int: - return "Int" + return "int" case ReprKind_Float: - return "Float" + return "float" case ReprKind_String: - return "String" + return "string" case ReprKind_Bytes: - return "Bytes" + return "bytes" case ReprKind_Link: - return "Link" + return "link" default: panic("invalid enumeration value!") } @@ -76,3 +76,12 @@ func (x ReprKindSet) String() string { s += x[len(x)-1].String() return s } + +func (x ReprKindSet) Contains(e ReprKind) bool { + for _, v := range x { + if v == e { + return true + } + } + return false +} diff --git a/module.tl b/module.tl index b4caed74..b074417d 100644 --- a/module.tl +++ b/module.tl @@ -15,7 +15,7 @@ "action": { "exec": [ "/bin/bash", "-c", - "export PATH=$PATH:/app/go/go/bin && export GOPATH=$PWD/.gopath && go test ./..." + "export PATH=$PATH:/app/go/go/bin && export GOPATH=$PWD/.gopath && go test -tags 'skipgenbehavtests' ./..." ] } } diff --git a/node/basic/list.go b/node/basic/list.go index c00875f2..9a61fc4d 100644 --- a/node/basic/list.go +++ b/node/basic/list.go @@ -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) } @@ -182,16 +182,18 @@ func (plainList__Assembler) AssignLink(ipld.Link) error { } func (na *plainList__Assembler) AssignNode(v ipld.Node) error { // Sanity check, then update, assembler state. + // Update of state to 'finished' comes later; where exactly depends on if shortcuts apply. if na.state != laState_initial { panic("misuse") } - na.state = laState_finished // Copy the content. if v2, ok := v.(*plainList); ok { // if our own type: shortcut. // Copy the structure by value. // This means we'll have pointers into the same internal maps and slices; // this is okay, because the Node type promises it's immutable, and we are going to instantly finish ourselves to also maintain that. + // FIXME: the shortcut behaves differently than the long way: it discards any existing progress. Doesn't violate immut, but is odd. *na.w = *v2 + na.state = laState_finished return nil } // If the above shortcut didn't work, resort to a generic copy. @@ -209,8 +211,7 @@ func (na *plainList__Assembler) AssignNode(v ipld.Node) error { return err } } - // validators could run and report errors promptly, if this type had any -- same as for regular Finish. - return nil + return na.Finish() } func (plainList__Assembler) Style() ipld.NodeStyle { return Style__List{} diff --git a/node/basic/map.go b/node/basic/map.go index f6d2ef45..91b39c78 100644 --- a/node/basic/map.go +++ b/node/basic/map.go @@ -197,17 +197,19 @@ func (plainMap__Assembler) AssignLink(ipld.Link) error { return mixins.MapAssembler{"map"}.AssignLink(nil) } func (na *plainMap__Assembler) AssignNode(v ipld.Node) error { - // Sanity check, then update, assembler state. + // Sanity check assembler state. + // Update of state to 'finished' comes later; where exactly depends on if shortcuts apply. if na.state != maState_initial { panic("misuse") } - na.state = maState_finished // Copy the content. if v2, ok := v.(*plainMap); ok { // if our own type: shortcut. // Copy the structure by value. // This means we'll have pointers into the same internal maps and slices; // this is okay, because the Node type promises it's immutable, and we are going to instantly finish ourselves to also maintain that. + // FIXME: the shortcut behaves differently than the long way: it discards any existing progress. Doesn't violate immut, but is odd. *na.w = *v2 + na.state = maState_finished return nil } // If the above shortcut didn't work, resort to a generic copy. @@ -228,8 +230,7 @@ func (na *plainMap__Assembler) AssignNode(v ipld.Node) error { return err } } - // validators could run and report errors promptly, if this type had any -- same as for regular Finish. - return nil + return na.Finish() } func (plainMap__Assembler) Style() ipld.NodeStyle { return Style__Map{} @@ -240,16 +241,17 @@ func (plainMap__Assembler) Style() ipld.NodeStyle { // AssembleEntry is part of conforming to MapAssembler, which we do on // plainMap__Assembler so that BeginMap can just return a retyped pointer rather than new object. func (ma *plainMap__Assembler) AssembleEntry(k string) (ipld.NodeAssembler, error) { - // Sanity check, then update, assembler state. + // Sanity check assembler state. + // Update of state comes after possible key rejection. if ma.state != maState_initial { panic("misuse") } - ma.state = maState_midValue // Check for dup keys; error if so. _, exists := ma.w.m[k] if exists { return nil, ipld.ErrRepeatedMapKey{plainString(k)} } + ma.state = maState_midValue ma.w.t = append(ma.w.t, plainMap__Entry{k: plainString(k)}) // Make value assembler valid by giving it pointer back to whole 'ma'; yield it. ma.va.ma = ma @@ -264,8 +266,6 @@ func (ma *plainMap__Assembler) AssembleKey() ipld.NodeAssembler { panic("misuse") } ma.state = maState_midKey - // Extend entry table. - ma.w.t = append(ma.w.t, plainMap__Entry{}) // Make key assembler valid by giving it pointer back to whole 'ma'; yield it. ma.ka.ma = ma return &ma.ka @@ -324,14 +324,18 @@ func (plainMap__KeyAssembler) AssignFloat(float64) error { } func (mka *plainMap__KeyAssembler) AssignString(v string) error { // Check for dup keys; error if so. + // (And, backtrack state to accepting keys again so we don't get eternally wedged here.) _, exists := mka.ma.w.m[v] if exists { + mka.ma.state = maState_initial + mka.ma = nil // invalidate self to prevent further incorrect use. return ipld.ErrRepeatedMapKey{plainString(v)} } // Assign the key into the end of the entry table; // we'll be doing map insertions after we get the value in hand. // (There's no need to delegate to another assembler for the key type, // because we're just at Data Model level here, which only regards plain strings.) + mka.ma.w.t = append(mka.ma.w.t, plainMap__Entry{}) mka.ma.w.t[len(mka.ma.w.t)-1].k = plainString(v) // Update parent assembler state: clear to proceed. mka.ma.state = maState_expectValue diff --git a/node/gendemo/HACKME.md b/node/gendemo/HACKME.md index 7af2de40..d6ac6fc9 100644 --- a/node/gendemo/HACKME.md +++ b/node/gendemo/HACKME.md @@ -3,9 +3,13 @@ hackme This package demonstrates what codegen output code looks and acts like. -The main purpose is to benchmark things. - -This is a work-in-progress package, full of rough edges. +The main purpose is to benchmark things, +and to provide an easy-to-look-at _thing_ for prospective users +who want to lay eyes on generated code without needing to get up-and-running with the generator themselves. This package is absolutely _not_ full of general purpose node implementations that you should use in _any_ application. + +The input info for the code generation is at the top of the `hax_test.go` file. +(This'll be extracted to be its own schema file, etc, later -- +but you'll have to imagine that part; at present, it's wired directly in code.) diff --git a/node/gendemo/HACKME_abbrevs.md b/node/gendemo/HACKME_abbrevs.md deleted file mode 100644 index 47d0cece..00000000 --- a/node/gendemo/HACKME_abbrevs.md +++ /dev/null @@ -1,17 +0,0 @@ -abbreviations -============= - -- `n` -- **n**ode, of course -- the accessor functions on node implementations usually refer to their 'this' as 'n'. -- `w` -- **w**ork-in-progress node -- you'll see this in nearly every assembler. -- `ca` -- **c**hild **a**ssembler -- the thing embedded in key assemblers and value assemblers in recursive kinds. - -inside nodes: - -- `x` -- a placeholder for "the thing" for types that contain only one element of data (e.g., the string inside a codegen'd node of string kind). -- `t` -- **t**able -- the slice inside most map nodes that is used for alloc amortizations and maintaining order. -- `m` -- **m**ap -- the actual map inside most map nodes (seealso `t`, which is usually a sibling). - -deprecated abbrevs: - -- `ta` -- **t**yped **a**ssembler -- but this should probably subjected to `s/ta/na/g`; it's a weird distinction to make. - - maybe worth keeping just so we can have `ra` for reprassembler. diff --git a/node/gendemo/hax_test.go b/node/gendemo/hax_test.go new file mode 100644 index 00000000..2f166c9b --- /dev/null +++ b/node/gendemo/hax_test.go @@ -0,0 +1,64 @@ +package gendemo + +import ( + "os/exec" + "testing" + + "github.com/ipld/go-ipld-prime/node/tests" + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go" +) + +// i am the worst person and this is the worst code +// but it does do codegen when you test this package! +// (it's also legitimately trash tho, because if you get a compile error, you have to manually rm the relevant files, which is not fun.) +func init() { + pkgName := "gendemo" + ts := schema.TypeSystem{} + ts.Init() + adjCfg := &gengo.AdjunctCfg{} + ts.Accumulate(schema.SpawnInt("Int")) + ts.Accumulate(schema.SpawnString("String")) + ts.Accumulate(schema.SpawnStruct("Msg3", + []schema.StructField{ + schema.SpawnStructField("whee", ts.TypeByName("Int"), false, false), + schema.SpawnStructField("woot", ts.TypeByName("Int"), false, false), + schema.SpawnStructField("waga", ts.TypeByName("Int"), false, false), + }, + schema.SpawnStructRepresentationMap(nil), + )) + ts.Accumulate(schema.SpawnMap("Map__String__Msg3", + ts.TypeByName("String"), ts.TypeByName("Msg3"), false)) + gengo.Generate(".", pkgName, ts, adjCfg) + exec.Command("go", "fmt").Run() +} + +func BenchmarkMapStrInt_3n_AssembleStandard(b *testing.B) { + tests.SpecBenchmarkMapStrInt_3n_AssembleStandard(b, _Msg3__Style{}) +} +func BenchmarkMapStrInt_3n_AssembleEntry(b *testing.B) { + tests.SpecBenchmarkMapStrInt_3n_AssembleEntry(b, _Msg3__Style{}) +} +func BenchmarkMapStrInt_3n_Iteration(b *testing.B) { + tests.SpecBenchmarkMapStrInt_3n_Iteration(b, _Msg3__Style{}) +} +func BenchmarkSpec_Marshal_Map3StrInt(b *testing.B) { + tests.BenchmarkSpec_Marshal_Map3StrInt(b, _Msg3__Style{}) +} +func BenchmarkSpec_Marshal_Map3StrInt_CodecNull(b *testing.B) { + tests.BenchmarkSpec_Marshal_Map3StrInt_CodecNull(b, _Msg3__Style{}) +} +func BenchmarkSpec_Unmarshal_Map3StrInt(b *testing.B) { + tests.BenchmarkSpec_Unmarshal_Map3StrInt(b, _Msg3__Style{}) +} + +func BenchmarkSpec_Marshal_MapNStrMap3StrInt(b *testing.B) { + tests.BenchmarkSpec_Marshal_MapNStrMap3StrInt(b, _Map__String__Msg3__Style{}) +} +func BenchmarkSpec_Unmarshal_MapNStrMap3StrInt(b *testing.B) { + tests.BenchmarkSpec_Unmarshal_MapNStrMap3StrInt(b, _Map__String__Msg3__Style{}) +} + +// the standard 'walk' benchmarks don't work yet because those use selectors and use the style we give them for that, which... +// does not fly: cramming selector keys into assemblers meant for struct types from our test corpus? nope. +// this is a known shortcut-become-bug with the design of the 'walk' benchmarks; we'll have to fix soon. diff --git a/node/gendemo/int.go b/node/gendemo/int.go deleted file mode 100644 index db6ea4a9..00000000 --- a/node/gendemo/int.go +++ /dev/null @@ -1,139 +0,0 @@ -package gendemo - -import ( - ipld "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/node/mixins" -) - -var ( - _ ipld.Node = plainInt(0) - _ ipld.NodeStyle = Style__Int{} - _ ipld.NodeBuilder = &plainInt__Builder{} - _ ipld.NodeAssembler = &plainInt__Assembler{} -) - -// plainInt is a simple boxed int that complies with ipld.Node. -type plainInt int - -// -- Node interface methods --> - -func (plainInt) ReprKind() ipld.ReprKind { - return ipld.ReprKind_Int -} -func (plainInt) LookupString(string) (ipld.Node, error) { - return mixins.Int{"gendemo.Int"}.LookupString("") -} -func (plainInt) Lookup(key ipld.Node) (ipld.Node, error) { - return mixins.Int{"gendemo.Int"}.Lookup(nil) -} -func (plainInt) LookupIndex(idx int) (ipld.Node, error) { - return mixins.Int{"gendemo.Int"}.LookupIndex(0) -} -func (plainInt) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - return mixins.Int{"gendemo.Int"}.LookupSegment(seg) -} -func (plainInt) MapIterator() ipld.MapIterator { - return nil -} -func (plainInt) ListIterator() ipld.ListIterator { - return nil -} -func (plainInt) Length() int { - return -1 -} -func (plainInt) IsUndefined() bool { - return false -} -func (plainInt) IsNull() bool { - return false -} -func (plainInt) AsBool() (bool, error) { - return mixins.Int{"gendemo.Int"}.AsBool() -} -func (n plainInt) AsInt() (int, error) { - return int(n), nil -} -func (plainInt) AsFloat() (float64, error) { - return mixins.Int{"gendemo.Int"}.AsFloat() -} -func (plainInt) AsString() (string, error) { - return mixins.Int{"gendemo.Int"}.AsString() -} -func (plainInt) AsBytes() ([]byte, error) { - return mixins.Int{"gendemo.Int"}.AsBytes() -} -func (plainInt) AsLink() (ipld.Link, error) { - return mixins.Int{"gendemo.Int"}.AsLink() -} -func (plainInt) Style() ipld.NodeStyle { - return Style__Int{} -} - -// -- NodeStyle --> - -type Style__Int struct{} - -func (Style__Int) NewBuilder() ipld.NodeBuilder { - var w plainInt - return &plainInt__Builder{plainInt__Assembler{w: &w}} -} - -// -- NodeBuilder --> - -type plainInt__Builder struct { - plainInt__Assembler -} - -func (nb *plainInt__Builder) Build() ipld.Node { - return nb.w -} -func (nb *plainInt__Builder) Reset() { - var w plainInt - *nb = plainInt__Builder{plainInt__Assembler{w: &w}} -} - -// -- NodeAssembler --> - -type plainInt__Assembler struct { - w *plainInt -} - -func (plainInt__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { - return mixins.IntAssembler{"gendemo.Int"}.BeginMap(0) -} -func (plainInt__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { - return mixins.IntAssembler{"gendemo.Int"}.BeginList(0) -} -func (plainInt__Assembler) AssignNull() error { - return mixins.IntAssembler{"gendemo.Int"}.AssignNull() -} -func (plainInt__Assembler) AssignBool(bool) error { - return mixins.IntAssembler{"gendemo.Int"}.AssignBool(false) -} -func (na *plainInt__Assembler) AssignInt(v int) error { - *na.w = plainInt(v) - return nil -} -func (plainInt__Assembler) AssignFloat(float64) error { - return mixins.IntAssembler{"gendemo.Int"}.AssignFloat(0) -} -func (plainInt__Assembler) AssignString(string) error { - return mixins.IntAssembler{"gendemo.Int"}.AssignString("") -} -func (plainInt__Assembler) AssignBytes([]byte) error { - return mixins.IntAssembler{"gendemo.Int"}.AssignBytes(nil) -} -func (plainInt__Assembler) AssignLink(ipld.Link) error { - return mixins.IntAssembler{"gendemo.Int"}.AssignLink(nil) -} -func (na *plainInt__Assembler) AssignNode(v ipld.Node) error { - if v2, err := v.AsInt(); err != nil { - return err - } else { - *na.w = plainInt(v2) - return nil - } -} -func (plainInt__Assembler) Style() ipld.NodeStyle { - return Style__Int{} -} diff --git a/node/gendemo/map_K2_T2.go b/node/gendemo/map_K2_T2.go deleted file mode 100644 index 7f0d83a0..00000000 --- a/node/gendemo/map_K2_T2.go +++ /dev/null @@ -1,382 +0,0 @@ -package gendemo - -// Map_K2_T2 and this file is how a codegen'd map type would work. it's allowed to use concrete key and value types. -// In constrast with Map_K_T, this one has both complex keys and a struct for the value. - -import ( - "fmt" - - ipld "github.com/ipld/go-ipld-prime" -) - -// --- we need some types to use for keys and values: ---> -/* ipldsch: - type K2 struct { u string, i string } representation stringjoin (":") - type T2 struct { a int, b int, c int, d int } -*/ - -type K2 struct{ u, i plainString } -type T2 struct{ a, b, c, d plainInt } - -func (K2) ReprKind() ipld.ReprKind { - return ipld.ReprKind_Map -} -func (n *K2) LookupString(key string) (ipld.Node, error) { - switch key { - case "u": - return &n.u, nil - case "i": - return &n.i, nil - default: - return nil, fmt.Errorf("no such field") - } -} -func (n *K2) Lookup(key ipld.Node) (ipld.Node, error) { - ks, err := key.AsString() - if err != nil { - return nil, err - } - return n.LookupString(ks) -} -func (K2) LookupIndex(idx int) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "K2", MethodName: "LookupIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Map} -} -func (n *K2) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - return n.LookupString(seg.String()) -} -func (n *K2) MapIterator() ipld.MapIterator { - return &_K2_MapIterator{n, 0} -} -func (K2) ListIterator() ipld.ListIterator { - return nil -} -func (K2) Length() int { - return -1 -} -func (K2) IsUndefined() bool { - return false -} -func (K2) IsNull() bool { - return false -} -func (K2) AsBool() (bool, error) { - return false, ipld.ErrWrongKind{TypeName: "K2", MethodName: "AsBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: ipld.ReprKind_Map} -} -func (K2) AsInt() (int, error) { - return 0, ipld.ErrWrongKind{TypeName: "K2", MethodName: "AsInt", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_Map} -} -func (K2) AsFloat() (float64, error) { - return 0, ipld.ErrWrongKind{TypeName: "K2", MethodName: "AsFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_Map} -} -func (K2) AsString() (string, error) { - return "", ipld.ErrWrongKind{TypeName: "K2", MethodName: "AsString", AppropriateKind: ipld.ReprKindSet_JustString, ActualKind: ipld.ReprKind_Map} -} -func (K2) AsBytes() ([]byte, error) { - return nil, ipld.ErrWrongKind{TypeName: "K2", MethodName: "AsBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: ipld.ReprKind_Map} -} -func (K2) AsLink() (ipld.Link, error) { - return nil, ipld.ErrWrongKind{TypeName: "K2", MethodName: "AsLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: ipld.ReprKind_Map} -} -func (K2) Style() ipld.NodeStyle { - panic("todo") -} - -type _K2_MapIterator struct { - n *K2 - idx int -} - -func (itr *_K2_MapIterator) Next() (k ipld.Node, v ipld.Node, _ error) { - if itr.idx >= 2 { - return nil, nil, ipld.ErrIteratorOverread{} - } - switch itr.idx { - case 0: - k = plainString("u") // TODO: I guess we should generate const pools for struct field names? - v = &itr.n.u - case 1: - k = plainString("i") - v = &itr.n.i - default: - panic("unreachable") - } - itr.idx++ - return -} -func (itr *_K2_MapIterator) Done() bool { - return itr.idx >= 2 -} - -// maState is an enum of the state machine for a map assembler. -// (this might be something to export reusably, but it's also very much an impl detail that need not be seen, so, dubious.) -type maState uint8 - -const ( - maState_initial maState = iota // also the 'expect key or finish' state - maState_midKey // waiting for a 'finished' state in the KeyAssembler. - maState_expectValue // 'AssembleValue' is the only valid next step - maState_midValue // waiting for a 'finished' state in the ValueAssembler. - maState_finished // 'w' will also be nil, but this is a politer statement -) - -type _K2__Assembler struct { - w *K2 - - state maState - - isset_u bool - isset_i bool -} -type _K2__ReprAssembler struct { - w *K2 - - // note how this is totally different than the type-level assembler -- that's map-like, this is string. -} - -func (ta *_K2__Assembler) BeginMap(_ int) (ipld.MapAssembler, error) { panic("no") } -func (_K2__Assembler) BeginList(_ int) (ipld.ListAssembler, error) { panic("no") } -func (_K2__Assembler) AssignNull() error { panic("no") } -func (_K2__Assembler) AssignBool(bool) error { panic("no") } -func (_K2__Assembler) AssignInt(v int) error { panic("no") } -func (_K2__Assembler) AssignFloat(float64) error { panic("no") } -func (_K2__Assembler) AssignString(v string) error { panic("no") } -func (_K2__Assembler) AssignBytes([]byte) error { panic("no") } -func (ta *_K2__Assembler) AssignNode(v ipld.Node) error { - if v2, ok := v.(*K2); ok { - *ta.w = *v2 - return nil - } - panic("todo implement generic copy and use it here") -} -func (_K2__Assembler) Style() ipld.NodeStyle { panic("later") } - -func (ma *_K2__Assembler) AssembleEntry(k string) (ipld.NodeAssembler, error) { - // Sanity check, then update, assembler state. - if ma.state != maState_initial { - panic("misuse") - } - ma.state = maState_midValue - // Figure out which field we're addressing, - // check if it's already been assigned (error if so), - // grab a pointer to it and init its value assembler with that, - // and yield that value assembler. - // (Note that `isset_foo` bools may be inside the 'ma.w' node if - // that field is optional; if it's required, they stay in 'ma'.) - switch k { - case "u": - if ma.isset_u { - return nil, ipld.ErrRepeatedMapKey{plainString("u")} // REVIEW: interesting to note this is a place we *keep* needing a basic string node impl, *everywhere*. - } - // TODO initialize the field child assembler 'w' *and* 'finish' callback to us; return it. - panic("todo") - case "i": - // TODO same as above - panic("todo") - default: - panic("invalid field key") - } -} - -func (ma *_K2__Assembler) AssembleKey() ipld.NodeAssembler { - // Sanity check, then update, assembler state. - if ma.state != maState_initial { - panic("misuse") - } - ma.state = maState_midKey - // TODO return a fairly dummy assembler which just contains a string switch (probably sharing code with AssembleEntry). - panic("todo") -} -func (ma *_K2__Assembler) AssembleValue() ipld.NodeAssembler { - // Sanity check, then update, assembler state. - if ma.state != maState_expectValue { - panic("misuse") - } - ma.state = maState_midValue - // TODO initialize the field child assembler 'w' *and* 'finish' callback to us; return it. - panic("todo") -} -func (ma *_K2__Assembler) Finish() error { - // Sanity check assembler state. - if ma.state != maState_initial { - panic("misuse") - } - ma.state = maState_finished - // validators could run and report errors promptly, if this type had any. - return nil -} -func (_K2__Assembler) KeyStyle() ipld.NodeStyle { panic("later") } -func (_K2__Assembler) ValueStyle(k string) ipld.NodeStyle { panic("later") } - -func (T2) ReprKind() ipld.ReprKind { - return ipld.ReprKind_Map -} -func (n *T2) LookupString(key string) (ipld.Node, error) { - switch key { - case "a": - return &n.a, nil - case "b": - return &n.b, nil - case "c": - return &n.c, nil - case "d": - return &n.d, nil - default: - return nil, fmt.Errorf("no such field") - } -} -func (n *T2) Lookup(key ipld.Node) (ipld.Node, error) { - ks, err := key.AsString() - if err != nil { - return nil, err - } - return n.LookupString(ks) -} -func (T2) LookupIndex(idx int) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "T2", MethodName: "LookupIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Map} -} -func (n *T2) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - return n.LookupString(seg.String()) -} -func (n *T2) MapIterator() ipld.MapIterator { - return &_T2_MapIterator{n, 0} -} -func (T2) ListIterator() ipld.ListIterator { - return nil -} -func (T2) Length() int { - return -1 -} -func (T2) IsUndefined() bool { - return false -} -func (T2) IsNull() bool { - return false -} -func (T2) AsBool() (bool, error) { - return false, ipld.ErrWrongKind{TypeName: "T2", MethodName: "AsBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: ipld.ReprKind_Map} -} -func (T2) AsInt() (int, error) { - return 0, ipld.ErrWrongKind{TypeName: "T2", MethodName: "AsInt", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_Map} -} -func (T2) AsFloat() (float64, error) { - return 0, ipld.ErrWrongKind{TypeName: "T2", MethodName: "AsFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_Map} -} -func (T2) AsString() (string, error) { - return "", ipld.ErrWrongKind{TypeName: "T2", MethodName: "AsString", AppropriateKind: ipld.ReprKindSet_JustString, ActualKind: ipld.ReprKind_Map} -} -func (T2) AsBytes() ([]byte, error) { - return nil, ipld.ErrWrongKind{TypeName: "T2", MethodName: "AsBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: ipld.ReprKind_Map} -} -func (T2) AsLink() (ipld.Link, error) { - return nil, ipld.ErrWrongKind{TypeName: "T2", MethodName: "AsLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: ipld.ReprKind_Map} -} -func (T2) Style() ipld.NodeStyle { - panic("todo") -} - -type _T2_MapIterator struct { - n *T2 - idx int -} - -func (itr *_T2_MapIterator) Next() (k ipld.Node, v ipld.Node, _ error) { - if itr.idx >= 4 { - return nil, nil, ipld.ErrIteratorOverread{} - } - switch itr.idx { - case 0: - k = plainString("a") // TODO: I guess we should generate const pools for struct field names? - v = &itr.n.a - case 1: - k = plainString("b") - v = &itr.n.b - case 2: - k = plainString("c") - v = &itr.n.c - case 3: - k = plainString("d") - v = &itr.n.d - default: - panic("unreachable") - } - itr.idx++ - return -} -func (itr *_T2_MapIterator) Done() bool { - return itr.idx >= 4 -} - -type _T2__Assembler struct { - w *T2 -} -type _T2__ReprAssembler struct { - w *T2 -} - -func (ta *_T2__Assembler) BeginMap(_ int) (ipld.MapAssembler, error) { - return ta, nil -} -func (_T2__Assembler) BeginList(_ int) (ipld.ListAssembler, error) { panic("no") } -func (_T2__Assembler) AssignNull() error { panic("no") } -func (_T2__Assembler) AssignBool(bool) error { panic("no") } -func (_T2__Assembler) AssignInt(int) error { panic("no") } -func (_T2__Assembler) AssignFloat(float64) error { panic("no") } -func (_T2__Assembler) AssignString(v string) error { panic("no") } -func (_T2__Assembler) AssignBytes([]byte) error { panic("no") } -func (ta *_T2__Assembler) AssignNode(v ipld.Node) error { - if v2, ok := v.(*T2); ok { - *ta.w = *v2 - return nil - } - // todo: apply a generic 'copy' function. - panic("later") -} -func (_T2__Assembler) Style() ipld.NodeStyle { panic("later") } - -func (ta *_T2__Assembler) AssembleEntry(string) (ipld.NodeAssembler, error) { - // this'll be fun - panic("soon") -} -func (ta *_T2__Assembler) AssembleKey() ipld.NodeAssembler { - // this'll be fun - panic("soon") -} -func (ta *_T2__Assembler) AssembleValue() ipld.NodeAssembler { - // also fun - panic("soon") -} -func (ta *_T2__Assembler) Finish() error { - panic("soon") -} -func (_T2__Assembler) KeyStyle() ipld.NodeStyle { panic("later") } -func (_T2__Assembler) ValueStyle(k string) ipld.NodeStyle { panic("later") } - -// --- okay, now the type of interest: the map. ---> -/* ipldsch: - type Root struct { mp {K2:T2} } # nevermind the root part, the anonymous map is the point. -*/ - -type Map_K2_T2 struct { - m map[K2]*T2 // used for quick lookup. - t []_Map_K2_T2__entry // used both for order maintainence, and for allocation amortization for both keys and values. -} - -type _Map_K2_T2__entry struct { - k K2 // address of this used when we return keys as nodes, such as in iterators. Need in one place to amortize shifts to heap when ptr'ing for iface. - v T2 // address of this is used in map values and to return. -} - -func (n *Map_K2_T2) LookupString(key string) (ipld.Node, error) { - panic("decision") // FIXME: What's this supposed to do? does this error for maps with complex keys? -} - -type _Map_K2_T2__Assembler struct { - w *Map_K2_T2 - ka _K2__Assembler - va _T2__Assembler -} -type _Map_K2_T2__ReprAssembler struct { - w *Map_K2_T2 - ka _K2__ReprAssembler - va _T2__ReprAssembler -} diff --git a/node/gendemo/map_K_T.go b/node/gendemo/map_K_T.go deleted file mode 100644 index 0b1f1c17..00000000 --- a/node/gendemo/map_K_T.go +++ /dev/null @@ -1,512 +0,0 @@ -package gendemo - -// Map_K_T and this file is how a codegen'd map type would work. it's allowed to use concrete key and value types. - -import ( - "fmt" - - ipld "github.com/ipld/go-ipld-prime" -) - -// --- we need some types to use for keys and values: ---> -/* ipldsch: - type K string - type T int -*/ - -type K struct{ x string } -type T struct{ x int } - -func (K) ReprKind() ipld.ReprKind { - return ipld.ReprKind_String -} -func (K) LookupString(string) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "LookupString", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String} -} -func (K) Lookup(key ipld.Node) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "Lookup", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String} -} -func (K) LookupIndex(idx int) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "LookupIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_String} -} -func (K) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "LookupSegment", AppropriateKind: ipld.ReprKindSet_Recursive, ActualKind: ipld.ReprKind_String} -} -func (K) MapIterator() ipld.MapIterator { - return nil -} -func (K) ListIterator() ipld.ListIterator { - return nil -} -func (K) Length() int { - return -1 -} -func (K) IsUndefined() bool { - return false -} -func (K) IsNull() bool { - return false -} -func (K) AsBool() (bool, error) { - return false, ipld.ErrWrongKind{MethodName: "AsBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: ipld.ReprKind_String} -} -func (K) AsInt() (int, error) { - return 0, ipld.ErrWrongKind{MethodName: "AsInt", AppropriateKind: ipld.ReprKindSet_JustInt, ActualKind: ipld.ReprKind_String} -} -func (K) AsFloat() (float64, error) { - return 0, ipld.ErrWrongKind{MethodName: "AsFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_String} -} -func (n *K) AsString() (string, error) { - return n.x, nil -} -func (K) AsBytes() ([]byte, error) { - return nil, ipld.ErrWrongKind{MethodName: "AsBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: ipld.ReprKind_String} -} -func (K) AsLink() (ipld.Link, error) { - return nil, ipld.ErrWrongKind{MethodName: "AsLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: ipld.ReprKind_String} -} -func (K) Style() ipld.NodeStyle { - panic("todo") -} - -func (T) ReprKind() ipld.ReprKind { - return ipld.ReprKind_String -} -func (T) LookupString(string) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "LookupString", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_Int} -} -func (T) Lookup(key ipld.Node) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "Lookup", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_Int} -} -func (T) LookupIndex(idx int) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "LookupIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Int} -} -func (T) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "LookupSegment", AppropriateKind: ipld.ReprKindSet_Recursive, ActualKind: ipld.ReprKind_Int} -} -func (T) MapIterator() ipld.MapIterator { - return nil -} -func (T) ListIterator() ipld.ListIterator { - return nil -} -func (T) Length() int { - return -1 -} -func (T) IsUndefined() bool { - return false -} -func (T) IsNull() bool { - return false -} -func (T) AsBool() (bool, error) { - return false, ipld.ErrWrongKind{MethodName: "AsBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: ipld.ReprKind_Int} -} -func (n *T) AsInt() (int, error) { - return n.x, nil -} -func (T) AsFloat() (float64, error) { - return 0, ipld.ErrWrongKind{MethodName: "AsFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_Int} -} -func (T) AsString() (string, error) { - return "", ipld.ErrWrongKind{MethodName: "AsString", AppropriateKind: ipld.ReprKindSet_JustString, ActualKind: ipld.ReprKind_Int} -} -func (T) AsBytes() ([]byte, error) { - return nil, ipld.ErrWrongKind{MethodName: "AsBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: ipld.ReprKind_Int} -} -func (T) AsLink() (ipld.Link, error) { - return nil, ipld.ErrWrongKind{MethodName: "AsLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: ipld.ReprKind_Int} -} -func (T) Style() ipld.NodeStyle { - panic("todo") -} - -type _K__Assembler struct { - w *K -} -type _K__ReprAssembler struct { - w *K -} - -func (_K__Assembler) BeginMap(_ int) (ipld.MapAssembler, error) { panic("no") } -func (_K__Assembler) BeginList(_ int) (ipld.ListAssembler, error) { panic("no") } -func (_K__Assembler) AssignNull() error { panic("no") } -func (_K__Assembler) AssignBool(bool) error { panic("no") } -func (_K__Assembler) AssignInt(v int) error { panic("no") } -func (_K__Assembler) AssignFloat(float64) error { panic("no") } -func (ta *_K__Assembler) AssignString(v string) error { - ta.w.x = v - return nil -} -func (_K__Assembler) AssignBytes([]byte) error { panic("no") } -func (ta *_K__Assembler) AssignNode(v ipld.Node) error { - if v2, ok := v.(*K); ok { - *ta.w = *v2 - return nil - } - v2, err := v.AsString() - if err != nil { - return err // TODO:errors: probably deserves a layer of decoration being more explicit about invalid assignment. - } - return ta.AssignString(v2) -} -func (_K__Assembler) Style() ipld.NodeStyle { panic("later") } - -type _T__Assembler struct { - w *T -} -type _T__ReprAssembler struct { - w *T -} - -func (_T__Assembler) BeginMap(_ int) (ipld.MapAssembler, error) { panic("no") } -func (_T__Assembler) BeginList(_ int) (ipld.ListAssembler, error) { panic("no") } -func (_T__Assembler) AssignNull() error { panic("no") } -func (_T__Assembler) AssignBool(bool) error { panic("no") } -func (ta *_T__Assembler) AssignInt(v int) error { - ta.w.x = v - return nil -} -func (_T__Assembler) AssignFloat(float64) error { panic("no") } -func (_T__Assembler) AssignString(v string) error { panic("no") } -func (_T__Assembler) AssignBytes([]byte) error { panic("no") } -func (ta *_T__Assembler) AssignNode(v ipld.Node) error { - if v2, ok := v.(*T); ok { - *ta.w = *v2 - return nil - } - v2, err := v.AsInt() - if err != nil { - return err // TODO:errors: probably deserves a layer of decoration being more explicit about invalid assignment. - } - return ta.AssignInt(v2) -} -func (_T__Assembler) Style() ipld.NodeStyle { panic("later") } - -// --- okay, now the type of interest: the map. ---> -/* ipldsch: - type Root struct { mp {K:T} } # nevermind the root part, the anonymous map is the point. -*/ - -type Map_K_T struct { - m map[K]*T // used for quick lookup. - t []_Map_K_T__entry // used both for order maintainence, and for allocation amortization for both keys and values. -} - -type _Map_K_T__entry struct { - k K // address of this used when we return keys as nodes, such as in iterators. Need in one place to amortize shifts to heap when ptr'ing for iface. - v T // address of this is used in map values and to return. -} - -func (Map_K_T) ReprKind() ipld.ReprKind { - return ipld.ReprKind_Map -} -func (n *Map_K_T) Get(key *K) (*T, error) { - v, exists := n.m[*key] - if !exists { - return nil, ipld.ErrNotExists{ipld.PathSegmentOfString(key.x)} - } - return v, nil -} -func (n *Map_K_T) LookupString(key string) (ipld.Node, error) { - v, exists := n.m[K{key}] - if !exists { - return nil, ipld.ErrNotExists{ipld.PathSegmentOfString(key)} - } - return v, nil -} -func (n *Map_K_T) Lookup(key ipld.Node) (ipld.Node, error) { - if k2, ok := key.(*K); ok { - return n.Get(k2) - } - ks, err := key.AsString() - if err != nil { - return nil, err - } - return n.LookupString(ks) -} -func (Map_K_T) LookupIndex(idx int) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "Map_K_T", MethodName: "LookupIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Map} -} -func (n *Map_K_T) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - return n.LookupString(seg.String()) -} -func (n *Map_K_T) MapIterator() ipld.MapIterator { - return &_Map_K_T_MapIterator{n, 0} -} -func (Map_K_T) ListIterator() ipld.ListIterator { - return nil -} -func (n *Map_K_T) Length() int { - return len(n.t) -} -func (Map_K_T) IsUndefined() bool { - return false -} -func (Map_K_T) IsNull() bool { - return false -} -func (Map_K_T) AsBool() (bool, error) { - return false, ipld.ErrWrongKind{TypeName: "Map_K_T", MethodName: "AsBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: ipld.ReprKind_Map} -} -func (Map_K_T) AsInt() (int, error) { - return 0, ipld.ErrWrongKind{TypeName: "Map_K_T", MethodName: "AsInt", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_Map} -} -func (Map_K_T) AsFloat() (float64, error) { - return 0, ipld.ErrWrongKind{TypeName: "Map_K_T", MethodName: "AsFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_Map} -} -func (Map_K_T) AsString() (string, error) { - return "", ipld.ErrWrongKind{TypeName: "Map_K_T", MethodName: "AsString", AppropriateKind: ipld.ReprKindSet_JustString, ActualKind: ipld.ReprKind_Map} -} -func (Map_K_T) AsBytes() ([]byte, error) { - return nil, ipld.ErrWrongKind{TypeName: "Map_K_T", MethodName: "AsBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: ipld.ReprKind_Map} -} -func (Map_K_T) AsLink() (ipld.Link, error) { - return nil, ipld.ErrWrongKind{TypeName: "Map_K_T", MethodName: "AsLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: ipld.ReprKind_Map} -} -func (Map_K_T) Style() ipld.NodeStyle { - return Type__Map_K_T{} -} - -type _Map_K_T_MapIterator struct { - n *Map_K_T - idx int -} - -func (itr *_Map_K_T_MapIterator) Next() (k ipld.Node, v ipld.Node, _ error) { - if itr.Done() { - return nil, nil, ipld.ErrIteratorOverread{} - } - k = &itr.n.t[itr.idx].k - v = &itr.n.t[itr.idx].v - itr.idx++ - return -} -func (itr *_Map_K_T_MapIterator) Done() bool { - return itr.idx >= len(itr.n.t) -} - -// Type__Map_K_T implements both schema.Type and ipld.NodeStyle. -// -// REVIEW: Should this just be exported? I think probably yes. -// Alternatives: `Types().Map_K_T().NewBuilder()`; or, `Types` as a large const? -type Type__Map_K_T struct{} - -func (Type__Map_K_T) NewBuilder() ipld.NodeBuilder { - return &_Map_K_T__Builder{_Map_K_T__Assembler{ - w: &Map_K_T{}, - }} -} - -// Overall assembly flow: -// - ('w' must already be set before beginning.) -// - BeginMap -- initializes the contents of the node in 'w'. -// - AssembleKey -- extends 'w.t', and sets up 'ka.ca.w' to point to the 'k' in the very tail of 'w.t'. -// - !branch: -// - AssignString -- delegates to _K__Assembler (which may run validations); then checks for repeated key, errors if so; in case of either of those errors, un-extends 'w.t'. -// - Assign -- more or less does one of the other two, above or below. -// - BeginMap -- doesn't apply in this case (key is not complex), but if it was/did... -// - Finish -- is implemented on _Map_K_T__KeyAssembler and delegates to _K__Assembler, because must do the repeated key check. -// - (okay, the key is now confirmed. but, keep in mind: we still might need to back out if the value assignment errors.) -// - AssembleValue -- sets up 'va.ca.w' to point to the 'v' in the very tail of 'w.t'. -// - ... -// -// (Yep, basically any path through key *or* value assembly may error, and if they do, -// the parent has to roll back the last entry in 'w.t' -- so everything has a wrapper.) -type _Map_K_T__Assembler struct { - w *Map_K_T - ka _Map_K_T__KeyAssembler - va _Map_K_T__ValueAssembler - - state maState -} -type _Map_K_T__Builder struct { - _Map_K_T__Assembler -} -type _Map_K_T__KeyAssembler struct { - ma *_Map_K_T__Assembler // annoyingly cyclic but needed to do dupkey checks. - ca _K__Assembler -} -type _Map_K_T__ValueAssembler struct { - ma *_Map_K_T__Assembler // annoyingly cyclic but needed to reset the midappend state. - ca _T__Assembler -} - -func (nb *_Map_K_T__Builder) Build() ipld.Node { - result := nb.w - nb.w = nil - return result -} -func (nb *_Map_K_T__Builder) Reset() { - *nb = _Map_K_T__Builder{} - nb.w = &Map_K_T{} -} - -func (na *_Map_K_T__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { - // Allocate storage space. - na.w.t = make([]_Map_K_T__entry, 0, sizeHint) - na.w.m = make(map[K]*T, sizeHint) - // Initialize the key and value assemblers with pointers back to the whole. - na.ka.ma = na - na.va.ma = na - // That's it; return self as the MapAssembler. We already have all the right methods on this structure. - return na, nil -} -func (_Map_K_T__Assembler) BeginList(_ int) (ipld.ListAssembler, error) { panic("no") } -func (_Map_K_T__Assembler) AssignNull() error { panic("no") } -func (_Map_K_T__Assembler) AssignBool(bool) error { panic("no") } -func (_Map_K_T__Assembler) AssignInt(v int) error { panic("no") } -func (_Map_K_T__Assembler) AssignFloat(float64) error { panic("no") } -func (_Map_K_T__Assembler) AssignString(v string) error { panic("no") } -func (_Map_K_T__Assembler) AssignBytes([]byte) error { panic("no") } -func (_Map_K_T__Assembler) AssignLink(ipld.Link) error { panic("no") } -func (ta *_Map_K_T__Assembler) AssignNode(v ipld.Node) error { - if v2, ok := v.(*Map_K_T); ok { - *ta.w = *v2 - ta.w = nil // block further mutation - return nil - } - // todo: apply a generic 'copy' function. - panic("later") -} -func (_Map_K_T__Assembler) Style() ipld.NodeStyle { panic("later") } - -func (ma *_Map_K_T__Assembler) AssembleEntry(k string) (ipld.NodeAssembler, error) { - // Sanity check, then update, assembler state. - if ma.state != maState_initial { - panic("misuse") - } - ma.state = maState_midValue - // Check for dup keys; error if so. - _, exists := ma.w.m[K{k}] - if exists { - return nil, ipld.ErrRepeatedMapKey{&K{k}} - } - // Extend entry table and update map to point into the new row. - l := len(ma.w.t) - ma.w.t = append(ma.w.t, _Map_K_T__entry{k: K{k}}) - ma.w.m[K{k}] = &ma.w.t[l].v - // Init the value assembler with a pointer to its target and yield it. - ma.va.ca.w = &ma.w.t[l].v - return &ma.va, nil -} - -func (ma *_Map_K_T__Assembler) AssembleKey() ipld.NodeAssembler { - // Sanity check, then update, assembler state. - if ma.state != maState_initial { - panic("misuse") - } - ma.state = maState_midKey - // Extend entry table. - l := len(ma.w.t) - ma.w.t = append(ma.w.t, _Map_K_T__entry{}) - // Init the key assembler with a pointer to its target and to whole 'ma' and yield it. - ma.ka.ma = ma - ma.ka.ca.w = &ma.w.t[l].k - return &ma.ka -} -func (ma *_Map_K_T__Assembler) AssembleValue() ipld.NodeAssembler { - // Sanity check, then update, assembler state. - if ma.state != maState_expectValue { - panic("misuse") - } - ma.state = maState_midValue - // Init the value assembler with a pointer to its target and yield it. - ma.va.ca.w = &ma.w.t[len(ma.w.t)-1].v - return &ma.va -} -func (ma *_Map_K_T__Assembler) Finish() error { - // Sanity check, then update, assembler state. - if ma.state != maState_initial { - panic("misuse") - } - ma.state = maState_finished - // validators could run and report errors promptly, if this type had any. - return nil -} -func (_Map_K_T__Assembler) KeyStyle() ipld.NodeStyle { panic("later") } -func (_Map_K_T__Assembler) ValueStyle(_ string) ipld.NodeStyle { panic("later") } - -func (_Map_K_T__KeyAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { panic("no") } -func (_Map_K_T__KeyAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { panic("no") } -func (_Map_K_T__KeyAssembler) AssignNull() error { panic("no") } -func (_Map_K_T__KeyAssembler) AssignBool(bool) error { panic("no") } -func (_Map_K_T__KeyAssembler) AssignInt(int) error { panic("no") } -func (_Map_K_T__KeyAssembler) AssignFloat(float64) error { panic("no") } -func (mka *_Map_K_T__KeyAssembler) AssignString(v string) error { - // Check for dup keys; error if so. - _, exists := mka.ma.w.m[K{v}] - if exists { - k := K{v} - return ipld.ErrRepeatedMapKey{&k} - } - // Delegate to the key type's assembler. It may run validations and may error. - // This results in the entry table memory being updated. - // When it returns, the delegated assembler should've already nil'd its 'w' to prevent further mutation. - if err := mka.ca.AssignString(v); err != nil { - return err // REVIEW:errors: probably deserves a wrapper indicating the error came during key coersion. - } - // Update the map to point into the entry value! - // (Hopefully the go compiler recognizes our assignment after existence check and optimizes appropriately.) - mka.ma.w.m[K{v}] = &mka.ma.w.t[len(mka.ma.w.t)-1].v - // Update parent assembler state: clear to proceed. - mka.ma.state = maState_expectValue - mka.ma = nil // invalidate self to prevent further incorrect use. - return nil -} -func (_Map_K_T__KeyAssembler) AssignBytes([]byte) error { panic("no") } -func (_Map_K_T__KeyAssembler) AssignLink(ipld.Link) error { panic("no") } -func (mka *_Map_K_T__KeyAssembler) AssignNode(v ipld.Node) error { - vs, err := v.AsString() - if err != nil { - return fmt.Errorf("cannot assign non-string node into map key assembler") // FIXME:errors: this doesn't quite fit in ErrWrongKind cleanly; new error type? - } - return mka.AssignString(vs) -} -func (_Map_K_T__KeyAssembler) Style() ipld.NodeStyle { panic("later") } // probably should give the style of plainString, which could say "only stores string kind" (though we haven't made such a feature part of the interface yet). - -func (mva *_Map_K_T__ValueAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { - panic("todo") // We would add the additional required methods to 'mva' to save another type... but in this case it's also clear to us at codegen time this method can just error. -} -func (mva *_Map_K_T__ValueAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { - panic("todo") // We would add the additional required methods to 'mva' to save another type... but in this case it's also clear to us at codegen time this method can just error. -} -func (mva *_Map_K_T__ValueAssembler) AssignNull() error { panic("todo") } // All these scalar rejections also clear to us at codegen time. We can report them without delegation. Should? Debatable; but will save SLOC. -func (mva *_Map_K_T__ValueAssembler) AssignBool(bool) error { panic("todo") } -func (mva *_Map_K_T__ValueAssembler) AssignInt(v int) error { - if err := mva.ca.AssignInt(v); err != nil { - return err - } - mva.flush() - return nil -} -func (mva *_Map_K_T__ValueAssembler) AssignFloat(float64) error { panic("todo") } -func (mva *_Map_K_T__ValueAssembler) AssignString(v string) error { panic("todo") } -func (mva *_Map_K_T__ValueAssembler) AssignBytes([]byte) error { panic("todo") } -func (mva *_Map_K_T__ValueAssembler) AssignLink(ipld.Link) error { panic("todo") } -func (mva *_Map_K_T__ValueAssembler) AssignNode(v ipld.Node) error { - if err := mva.ca.AssignNode(v); err != nil { - return err - } - mva.flush() - return nil -} -func (mva *_Map_K_T__ValueAssembler) flush() { - // The child assembler already assigned directly into the target memory, - // so there's not much to do here... except update the assembler state machine. - // We also don't check the previous state because context makes us already sure: - // A) the appropriate time to do that would've been *before* assignments; - // A.2) accordingly, we did so before exposing this value assembler at all; and - // B) if we were in a wrong state because someone holds onto this too long, - // the invalidation we're about to do on `mva.ca.w` will make it impossible - // for them to make changes in appropriately. - mva.ma.state = maState_initial - mva.ca.w = nil -} -func (_Map_K_T__ValueAssembler) Style() ipld.NodeStyle { panic("later") } - -// type _Map_K_T__ReprAssembler struct { -// w *Map_K_T -// // todo: ka _Map_K_T__KeyAssembler ? might need a different type for repr. -// // todo: va _Map_K_T__ValueAssembler ? might need a different type for repr. -// } diff --git a/node/gendemo/map_K_T_test.go b/node/gendemo/map_K_T_test.go deleted file mode 100644 index c67d466d..00000000 --- a/node/gendemo/map_K_T_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package gendemo - -import ( - "testing" - - "github.com/ipld/go-ipld-prime/node/tests" -) - -func TestGennedMapStrInt(t *testing.T) { - tests.SpecTestMapStrInt(t, Type__Map_K_T{}) -} - -func BenchmarkMapStrInt_3n_AssembleStandard(b *testing.B) { - tests.SpecBenchmarkMapStrInt_3n_AssembleStandard(b, Type__Map_K_T{}) -} -func BenchmarkMapStrInt_3n_AssembleEntry(b *testing.B) { - tests.SpecBenchmarkMapStrInt_3n_AssembleEntry(b, Type__Map_K_T{}) -} -func BenchmarkMapStrInt_3n_Iteration(b *testing.B) { - tests.SpecBenchmarkMapStrInt_3n_Iteration(b, Type__Map_K_T{}) -} - -func BenchmarkMapStrInt_25n_AssembleStandard(b *testing.B) { - tests.SpecBenchmarkMapStrInt_25n_AssembleStandard(b, Type__Map_K_T{}) -} -func BenchmarkMapStrInt_25n_AssembleEntry(b *testing.B) { - tests.SpecBenchmarkMapStrInt_25n_AssembleEntry(b, Type__Map_K_T{}) -} -func BenchmarkMapStrInt_25n_Iteration(b *testing.B) { - tests.SpecBenchmarkMapStrInt_25n_Iteration(b, Type__Map_K_T{}) -} diff --git a/node/gendemo/minima.go b/node/gendemo/minima.go new file mode 100644 index 00000000..a9a4a469 --- /dev/null +++ b/node/gendemo/minima.go @@ -0,0 +1,30 @@ +package gendemo + +// Code generated by go-ipld-prime gengo. DO NOT EDIT. + +import ( + "github.com/ipld/go-ipld-prime/schema" +) + +const ( + midvalue = schema.Maybe(4) + allowNull = schema.Maybe(5) +) + +type maState uint8 + +const ( + maState_initial maState = iota + maState_midKey + maState_expectValue + maState_midValue + maState_finished +) + +type laState uint8 + +const ( + laState_initial laState = iota + laState_midValue + laState_finished +) diff --git a/node/gendemo/string.go b/node/gendemo/string.go deleted file mode 100644 index 9c9a21b7..00000000 --- a/node/gendemo/string.go +++ /dev/null @@ -1,144 +0,0 @@ -package gendemo - -import ( - ipld "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/node/mixins" -) - -var ( - _ ipld.Node = plainString("") - _ ipld.NodeStyle = Style__String{} - _ ipld.NodeBuilder = &plainString__Builder{} - _ ipld.NodeAssembler = &plainString__Assembler{} -) - -// plainString is a simple boxed string that complies with ipld.Node. -// It's useful for many things, such as boxing map keys. -// -// The implementation is a simple typedef of a string; -// handling it as a Node incurs 'runtime.convTstring', -// which is about the best we can do. -type plainString string - -// -- Node interface methods --> - -func (plainString) ReprKind() ipld.ReprKind { - return ipld.ReprKind_String -} -func (plainString) LookupString(string) (ipld.Node, error) { - return mixins.String{"gendemo.String"}.LookupString("") -} -func (plainString) Lookup(key ipld.Node) (ipld.Node, error) { - return mixins.String{"gendemo.String"}.Lookup(nil) -} -func (plainString) LookupIndex(idx int) (ipld.Node, error) { - return mixins.String{"gendemo.String"}.LookupIndex(0) -} -func (plainString) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - return mixins.String{"gendemo.String"}.LookupSegment(seg) -} -func (plainString) MapIterator() ipld.MapIterator { - return nil -} -func (plainString) ListIterator() ipld.ListIterator { - return nil -} -func (plainString) Length() int { - return -1 -} -func (plainString) IsUndefined() bool { - return false -} -func (plainString) IsNull() bool { - return false -} -func (plainString) AsBool() (bool, error) { - return mixins.String{"gendemo.String"}.AsBool() -} -func (plainString) AsInt() (int, error) { - return mixins.String{"gendemo.String"}.AsInt() -} -func (plainString) AsFloat() (float64, error) { - return mixins.String{"gendemo.String"}.AsFloat() -} -func (x plainString) AsString() (string, error) { - return string(x), nil -} -func (plainString) AsBytes() ([]byte, error) { - return mixins.String{"gendemo.String"}.AsBytes() -} -func (plainString) AsLink() (ipld.Link, error) { - return mixins.String{"gendemo.String"}.AsLink() -} -func (plainString) Style() ipld.NodeStyle { - return Style__String{} -} - -// -- NodeStyle --> - -type Style__String struct{} - -func (Style__String) NewBuilder() ipld.NodeBuilder { - var w plainString - return &plainString__Builder{plainString__Assembler{w: &w}} -} - -// -- NodeBuilder --> - -type plainString__Builder struct { - plainString__Assembler -} - -func (nb *plainString__Builder) Build() ipld.Node { - return nb.w -} -func (nb *plainString__Builder) Reset() { - var w plainString - *nb = plainString__Builder{plainString__Assembler{w: &w}} -} - -// -- NodeAssembler --> - -type plainString__Assembler struct { - w *plainString -} - -func (plainString__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { - return mixins.StringAssembler{"gendemo.String"}.BeginMap(0) -} -func (plainString__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { - return mixins.StringAssembler{"gendemo.String"}.BeginList(0) -} -func (plainString__Assembler) AssignNull() error { - return mixins.StringAssembler{"gendemo.String"}.AssignNull() -} -func (plainString__Assembler) AssignBool(bool) error { - return mixins.StringAssembler{"gendemo.String"}.AssignBool(false) -} -func (plainString__Assembler) AssignInt(int) error { - return mixins.StringAssembler{"gendemo.String"}.AssignInt(0) -} -func (plainString__Assembler) AssignFloat(float64) error { - return mixins.StringAssembler{"gendemo.String"}.AssignFloat(0) -} -func (na *plainString__Assembler) AssignString(v string) error { - *na.w = plainString(v) - return nil -} -func (plainString__Assembler) AssignBytes([]byte) error { - return mixins.StringAssembler{"gendemo.String"}.AssignBytes(nil) -} -func (plainString__Assembler) AssignLink(ipld.Link) error { - return mixins.StringAssembler{"gendemo.String"}.AssignLink(nil) -} -func (na *plainString__Assembler) AssignNode(v ipld.Node) error { - if v2, err := v.AsString(); err != nil { - return err - } else { - *na.w = plainString(v2) - return nil - } -} -func (plainString__Assembler) Style() ipld.NodeStyle { - return Style__String{} -} diff --git a/node/gendemo/tInt.go b/node/gendemo/tInt.go new file mode 100644 index 00000000..4dc3d2d4 --- /dev/null +++ b/node/gendemo/tInt.go @@ -0,0 +1,225 @@ +package gendemo + +// Code generated by go-ipld-prime gengo. DO NOT EDIT. + +import ( + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" + "github.com/ipld/go-ipld-prime/schema" +) + +type _Int struct{ x int } +type Int = *_Int + +func (n Int) Int() int { + return n.x +} +func (_Int__Style) FromInt(v int) (Int, error) { + n := _Int{v} + return &n, nil +} + +type _Int__Maybe struct { + m schema.Maybe + v Int +} +type MaybeInt = *_Int__Maybe + +func (m MaybeInt) IsNull() bool { + return m.m == schema.Maybe_Null +} +func (m MaybeInt) IsUndefined() bool { + return m.m == schema.Maybe_Absent +} +func (m MaybeInt) Exists() bool { + return m.m == schema.Maybe_Value +} +func (m MaybeInt) AsNode() ipld.Node { + switch m.m { + case schema.Maybe_Absent: + return ipld.Undef + case schema.Maybe_Null: + return ipld.Null + case schema.Maybe_Value: + return m.v + default: + panic("unreachable") + } +} +func (m MaybeInt) Must() Int { + if !m.Exists() { + panic("unbox of a maybe rejected") + } + return m.v +} + +var _ ipld.Node = (Int)(&_Int{}) +var _ schema.TypedNode = (Int)(&_Int{}) + +func (Int) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Int +} +func (Int) LookupString(string) (ipld.Node, error) { + return mixins.Int{"gendemo.Int"}.LookupString("") +} +func (Int) Lookup(ipld.Node) (ipld.Node, error) { + return mixins.Int{"gendemo.Int"}.Lookup(nil) +} +func (Int) LookupIndex(idx int) (ipld.Node, error) { + return mixins.Int{"gendemo.Int"}.LookupIndex(0) +} +func (Int) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { + return mixins.Int{"gendemo.Int"}.LookupSegment(seg) +} +func (Int) MapIterator() ipld.MapIterator { + return nil +} +func (Int) ListIterator() ipld.ListIterator { + return nil +} +func (Int) Length() int { + return -1 +} +func (Int) IsUndefined() bool { + return false +} +func (Int) IsNull() bool { + return false +} +func (Int) AsBool() (bool, error) { + return mixins.Int{"gendemo.Int"}.AsBool() +} +func (n Int) AsInt() (int, error) { + return n.x, nil +} +func (Int) AsFloat() (float64, error) { + return mixins.Int{"gendemo.Int"}.AsFloat() +} +func (Int) AsString() (string, error) { + return mixins.Int{"gendemo.Int"}.AsString() +} +func (Int) AsBytes() ([]byte, error) { + return mixins.Int{"gendemo.Int"}.AsBytes() +} +func (Int) AsLink() (ipld.Link, error) { + return mixins.Int{"gendemo.Int"}.AsLink() +} +func (Int) Style() ipld.NodeStyle { + return _Int__Style{} +} + +type _Int__Style struct{} + +func (_Int__Style) NewBuilder() ipld.NodeBuilder { + var nb _Int__Builder + nb.Reset() + return &nb +} + +type _Int__Builder struct { + _Int__Assembler +} + +func (nb *_Int__Builder) Build() ipld.Node { + if *nb.m != schema.Maybe_Value { + panic("invalid state: cannot call Build on an assembler that's not finished") + } + return nb.w +} +func (nb *_Int__Builder) Reset() { + var w _Int + var m schema.Maybe + *nb = _Int__Builder{_Int__Assembler{w: &w, m: &m}} +} + +type _Int__Assembler struct { + w *_Int + m *schema.Maybe +} + +func (na *_Int__Assembler) reset() {} +func (_Int__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { + return mixins.IntAssembler{"gendemo.Int"}.BeginMap(0) +} +func (_Int__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { + return mixins.IntAssembler{"gendemo.Int"}.BeginList(0) +} +func (na *_Int__Assembler) AssignNull() error { + switch *na.m { + case allowNull: + *na.m = schema.Maybe_Null + return nil + case schema.Maybe_Absent: + return mixins.IntAssembler{"gendemo.Int"}.AssignNull() + case schema.Maybe_Value, schema.Maybe_Null: + panic("invalid state: cannot assign into assembler that's already finished") + } + panic("unreachable") +} +func (_Int__Assembler) AssignBool(bool) error { + return mixins.IntAssembler{"gendemo.Int"}.AssignBool(false) +} +func (na *_Int__Assembler) AssignInt(v int) error { + switch *na.m { + case schema.Maybe_Value, schema.Maybe_Null: + panic("invalid state: cannot assign into assembler that's already finished") + } + if na.w == nil { + na.w = &_Int{} + } + na.w.x = v + *na.m = schema.Maybe_Value + return nil +} +func (_Int__Assembler) AssignFloat(float64) error { + return mixins.IntAssembler{"gendemo.Int"}.AssignFloat(0) +} +func (_Int__Assembler) AssignString(string) error { + return mixins.IntAssembler{"gendemo.Int"}.AssignString("") +} +func (_Int__Assembler) AssignBytes([]byte) error { + return mixins.IntAssembler{"gendemo.Int"}.AssignBytes(nil) +} +func (_Int__Assembler) AssignLink(ipld.Link) error { + return mixins.IntAssembler{"gendemo.Int"}.AssignLink(nil) +} +func (na *_Int__Assembler) AssignNode(v ipld.Node) error { + if v.IsNull() { + return na.AssignNull() + } + if v2, ok := v.(*_Int); ok { + switch *na.m { + case schema.Maybe_Value, schema.Maybe_Null: + panic("invalid state: cannot assign into assembler that's already finished") + } + if na.w == nil { + na.w = v2 + *na.m = schema.Maybe_Value + return nil + } + *na.w = *v2 + *na.m = schema.Maybe_Value + return nil + } + if v2, err := v.AsInt(); err != nil { + return err + } else { + return na.AssignInt(v2) + } +} +func (_Int__Assembler) Style() ipld.NodeStyle { + return _Int__Style{} +} +func (Int) Type() schema.Type { + return nil /*TODO:typelit*/ +} +func (n Int) Representation() ipld.Node { + return (*_Int__Repr)(n) +} + +type _Int__Repr = _Int + +var _ ipld.Node = &_Int__Repr{} + +type _Int__ReprStyle = _Int__Style +type _Int__ReprAssembler = _Int__Assembler diff --git a/node/gendemo/tMap__String__Msg3.go b/node/gendemo/tMap__String__Msg3.go new file mode 100644 index 00000000..312b828a --- /dev/null +++ b/node/gendemo/tMap__String__Msg3.go @@ -0,0 +1,759 @@ +package gendemo + +// Code generated by go-ipld-prime gengo. DO NOT EDIT. + +import ( + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" + "github.com/ipld/go-ipld-prime/schema" +) + +type _Map__String__Msg3 struct { + m map[_String]*_Msg3 + t []_Map__String__Msg3__entry +} +type Map__String__Msg3 = *_Map__String__Msg3 +type _Map__String__Msg3__entry struct { + k _String + v _Msg3 +} + +func (n *_Map__String__Msg3) LookupMaybe(k String) MaybeMsg3 { + v, ok := n.m[*k] + if !ok { + return &_Map__String__Msg3__valueAbsent + } + return &_Msg3__Maybe{ + m: schema.Maybe_Value, + v: v, + } +} + +var _Map__String__Msg3__valueAbsent = _Msg3__Maybe{m: schema.Maybe_Absent} + +// TODO generate also a plain Lookup method that doesn't box and alloc if this type contains non-nullable values! +type _Map__String__Msg3__Maybe struct { + m schema.Maybe + v Map__String__Msg3 +} +type MaybeMap__String__Msg3 = *_Map__String__Msg3__Maybe + +func (m MaybeMap__String__Msg3) IsNull() bool { + return m.m == schema.Maybe_Null +} +func (m MaybeMap__String__Msg3) IsUndefined() bool { + return m.m == schema.Maybe_Absent +} +func (m MaybeMap__String__Msg3) Exists() bool { + return m.m == schema.Maybe_Value +} +func (m MaybeMap__String__Msg3) AsNode() ipld.Node { + switch m.m { + case schema.Maybe_Absent: + return ipld.Undef + case schema.Maybe_Null: + return ipld.Null + case schema.Maybe_Value: + return m.v + default: + panic("unreachable") + } +} +func (m MaybeMap__String__Msg3) Must() Map__String__Msg3 { + if !m.Exists() { + panic("unbox of a maybe rejected") + } + return m.v +} + +var _ ipld.Node = (Map__String__Msg3)(&_Map__String__Msg3{}) +var _ schema.TypedNode = (Map__String__Msg3)(&_Map__String__Msg3{}) + +func (Map__String__Msg3) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Map +} +func (n Map__String__Msg3) LookupString(k string) (ipld.Node, error) { + var k2 _String + if err := (_String__Style{}).fromString(&k2, k); err != nil { + return nil, err // TODO wrap in some kind of ErrInvalidKey + } + v, exists := n.m[k2] + if !exists { + return nil, ipld.ErrNotExists{ipld.PathSegmentOfString(k)} + } + return v, nil +} +func (n Map__String__Msg3) Lookup(k ipld.Node) (ipld.Node, error) { + k2, ok := k.(String) + if !ok { + panic("todo invalid key type error") + // 'ipld.ErrInvalidKey{TypeName:"gendemo.Map__String__Msg3", Key:&_String{k}}' doesn't quite cut it: need room to explain the type, and it's not guaranteed k can be turned into a string at all + } + v, exists := n.m[*k2] + if !exists { + return nil, ipld.ErrNotExists{ipld.PathSegmentOfString(k2.String())} + } + return v, nil +} +func (Map__String__Msg3) LookupIndex(idx int) (ipld.Node, error) { + return mixins.Map{"gendemo.Map__String__Msg3"}.LookupIndex(0) +} +func (n Map__String__Msg3) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { + return n.LookupString(seg.String()) +} +func (n Map__String__Msg3) MapIterator() ipld.MapIterator { + return &_Map__String__Msg3__MapItr{n, 0} +} + +type _Map__String__Msg3__MapItr struct { + n Map__String__Msg3 + idx int +} + +func (itr *_Map__String__Msg3__MapItr) Next() (k ipld.Node, v ipld.Node, _ error) { + if itr.idx >= len(itr.n.t) { + return nil, nil, ipld.ErrIteratorOverread{} + } + x := &itr.n.t[itr.idx] + k = &x.k + v = &x.v + itr.idx++ + return +} +func (itr *_Map__String__Msg3__MapItr) Done() bool { + return itr.idx >= len(itr.n.t) +} + +func (Map__String__Msg3) ListIterator() ipld.ListIterator { + return nil +} +func (n Map__String__Msg3) Length() int { + return len(n.t) +} +func (Map__String__Msg3) IsUndefined() bool { + return false +} +func (Map__String__Msg3) IsNull() bool { + return false +} +func (Map__String__Msg3) AsBool() (bool, error) { + return mixins.Map{"gendemo.Map__String__Msg3"}.AsBool() +} +func (Map__String__Msg3) AsInt() (int, error) { + return mixins.Map{"gendemo.Map__String__Msg3"}.AsInt() +} +func (Map__String__Msg3) AsFloat() (float64, error) { + return mixins.Map{"gendemo.Map__String__Msg3"}.AsFloat() +} +func (Map__String__Msg3) AsString() (string, error) { + return mixins.Map{"gendemo.Map__String__Msg3"}.AsString() +} +func (Map__String__Msg3) AsBytes() ([]byte, error) { + return mixins.Map{"gendemo.Map__String__Msg3"}.AsBytes() +} +func (Map__String__Msg3) AsLink() (ipld.Link, error) { + return mixins.Map{"gendemo.Map__String__Msg3"}.AsLink() +} +func (Map__String__Msg3) Style() ipld.NodeStyle { + return _Map__String__Msg3__Style{} +} + +type _Map__String__Msg3__Style struct{} + +func (_Map__String__Msg3__Style) NewBuilder() ipld.NodeBuilder { + var nb _Map__String__Msg3__Builder + nb.Reset() + return &nb +} + +type _Map__String__Msg3__Builder struct { + _Map__String__Msg3__Assembler +} + +func (nb *_Map__String__Msg3__Builder) Build() ipld.Node { + if *nb.m != schema.Maybe_Value { + panic("invalid state: cannot call Build on an assembler that's not finished") + } + return nb.w +} +func (nb *_Map__String__Msg3__Builder) Reset() { + var w _Map__String__Msg3 + var m schema.Maybe + *nb = _Map__String__Msg3__Builder{_Map__String__Msg3__Assembler{w: &w, m: &m}} +} + +type _Map__String__Msg3__Assembler struct { + w *_Map__String__Msg3 + m *schema.Maybe + state maState + + cm schema.Maybe + ka _String__Assembler + va _Msg3__Assembler +} + +func (na *_Map__String__Msg3__Assembler) reset() { + na.state = maState_initial + na.ka.reset() + na.va.reset() +} +func (na *_Map__String__Msg3__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, 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 na.w == nil { + na.w = &_Map__String__Msg3{} + } + na.w.m = make(map[_String]*_Msg3, sizeHint) + na.w.t = make([]_Map__String__Msg3__entry, 0, sizeHint) + return na, nil +} +func (_Map__String__Msg3__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { + return mixins.MapAssembler{"gendemo.Map__String__Msg3"}.BeginList(0) +} +func (na *_Map__String__Msg3__Assembler) AssignNull() error { + switch *na.m { + case allowNull: + *na.m = schema.Maybe_Null + return nil + case schema.Maybe_Absent: + return mixins.MapAssembler{"gendemo.Map__String__Msg3"}.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") +} +func (_Map__String__Msg3__Assembler) AssignBool(bool) error { + return mixins.MapAssembler{"gendemo.Map__String__Msg3"}.AssignBool(false) +} +func (_Map__String__Msg3__Assembler) AssignInt(int) error { + return mixins.MapAssembler{"gendemo.Map__String__Msg3"}.AssignInt(0) +} +func (_Map__String__Msg3__Assembler) AssignFloat(float64) error { + return mixins.MapAssembler{"gendemo.Map__String__Msg3"}.AssignFloat(0) +} +func (_Map__String__Msg3__Assembler) AssignString(string) error { + return mixins.MapAssembler{"gendemo.Map__String__Msg3"}.AssignString("") +} +func (_Map__String__Msg3__Assembler) AssignBytes([]byte) error { + return mixins.MapAssembler{"gendemo.Map__String__Msg3"}.AssignBytes(nil) +} +func (_Map__String__Msg3__Assembler) AssignLink(ipld.Link) error { + return mixins.MapAssembler{"gendemo.Map__String__Msg3"}.AssignLink(nil) +} +func (na *_Map__String__Msg3__Assembler) AssignNode(v ipld.Node) error { + if v.IsNull() { + return na.AssignNull() + } + if v2, ok := v.(*_Map__String__Msg3); 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 na.w == nil { + na.w = v2 + *na.m = schema.Maybe_Value + return nil + } + *na.w = *v2 + *na.m = schema.Maybe_Value + return nil + } + if v.ReprKind() != ipld.ReprKind_Map { + return ipld.ErrWrongKind{TypeName: "gendemo.Map__String__Msg3", MethodName: "AssignNode", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: v.ReprKind()} + } + itr := v.MapIterator() + for !itr.Done() { + k, v, err := itr.Next() + if err != nil { + return err + } + if err := na.AssembleKey().AssignNode(k); err != nil { + return err + } + if err := na.AssembleValue().AssignNode(v); err != nil { + return err + } + } + return na.Finish() +} +func (_Map__String__Msg3__Assembler) Style() ipld.NodeStyle { + return _Map__String__Msg3__Style{} +} +func (ma *_Map__String__Msg3__Assembler) keyFinishTidy() bool { + switch ma.cm { + case schema.Maybe_Value: + ma.ka.w = nil + tz := &ma.w.t[len(ma.w.t)-1] + ma.cm = schema.Maybe_Absent + ma.state = maState_expectValue + ma.w.m[tz.k] = &tz.v + ma.va.w = &tz.v + ma.va.m = &ma.cm + ma.ka.reset() + return true + default: + return false + } +} +func (ma *_Map__String__Msg3__Assembler) valueFinishTidy() bool { + switch ma.cm { + case schema.Maybe_Value: + ma.va.w = nil + ma.cm = schema.Maybe_Absent + ma.state = maState_initial + ma.va.reset() + return true + default: + return false + } +} +func (ma *_Map__String__Msg3__Assembler) AssembleEntry(k string) (ipld.NodeAssembler, error) { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleEntry cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleEntry cannot be called on an assembler that's already finished") + } + + var k2 _String + if err := (_String__Style{}).fromString(&k2, k); err != nil { + return nil, err // TODO wrap in some kind of ErrInvalidKey + } + if _, exists := ma.w.m[k2]; exists { + return nil, ipld.ErrRepeatedMapKey{&k2} + } + ma.w.t = append(ma.w.t, _Map__String__Msg3__entry{k: k2}) + tz := &ma.w.t[len(ma.w.t)-1] + ma.state = maState_midValue + + ma.w.m[k2] = &tz.v + ma.va.w = &tz.v + ma.va.m = &ma.cm + return &ma.va, nil +} +func (ma *_Map__String__Msg3__Assembler) AssembleKey() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleKey cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleKey cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleKey cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleKey cannot be called on an assembler that's already finished") + } + ma.w.t = append(ma.w.t, _Map__String__Msg3__entry{}) + ma.state = maState_midKey + ma.ka.m = &ma.cm + ma.ka.w = &ma.w.t[len(ma.w.t)-1].k + return &ma.ka +} +func (ma *_Map__String__Msg3__Assembler) AssembleValue() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + panic("invalid state: AssembleValue cannot be called when no key is primed") + case maState_midKey: + if !ma.keyFinishTidy() { + panic("invalid state: AssembleValue cannot be called when in the middle of assembling a key") + } // if tidy success: carry on + case maState_expectValue: + // carry on + case maState_midValue: + panic("invalid state: AssembleValue cannot be called when in the middle of assembling another value") + case maState_finished: + panic("invalid state: AssembleValue cannot be called on an assembler that's already finished") + } + ma.state = maState_midValue + return &ma.va +} +func (ma *_Map__String__Msg3__Assembler) Finish() error { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: Finish cannot be called when in the middle of assembling a key") + case maState_expectValue: + panic("invalid state: Finish cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: Finish cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: Finish cannot be called on an assembler that's already finished") + } + ma.state = maState_finished + *ma.m = schema.Maybe_Value + return nil +} +func (ma *_Map__String__Msg3__Assembler) KeyStyle() ipld.NodeStyle { + return _String__Style{} +} +func (ma *_Map__String__Msg3__Assembler) ValueStyle(_ string) ipld.NodeStyle { + return _Msg3__Style{} +} +func (Map__String__Msg3) Type() schema.Type { + return nil /*TODO:typelit*/ +} +func (n Map__String__Msg3) Representation() ipld.Node { + return (*_Map__String__Msg3__Repr)(n) +} + +type _Map__String__Msg3__Repr _Map__String__Msg3 + +var _ ipld.Node = &_Map__String__Msg3__Repr{} + +func (_Map__String__Msg3__Repr) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Map +} +func (nr *_Map__String__Msg3__Repr) LookupString(k string) (ipld.Node, error) { + v, err := (Map__String__Msg3)(nr).LookupString(k) + if err != nil || v == ipld.Null { + return v, err + } + return v.(Msg3).Representation(), nil +} +func (nr *_Map__String__Msg3__Repr) Lookup(k ipld.Node) (ipld.Node, error) { + v, err := (Map__String__Msg3)(nr).Lookup(k) + if err != nil || v == ipld.Null { + return v, err + } + return v.(Msg3).Representation(), nil +} +func (_Map__String__Msg3__Repr) LookupIndex(idx int) (ipld.Node, error) { + return mixins.Map{"gendemo.Map__String__Msg3.Repr"}.LookupIndex(0) +} +func (n _Map__String__Msg3__Repr) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { + return n.LookupString(seg.String()) +} +func (nr *_Map__String__Msg3__Repr) MapIterator() ipld.MapIterator { + return &_Map__String__Msg3__ReprMapItr{(Map__String__Msg3)(nr), 0} +} + +type _Map__String__Msg3__ReprMapItr _Map__String__Msg3__MapItr + +func (itr *_Map__String__Msg3__ReprMapItr) Next() (k ipld.Node, v ipld.Node, err error) { + k, v, err = (*_Map__String__Msg3__MapItr)(itr).Next() + if err != nil || v == ipld.Null { + return + } + return k, v.(Msg3).Representation(), nil +} +func (itr *_Map__String__Msg3__ReprMapItr) Done() bool { + return (*_Map__String__Msg3__MapItr)(itr).Done() +} + +func (_Map__String__Msg3__Repr) ListIterator() ipld.ListIterator { + return nil +} +func (rn *_Map__String__Msg3__Repr) Length() int { + return len(rn.t) +} +func (_Map__String__Msg3__Repr) IsUndefined() bool { + return false +} +func (_Map__String__Msg3__Repr) IsNull() bool { + return false +} +func (_Map__String__Msg3__Repr) AsBool() (bool, error) { + return mixins.Map{"gendemo.Map__String__Msg3.Repr"}.AsBool() +} +func (_Map__String__Msg3__Repr) AsInt() (int, error) { + return mixins.Map{"gendemo.Map__String__Msg3.Repr"}.AsInt() +} +func (_Map__String__Msg3__Repr) AsFloat() (float64, error) { + return mixins.Map{"gendemo.Map__String__Msg3.Repr"}.AsFloat() +} +func (_Map__String__Msg3__Repr) AsString() (string, error) { + return mixins.Map{"gendemo.Map__String__Msg3.Repr"}.AsString() +} +func (_Map__String__Msg3__Repr) AsBytes() ([]byte, error) { + return mixins.Map{"gendemo.Map__String__Msg3.Repr"}.AsBytes() +} +func (_Map__String__Msg3__Repr) AsLink() (ipld.Link, error) { + return mixins.Map{"gendemo.Map__String__Msg3.Repr"}.AsLink() +} +func (_Map__String__Msg3__Repr) Style() ipld.NodeStyle { + return _Map__String__Msg3__ReprStyle{} +} + +type _Map__String__Msg3__ReprStyle struct{} + +func (_Map__String__Msg3__ReprStyle) NewBuilder() ipld.NodeBuilder { + var nb _Map__String__Msg3__ReprBuilder + nb.Reset() + return &nb +} + +type _Map__String__Msg3__ReprBuilder struct { + _Map__String__Msg3__ReprAssembler +} + +func (nb *_Map__String__Msg3__ReprBuilder) Build() ipld.Node { + if *nb.m != schema.Maybe_Value { + panic("invalid state: cannot call Build on an assembler that's not finished") + } + return nb.w +} +func (nb *_Map__String__Msg3__ReprBuilder) Reset() { + var w _Map__String__Msg3 + var m schema.Maybe + *nb = _Map__String__Msg3__ReprBuilder{_Map__String__Msg3__ReprAssembler{w: &w, m: &m}} +} + +type _Map__String__Msg3__ReprAssembler struct { + w *_Map__String__Msg3 + m *schema.Maybe + state maState + + cm schema.Maybe + ka _String__ReprAssembler + va _Msg3__ReprAssembler +} + +func (na *_Map__String__Msg3__ReprAssembler) reset() { + na.state = maState_initial + na.ka.reset() + na.va.reset() +} +func (na *_Map__String__Msg3__ReprAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, 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 na.w == nil { + na.w = &_Map__String__Msg3{} + } + na.w.m = make(map[_String]*_Msg3, sizeHint) + na.w.t = make([]_Map__String__Msg3__entry, 0, sizeHint) + return na, nil +} +func (_Map__String__Msg3__ReprAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { + return mixins.MapAssembler{"gendemo.Map__String__Msg3.Repr"}.BeginList(0) +} +func (na *_Map__String__Msg3__ReprAssembler) AssignNull() error { + switch *na.m { + case allowNull: + *na.m = schema.Maybe_Null + return nil + case schema.Maybe_Absent: + return mixins.MapAssembler{"gendemo.Map__String__Msg3.Repr.Repr"}.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") +} +func (_Map__String__Msg3__ReprAssembler) AssignBool(bool) error { + return mixins.MapAssembler{"gendemo.Map__String__Msg3.Repr"}.AssignBool(false) +} +func (_Map__String__Msg3__ReprAssembler) AssignInt(int) error { + return mixins.MapAssembler{"gendemo.Map__String__Msg3.Repr"}.AssignInt(0) +} +func (_Map__String__Msg3__ReprAssembler) AssignFloat(float64) error { + return mixins.MapAssembler{"gendemo.Map__String__Msg3.Repr"}.AssignFloat(0) +} +func (_Map__String__Msg3__ReprAssembler) AssignString(string) error { + return mixins.MapAssembler{"gendemo.Map__String__Msg3.Repr"}.AssignString("") +} +func (_Map__String__Msg3__ReprAssembler) AssignBytes([]byte) error { + return mixins.MapAssembler{"gendemo.Map__String__Msg3.Repr"}.AssignBytes(nil) +} +func (_Map__String__Msg3__ReprAssembler) AssignLink(ipld.Link) error { + return mixins.MapAssembler{"gendemo.Map__String__Msg3.Repr"}.AssignLink(nil) +} +func (na *_Map__String__Msg3__ReprAssembler) AssignNode(v ipld.Node) error { + if v.IsNull() { + return na.AssignNull() + } + if v2, ok := v.(*_Map__String__Msg3); 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 na.w == nil { + na.w = v2 + *na.m = schema.Maybe_Value + return nil + } + *na.w = *v2 + *na.m = schema.Maybe_Value + return nil + } + if v.ReprKind() != ipld.ReprKind_Map { + return ipld.ErrWrongKind{TypeName: "gendemo.Map__String__Msg3.Repr", MethodName: "AssignNode", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: v.ReprKind()} + } + itr := v.MapIterator() + for !itr.Done() { + k, v, err := itr.Next() + if err != nil { + return err + } + if err := na.AssembleKey().AssignNode(k); err != nil { + return err + } + if err := na.AssembleValue().AssignNode(v); err != nil { + return err + } + } + return na.Finish() +} +func (_Map__String__Msg3__ReprAssembler) Style() ipld.NodeStyle { + return _Map__String__Msg3__ReprStyle{} +} +func (ma *_Map__String__Msg3__ReprAssembler) keyFinishTidy() bool { + switch ma.cm { + case schema.Maybe_Value: + ma.ka.w = nil + tz := &ma.w.t[len(ma.w.t)-1] + ma.cm = schema.Maybe_Absent + ma.state = maState_expectValue + ma.w.m[tz.k] = &tz.v + ma.va.w = &tz.v + ma.va.m = &ma.cm + ma.ka.reset() + return true + default: + return false + } +} +func (ma *_Map__String__Msg3__ReprAssembler) valueFinishTidy() bool { + switch ma.cm { + case schema.Maybe_Value: + ma.va.w = nil + ma.cm = schema.Maybe_Absent + ma.state = maState_initial + ma.va.reset() + return true + default: + return false + } +} +func (ma *_Map__String__Msg3__ReprAssembler) AssembleEntry(k string) (ipld.NodeAssembler, error) { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleEntry cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleEntry cannot be called on an assembler that's already finished") + } + + var k2 _String + if err := (_String__ReprStyle{}).fromString(&k2, k); err != nil { + return nil, err // TODO wrap in some kind of ErrInvalidKey + } + if _, exists := ma.w.m[k2]; exists { + return nil, ipld.ErrRepeatedMapKey{&k2} + } + ma.w.t = append(ma.w.t, _Map__String__Msg3__entry{k: k2}) + tz := &ma.w.t[len(ma.w.t)-1] + ma.state = maState_midValue + + ma.w.m[k2] = &tz.v + ma.va.w = &tz.v + ma.va.m = &ma.cm + return &ma.va, nil +} +func (ma *_Map__String__Msg3__ReprAssembler) AssembleKey() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleKey cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleKey cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleKey cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleKey cannot be called on an assembler that's already finished") + } + ma.w.t = append(ma.w.t, _Map__String__Msg3__entry{}) + ma.state = maState_midKey + ma.ka.m = &ma.cm + ma.ka.w = &ma.w.t[len(ma.w.t)-1].k + return &ma.ka +} +func (ma *_Map__String__Msg3__ReprAssembler) AssembleValue() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + panic("invalid state: AssembleValue cannot be called when no key is primed") + case maState_midKey: + if !ma.keyFinishTidy() { + panic("invalid state: AssembleValue cannot be called when in the middle of assembling a key") + } // if tidy success: carry on + case maState_expectValue: + // carry on + case maState_midValue: + panic("invalid state: AssembleValue cannot be called when in the middle of assembling another value") + case maState_finished: + panic("invalid state: AssembleValue cannot be called on an assembler that's already finished") + } + ma.state = maState_midValue + return &ma.va +} +func (ma *_Map__String__Msg3__ReprAssembler) Finish() error { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: Finish cannot be called when in the middle of assembling a key") + case maState_expectValue: + panic("invalid state: Finish cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: Finish cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: Finish cannot be called on an assembler that's already finished") + } + ma.state = maState_finished + *ma.m = schema.Maybe_Value + return nil +} +func (ma *_Map__String__Msg3__ReprAssembler) KeyStyle() ipld.NodeStyle { + return _String__ReprStyle{} +} +func (ma *_Map__String__Msg3__ReprAssembler) ValueStyle(_ string) ipld.NodeStyle { + return _Msg3__ReprStyle{} +} diff --git a/node/gendemo/tMsg3.go b/node/gendemo/tMsg3.go new file mode 100644 index 00000000..47cefed1 --- /dev/null +++ b/node/gendemo/tMsg3.go @@ -0,0 +1,998 @@ +package gendemo + +// Code generated by go-ipld-prime gengo. DO NOT EDIT. + +import ( + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" + "github.com/ipld/go-ipld-prime/schema" +) + +type _Msg3 struct { + whee _Int + woot _Int + waga _Int +} +type Msg3 = *_Msg3 + +func (n _Msg3) FieldWhee() Int { + return &n.whee +} +func (n _Msg3) FieldWoot() Int { + return &n.woot +} +func (n _Msg3) FieldWaga() Int { + return &n.waga +} + +type _Msg3__Maybe struct { + m schema.Maybe + v Msg3 +} +type MaybeMsg3 = *_Msg3__Maybe + +func (m MaybeMsg3) IsNull() bool { + return m.m == schema.Maybe_Null +} +func (m MaybeMsg3) IsUndefined() bool { + return m.m == schema.Maybe_Absent +} +func (m MaybeMsg3) Exists() bool { + return m.m == schema.Maybe_Value +} +func (m MaybeMsg3) AsNode() ipld.Node { + switch m.m { + case schema.Maybe_Absent: + return ipld.Undef + case schema.Maybe_Null: + return ipld.Null + case schema.Maybe_Value: + return m.v + default: + panic("unreachable") + } +} +func (m MaybeMsg3) Must() Msg3 { + if !m.Exists() { + panic("unbox of a maybe rejected") + } + return m.v +} + +var ( + fieldName__Msg3_Whee = _String{"whee"} + fieldName__Msg3_Woot = _String{"woot"} + fieldName__Msg3_Waga = _String{"waga"} +) +var _ ipld.Node = (Msg3)(&_Msg3{}) +var _ schema.TypedNode = (Msg3)(&_Msg3{}) + +func (Msg3) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Map +} +func (n Msg3) LookupString(key string) (ipld.Node, error) { + switch key { + case "whee": + return &n.whee, nil + case "woot": + return &n.woot, nil + case "waga": + return &n.waga, nil + default: + return nil, schema.ErrNoSuchField{Type: nil /*TODO*/, FieldName: key} + } +} +func (n Msg3) Lookup(key ipld.Node) (ipld.Node, error) { + ks, err := key.AsString() + if err != nil { + return nil, err + } + return n.LookupString(ks) +} +func (Msg3) LookupIndex(idx int) (ipld.Node, error) { + return mixins.Map{"gendemo.Msg3"}.LookupIndex(0) +} +func (n Msg3) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { + return n.LookupString(seg.String()) +} +func (n Msg3) MapIterator() ipld.MapIterator { + return &_Msg3__MapItr{n, 0} +} + +type _Msg3__MapItr struct { + n Msg3 + idx int +} + +func (itr *_Msg3__MapItr) Next() (k ipld.Node, v ipld.Node, _ error) { + if itr.idx >= 3 { + return nil, nil, ipld.ErrIteratorOverread{} + } + switch itr.idx { + case 0: + k = &fieldName__Msg3_Whee + v = &itr.n.whee + case 1: + k = &fieldName__Msg3_Woot + v = &itr.n.woot + case 2: + k = &fieldName__Msg3_Waga + v = &itr.n.waga + default: + panic("unreachable") + } + itr.idx++ + return +} +func (itr *_Msg3__MapItr) Done() bool { + return itr.idx >= 3 +} + +func (Msg3) ListIterator() ipld.ListIterator { + return nil +} +func (Msg3) Length() int { + return 3 +} +func (Msg3) IsUndefined() bool { + return false +} +func (Msg3) IsNull() bool { + return false +} +func (Msg3) AsBool() (bool, error) { + return mixins.Map{"gendemo.Msg3"}.AsBool() +} +func (Msg3) AsInt() (int, error) { + return mixins.Map{"gendemo.Msg3"}.AsInt() +} +func (Msg3) AsFloat() (float64, error) { + return mixins.Map{"gendemo.Msg3"}.AsFloat() +} +func (Msg3) AsString() (string, error) { + return mixins.Map{"gendemo.Msg3"}.AsString() +} +func (Msg3) AsBytes() ([]byte, error) { + return mixins.Map{"gendemo.Msg3"}.AsBytes() +} +func (Msg3) AsLink() (ipld.Link, error) { + return mixins.Map{"gendemo.Msg3"}.AsLink() +} +func (Msg3) Style() ipld.NodeStyle { + return _Msg3__Style{} +} + +type _Msg3__Style struct{} + +func (_Msg3__Style) NewBuilder() ipld.NodeBuilder { + var nb _Msg3__Builder + nb.Reset() + return &nb +} + +type _Msg3__Builder struct { + _Msg3__Assembler +} + +func (nb *_Msg3__Builder) Build() ipld.Node { + if *nb.m != schema.Maybe_Value { + panic("invalid state: cannot call Build on an assembler that's not finished") + } + return nb.w +} +func (nb *_Msg3__Builder) Reset() { + var w _Msg3 + var m schema.Maybe + *nb = _Msg3__Builder{_Msg3__Assembler{w: &w, m: &m}} +} + +type _Msg3__Assembler struct { + w *_Msg3 + m *schema.Maybe + state maState + s int + f int + + cm schema.Maybe + ca_whee _Int__Assembler + ca_woot _Int__Assembler + ca_waga _Int__Assembler +} + +func (na *_Msg3__Assembler) reset() { + na.state = maState_initial + na.s = 0 + na.ca_whee.reset() + na.ca_woot.reset() + na.ca_waga.reset() +} + +var ( + fieldBit__Msg3_Whee = 1 << 0 + fieldBit__Msg3_Woot = 1 << 1 + fieldBit__Msg3_Waga = 1 << 2 + fieldBits__Msg3_sufficient = 0 + 1<<0 + 1<<1 + 1<<2 +) + +func (na *_Msg3__Assembler) BeginMap(int) (ipld.MapAssembler, 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 na.w == nil { + na.w = &_Msg3{} + } + return na, nil +} +func (_Msg3__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { + return mixins.MapAssembler{"gendemo.Msg3"}.BeginList(0) +} +func (na *_Msg3__Assembler) AssignNull() error { + switch *na.m { + case allowNull: + *na.m = schema.Maybe_Null + return nil + case schema.Maybe_Absent: + return mixins.MapAssembler{"gendemo.Msg3"}.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") +} +func (_Msg3__Assembler) AssignBool(bool) error { + return mixins.MapAssembler{"gendemo.Msg3"}.AssignBool(false) +} +func (_Msg3__Assembler) AssignInt(int) error { + return mixins.MapAssembler{"gendemo.Msg3"}.AssignInt(0) +} +func (_Msg3__Assembler) AssignFloat(float64) error { + return mixins.MapAssembler{"gendemo.Msg3"}.AssignFloat(0) +} +func (_Msg3__Assembler) AssignString(string) error { + return mixins.MapAssembler{"gendemo.Msg3"}.AssignString("") +} +func (_Msg3__Assembler) AssignBytes([]byte) error { + return mixins.MapAssembler{"gendemo.Msg3"}.AssignBytes(nil) +} +func (_Msg3__Assembler) AssignLink(ipld.Link) error { + return mixins.MapAssembler{"gendemo.Msg3"}.AssignLink(nil) +} +func (na *_Msg3__Assembler) AssignNode(v ipld.Node) error { + if v.IsNull() { + return na.AssignNull() + } + if v2, ok := v.(*_Msg3); 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 na.w == nil { + na.w = v2 + *na.m = schema.Maybe_Value + return nil + } + *na.w = *v2 + *na.m = schema.Maybe_Value + return nil + } + if v.ReprKind() != ipld.ReprKind_Map { + return ipld.ErrWrongKind{TypeName: "gendemo.Msg3", MethodName: "AssignNode", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: v.ReprKind()} + } + itr := v.MapIterator() + for !itr.Done() { + k, v, err := itr.Next() + if err != nil { + return err + } + if err := na.AssembleKey().AssignNode(k); err != nil { + return err + } + if err := na.AssembleValue().AssignNode(v); err != nil { + return err + } + } + return na.Finish() +} +func (_Msg3__Assembler) Style() ipld.NodeStyle { + return _Msg3__Style{} +} +func (ma *_Msg3__Assembler) valueFinishTidy() bool { + switch ma.f { + case 0: + switch ma.cm { + case schema.Maybe_Value: + ma.ca_whee.w = nil + ma.cm = schema.Maybe_Absent + ma.state = maState_initial + return true + default: + return false + } + case 1: + switch ma.cm { + case schema.Maybe_Value: + ma.ca_woot.w = nil + ma.cm = schema.Maybe_Absent + ma.state = maState_initial + return true + default: + return false + } + case 2: + switch ma.cm { + case schema.Maybe_Value: + ma.ca_waga.w = nil + ma.cm = schema.Maybe_Absent + ma.state = maState_initial + return true + default: + return false + } + default: + panic("unreachable") + } +} +func (ma *_Msg3__Assembler) AssembleEntry(k string) (ipld.NodeAssembler, error) { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleEntry cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleEntry cannot be called on an assembler that's already finished") + } + switch k { + case "whee": + if ma.s&fieldBit__Msg3_Whee != 0 { + return nil, ipld.ErrRepeatedMapKey{&fieldName__Msg3_Whee} + } + ma.s += fieldBit__Msg3_Whee + ma.state = maState_midValue + ma.ca_whee.w = &ma.w.whee + ma.ca_whee.m = &ma.cm + return &ma.ca_whee, nil + case "woot": + if ma.s&fieldBit__Msg3_Woot != 0 { + return nil, ipld.ErrRepeatedMapKey{&fieldName__Msg3_Woot} + } + ma.s += fieldBit__Msg3_Woot + ma.state = maState_midValue + ma.ca_woot.w = &ma.w.woot + ma.ca_woot.m = &ma.cm + return &ma.ca_woot, nil + case "waga": + if ma.s&fieldBit__Msg3_Waga != 0 { + return nil, ipld.ErrRepeatedMapKey{&fieldName__Msg3_Waga} + } + ma.s += fieldBit__Msg3_Waga + ma.state = maState_midValue + ma.ca_waga.w = &ma.w.waga + ma.ca_waga.m = &ma.cm + return &ma.ca_waga, nil + default: + return nil, ipld.ErrInvalidKey{TypeName: "gendemo.Msg3", Key: &_String{k}} + } +} +func (ma *_Msg3__Assembler) AssembleKey() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleKey cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleKey cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleKey cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleKey cannot be called on an assembler that's already finished") + } + ma.state = maState_midKey + return (*_Msg3__KeyAssembler)(ma) +} +func (ma *_Msg3__Assembler) AssembleValue() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + panic("invalid state: AssembleValue cannot be called when no key is primed") + case maState_midKey: + panic("invalid state: AssembleValue cannot be called when in the middle of assembling a key") + case maState_expectValue: + // carry on + case maState_midValue: + panic("invalid state: AssembleValue cannot be called when in the middle of assembling another value") + case maState_finished: + panic("invalid state: AssembleValue cannot be called on an assembler that's already finished") + } + ma.state = maState_midValue + switch ma.f { + case 0: + ma.ca_whee.w = &ma.w.whee + ma.ca_whee.m = &ma.cm + return &ma.ca_whee + case 1: + ma.ca_woot.w = &ma.w.woot + ma.ca_woot.m = &ma.cm + return &ma.ca_woot + case 2: + ma.ca_waga.w = &ma.w.waga + ma.ca_waga.m = &ma.cm + return &ma.ca_waga + default: + panic("unreachable") + } +} +func (ma *_Msg3__Assembler) Finish() error { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: Finish cannot be called when in the middle of assembling a key") + case maState_expectValue: + panic("invalid state: Finish cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: Finish cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: Finish cannot be called on an assembler that's already finished") + } + //FIXME check if all required fields are set + ma.state = maState_finished + *ma.m = schema.Maybe_Value + return nil +} +func (ma *_Msg3__Assembler) KeyStyle() ipld.NodeStyle { + return _String__Style{} +} +func (ma *_Msg3__Assembler) ValueStyle(k string) ipld.NodeStyle { + panic("todo structbuilder mapassembler valuestyle") +} + +type _Msg3__KeyAssembler _Msg3__Assembler + +func (_Msg3__KeyAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { + return mixins.StringAssembler{"gendemo.Msg3.KeyAssembler"}.BeginMap(0) +} +func (_Msg3__KeyAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { + return mixins.StringAssembler{"gendemo.Msg3.KeyAssembler"}.BeginList(0) +} +func (na *_Msg3__KeyAssembler) AssignNull() error { + return mixins.StringAssembler{"gendemo.Msg3.KeyAssembler"}.AssignNull() +} +func (_Msg3__KeyAssembler) AssignBool(bool) error { + return mixins.StringAssembler{"gendemo.Msg3.KeyAssembler"}.AssignBool(false) +} +func (_Msg3__KeyAssembler) AssignInt(int) error { + return mixins.StringAssembler{"gendemo.Msg3.KeyAssembler"}.AssignInt(0) +} +func (_Msg3__KeyAssembler) AssignFloat(float64) error { + return mixins.StringAssembler{"gendemo.Msg3.KeyAssembler"}.AssignFloat(0) +} +func (ka *_Msg3__KeyAssembler) AssignString(k string) error { + if ka.state != maState_midKey { + panic("misuse: KeyAssembler held beyond its valid lifetime") + } + switch k { + case "whee": + if ka.s&fieldBit__Msg3_Whee != 0 { + return ipld.ErrRepeatedMapKey{&fieldName__Msg3_Whee} + } + ka.s += fieldBit__Msg3_Whee + ka.state = maState_expectValue + ka.f = 0 + case "woot": + if ka.s&fieldBit__Msg3_Woot != 0 { + return ipld.ErrRepeatedMapKey{&fieldName__Msg3_Woot} + } + ka.s += fieldBit__Msg3_Woot + ka.state = maState_expectValue + ka.f = 1 + case "waga": + if ka.s&fieldBit__Msg3_Waga != 0 { + return ipld.ErrRepeatedMapKey{&fieldName__Msg3_Waga} + } + ka.s += fieldBit__Msg3_Waga + ka.state = maState_expectValue + ka.f = 2 + default: + return ipld.ErrInvalidKey{TypeName: "gendemo.Msg3", Key: &_String{k}} + } + return nil +} +func (_Msg3__KeyAssembler) AssignBytes([]byte) error { + return mixins.StringAssembler{"gendemo.Msg3.KeyAssembler"}.AssignBytes(nil) +} +func (_Msg3__KeyAssembler) AssignLink(ipld.Link) error { + return mixins.StringAssembler{"gendemo.Msg3.KeyAssembler"}.AssignLink(nil) +} +func (ka *_Msg3__KeyAssembler) AssignNode(v ipld.Node) error { + if v2, err := v.AsString(); err != nil { + return err + } else { + return ka.AssignString(v2) + } +} +func (_Msg3__KeyAssembler) Style() ipld.NodeStyle { + return _String__Style{} +} +func (Msg3) Type() schema.Type { + return nil /*TODO:typelit*/ +} +func (n Msg3) Representation() ipld.Node { + return (*_Msg3__Repr)(n) +} + +type _Msg3__Repr _Msg3 + +var ( + fieldName__Msg3_Whee_serial = _String{"whee"} + fieldName__Msg3_Woot_serial = _String{"woot"} + fieldName__Msg3_Waga_serial = _String{"waga"} +) +var _ ipld.Node = &_Msg3__Repr{} + +func (_Msg3__Repr) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Map +} +func (n *_Msg3__Repr) LookupString(key string) (ipld.Node, error) { + switch key { + case "whee": + return n.whee.Representation(), nil + case "woot": + return n.woot.Representation(), nil + case "waga": + return n.waga.Representation(), nil + default: + return nil, schema.ErrNoSuchField{Type: nil /*TODO*/, FieldName: key} + } +} +func (n *_Msg3__Repr) Lookup(key ipld.Node) (ipld.Node, error) { + ks, err := key.AsString() + if err != nil { + return nil, err + } + return n.LookupString(ks) +} +func (_Msg3__Repr) LookupIndex(idx int) (ipld.Node, error) { + return mixins.Map{"gendemo.Msg3.Repr"}.LookupIndex(0) +} +func (n _Msg3__Repr) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { + return n.LookupString(seg.String()) +} +func (n *_Msg3__Repr) MapIterator() ipld.MapIterator { + return &_Msg3__ReprMapItr{n, 0} +} + +type _Msg3__ReprMapItr struct { + n *_Msg3__Repr + idx int +} + +func (itr *_Msg3__ReprMapItr) Next() (k ipld.Node, v ipld.Node, _ error) { + if itr.idx >= 3 { + return nil, nil, ipld.ErrIteratorOverread{} + } + switch itr.idx { + case 0: + k = &fieldName__Msg3_Whee_serial + v = itr.n.whee.Representation() + case 1: + k = &fieldName__Msg3_Woot_serial + v = itr.n.woot.Representation() + case 2: + k = &fieldName__Msg3_Waga_serial + v = itr.n.waga.Representation() + default: + panic("unreachable") + } + itr.idx++ + return +} +func (itr *_Msg3__ReprMapItr) Done() bool { + return itr.idx >= 3 +} +func (_Msg3__Repr) ListIterator() ipld.ListIterator { + return nil +} +func (rn *_Msg3__Repr) Length() int { + l := 3 + return l +} +func (_Msg3__Repr) IsUndefined() bool { + return false +} +func (_Msg3__Repr) IsNull() bool { + return false +} +func (_Msg3__Repr) AsBool() (bool, error) { + return mixins.Map{"gendemo.Msg3.Repr"}.AsBool() +} +func (_Msg3__Repr) AsInt() (int, error) { + return mixins.Map{"gendemo.Msg3.Repr"}.AsInt() +} +func (_Msg3__Repr) AsFloat() (float64, error) { + return mixins.Map{"gendemo.Msg3.Repr"}.AsFloat() +} +func (_Msg3__Repr) AsString() (string, error) { + return mixins.Map{"gendemo.Msg3.Repr"}.AsString() +} +func (_Msg3__Repr) AsBytes() ([]byte, error) { + return mixins.Map{"gendemo.Msg3.Repr"}.AsBytes() +} +func (_Msg3__Repr) AsLink() (ipld.Link, error) { + return mixins.Map{"gendemo.Msg3.Repr"}.AsLink() +} +func (_Msg3__Repr) Style() ipld.NodeStyle { + return _Msg3__ReprStyle{} +} + +type _Msg3__ReprStyle struct{} + +func (_Msg3__ReprStyle) NewBuilder() ipld.NodeBuilder { + var nb _Msg3__ReprBuilder + nb.Reset() + return &nb +} + +type _Msg3__ReprBuilder struct { + _Msg3__ReprAssembler +} + +func (nb *_Msg3__ReprBuilder) Build() ipld.Node { + if *nb.m != schema.Maybe_Value { + panic("invalid state: cannot call Build on an assembler that's not finished") + } + return nb.w +} +func (nb *_Msg3__ReprBuilder) Reset() { + var w _Msg3 + var m schema.Maybe + *nb = _Msg3__ReprBuilder{_Msg3__ReprAssembler{w: &w, m: &m}} +} + +type _Msg3__ReprAssembler struct { + w *_Msg3 + m *schema.Maybe + state maState + s int + f int + + cm schema.Maybe + ca_whee _Int__ReprAssembler + ca_woot _Int__ReprAssembler + ca_waga _Int__ReprAssembler +} + +func (na *_Msg3__ReprAssembler) reset() { + na.state = maState_initial + na.s = 0 + na.ca_whee.reset() + na.ca_woot.reset() + na.ca_waga.reset() +} +func (na *_Msg3__ReprAssembler) BeginMap(int) (ipld.MapAssembler, 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 na.w == nil { + na.w = &_Msg3{} + } + return na, nil +} +func (_Msg3__ReprAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { + return mixins.MapAssembler{"gendemo.Msg3.Repr"}.BeginList(0) +} +func (na *_Msg3__ReprAssembler) AssignNull() error { + switch *na.m { + case allowNull: + *na.m = schema.Maybe_Null + return nil + case schema.Maybe_Absent: + return mixins.MapAssembler{"gendemo.Msg3.Repr.Repr"}.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") +} +func (_Msg3__ReprAssembler) AssignBool(bool) error { + return mixins.MapAssembler{"gendemo.Msg3.Repr"}.AssignBool(false) +} +func (_Msg3__ReprAssembler) AssignInt(int) error { + return mixins.MapAssembler{"gendemo.Msg3.Repr"}.AssignInt(0) +} +func (_Msg3__ReprAssembler) AssignFloat(float64) error { + return mixins.MapAssembler{"gendemo.Msg3.Repr"}.AssignFloat(0) +} +func (_Msg3__ReprAssembler) AssignString(string) error { + return mixins.MapAssembler{"gendemo.Msg3.Repr"}.AssignString("") +} +func (_Msg3__ReprAssembler) AssignBytes([]byte) error { + return mixins.MapAssembler{"gendemo.Msg3.Repr"}.AssignBytes(nil) +} +func (_Msg3__ReprAssembler) AssignLink(ipld.Link) error { + return mixins.MapAssembler{"gendemo.Msg3.Repr"}.AssignLink(nil) +} +func (na *_Msg3__ReprAssembler) AssignNode(v ipld.Node) error { + if v.IsNull() { + return na.AssignNull() + } + if v2, ok := v.(*_Msg3); 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 na.w == nil { + na.w = v2 + *na.m = schema.Maybe_Value + return nil + } + *na.w = *v2 + *na.m = schema.Maybe_Value + return nil + } + if v.ReprKind() != ipld.ReprKind_Map { + return ipld.ErrWrongKind{TypeName: "gendemo.Msg3.Repr", MethodName: "AssignNode", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: v.ReprKind()} + } + itr := v.MapIterator() + for !itr.Done() { + k, v, err := itr.Next() + if err != nil { + return err + } + if err := na.AssembleKey().AssignNode(k); err != nil { + return err + } + if err := na.AssembleValue().AssignNode(v); err != nil { + return err + } + } + return na.Finish() +} +func (_Msg3__ReprAssembler) Style() ipld.NodeStyle { + return _Msg3__ReprStyle{} +} +func (ma *_Msg3__ReprAssembler) valueFinishTidy() bool { + switch ma.f { + case 0: + switch ma.cm { + case schema.Maybe_Value: + ma.cm = schema.Maybe_Absent + ma.state = maState_initial + return true + default: + return false + } + case 1: + switch ma.cm { + case schema.Maybe_Value: + ma.cm = schema.Maybe_Absent + ma.state = maState_initial + return true + default: + return false + } + case 2: + switch ma.cm { + case schema.Maybe_Value: + ma.cm = schema.Maybe_Absent + ma.state = maState_initial + return true + default: + return false + } + default: + panic("unreachable") + } +} +func (ma *_Msg3__ReprAssembler) AssembleEntry(k string) (ipld.NodeAssembler, error) { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleEntry cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleEntry cannot be called on an assembler that's already finished") + } + switch k { + case "whee": + if ma.s&fieldBit__Msg3_Whee != 0 { + return nil, ipld.ErrRepeatedMapKey{&fieldName__Msg3_Whee_serial} + } + ma.s += fieldBit__Msg3_Whee + ma.state = maState_midValue + ma.ca_whee.w = &ma.w.whee + ma.ca_whee.m = &ma.cm + return &ma.ca_whee, nil + case "woot": + if ma.s&fieldBit__Msg3_Woot != 0 { + return nil, ipld.ErrRepeatedMapKey{&fieldName__Msg3_Woot_serial} + } + ma.s += fieldBit__Msg3_Woot + ma.state = maState_midValue + ma.ca_woot.w = &ma.w.woot + ma.ca_woot.m = &ma.cm + return &ma.ca_woot, nil + case "waga": + if ma.s&fieldBit__Msg3_Waga != 0 { + return nil, ipld.ErrRepeatedMapKey{&fieldName__Msg3_Waga_serial} + } + ma.s += fieldBit__Msg3_Waga + ma.state = maState_midValue + ma.ca_waga.w = &ma.w.waga + ma.ca_waga.m = &ma.cm + return &ma.ca_waga, nil + default: + return nil, ipld.ErrInvalidKey{TypeName: "gendemo.Msg3.Repr", Key: &_String{k}} + } +} +func (ma *_Msg3__ReprAssembler) AssembleKey() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleKey cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleKey cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleKey cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleKey cannot be called on an assembler that's already finished") + } + ma.state = maState_midKey + return (*_Msg3__ReprKeyAssembler)(ma) +} +func (ma *_Msg3__ReprAssembler) AssembleValue() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + panic("invalid state: AssembleValue cannot be called when no key is primed") + case maState_midKey: + panic("invalid state: AssembleValue cannot be called when in the middle of assembling a key") + case maState_expectValue: + // carry on + case maState_midValue: + panic("invalid state: AssembleValue cannot be called when in the middle of assembling another value") + case maState_finished: + panic("invalid state: AssembleValue cannot be called on an assembler that's already finished") + } + ma.state = maState_midValue + switch ma.f { + case 0: + ma.ca_whee.w = &ma.w.whee + ma.ca_whee.m = &ma.cm + return &ma.ca_whee + case 1: + ma.ca_woot.w = &ma.w.woot + ma.ca_woot.m = &ma.cm + return &ma.ca_woot + case 2: + ma.ca_waga.w = &ma.w.waga + ma.ca_waga.m = &ma.cm + return &ma.ca_waga + default: + panic("unreachable") + } +} +func (ma *_Msg3__ReprAssembler) Finish() error { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: Finish cannot be called when in the middle of assembling a key") + case maState_expectValue: + panic("invalid state: Finish cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: Finish cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: Finish cannot be called on an assembler that's already finished") + } + //FIXME check if all required fields are set + ma.state = maState_finished + *ma.m = schema.Maybe_Value + return nil +} +func (ma *_Msg3__ReprAssembler) KeyStyle() ipld.NodeStyle { + return _String__Style{} +} +func (ma *_Msg3__ReprAssembler) ValueStyle(k string) ipld.NodeStyle { + panic("todo structbuilder mapassembler repr valuestyle") +} + +type _Msg3__ReprKeyAssembler _Msg3__ReprAssembler + +func (_Msg3__ReprKeyAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { + return mixins.StringAssembler{"gendemo.Msg3.Repr.KeyAssembler"}.BeginMap(0) +} +func (_Msg3__ReprKeyAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { + return mixins.StringAssembler{"gendemo.Msg3.Repr.KeyAssembler"}.BeginList(0) +} +func (na *_Msg3__ReprKeyAssembler) AssignNull() error { + return mixins.StringAssembler{"gendemo.Msg3.Repr.KeyAssembler"}.AssignNull() +} +func (_Msg3__ReprKeyAssembler) AssignBool(bool) error { + return mixins.StringAssembler{"gendemo.Msg3.Repr.KeyAssembler"}.AssignBool(false) +} +func (_Msg3__ReprKeyAssembler) AssignInt(int) error { + return mixins.StringAssembler{"gendemo.Msg3.Repr.KeyAssembler"}.AssignInt(0) +} +func (_Msg3__ReprKeyAssembler) AssignFloat(float64) error { + return mixins.StringAssembler{"gendemo.Msg3.Repr.KeyAssembler"}.AssignFloat(0) +} +func (ka *_Msg3__ReprKeyAssembler) AssignString(k string) error { + if ka.state != maState_midKey { + panic("misuse: KeyAssembler held beyond its valid lifetime") + } + switch k { + case "whee": + if ka.s&fieldBit__Msg3_Whee != 0 { + return ipld.ErrRepeatedMapKey{&fieldName__Msg3_Whee_serial} + } + ka.s += fieldBit__Msg3_Whee + ka.state = maState_expectValue + ka.f = 0 + case "woot": + if ka.s&fieldBit__Msg3_Woot != 0 { + return ipld.ErrRepeatedMapKey{&fieldName__Msg3_Woot_serial} + } + ka.s += fieldBit__Msg3_Woot + ka.state = maState_expectValue + ka.f = 1 + case "waga": + if ka.s&fieldBit__Msg3_Waga != 0 { + return ipld.ErrRepeatedMapKey{&fieldName__Msg3_Waga_serial} + } + ka.s += fieldBit__Msg3_Waga + ka.state = maState_expectValue + ka.f = 2 + default: + return ipld.ErrInvalidKey{TypeName: "gendemo.Msg3.Repr", Key: &_String{k}} + } + return nil +} +func (_Msg3__ReprKeyAssembler) AssignBytes([]byte) error { + return mixins.StringAssembler{"gendemo.Msg3.Repr.KeyAssembler"}.AssignBytes(nil) +} +func (_Msg3__ReprKeyAssembler) AssignLink(ipld.Link) error { + return mixins.StringAssembler{"gendemo.Msg3.Repr.KeyAssembler"}.AssignLink(nil) +} +func (ka *_Msg3__ReprKeyAssembler) AssignNode(v ipld.Node) error { + if v2, err := v.AsString(); err != nil { + return err + } else { + return ka.AssignString(v2) + } +} +func (_Msg3__ReprKeyAssembler) Style() ipld.NodeStyle { + return _String__Style{} +} diff --git a/node/gendemo/tString.go b/node/gendemo/tString.go new file mode 100644 index 00000000..b3a9c8cc --- /dev/null +++ b/node/gendemo/tString.go @@ -0,0 +1,229 @@ +package gendemo + +// Code generated by go-ipld-prime gengo. DO NOT EDIT. + +import ( + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" + "github.com/ipld/go-ipld-prime/schema" +) + +type _String struct{ x string } +type String = *_String + +func (n String) String() string { + return n.x +} +func (_String__Style) fromString(w *_String, v string) error { + *w = _String{v} + return nil +} +func (_String__Style) FromString(v string) (String, error) { + n := _String{v} + return &n, nil +} + +type _String__Maybe struct { + m schema.Maybe + v String +} +type MaybeString = *_String__Maybe + +func (m MaybeString) IsNull() bool { + return m.m == schema.Maybe_Null +} +func (m MaybeString) IsUndefined() bool { + return m.m == schema.Maybe_Absent +} +func (m MaybeString) Exists() bool { + return m.m == schema.Maybe_Value +} +func (m MaybeString) AsNode() ipld.Node { + switch m.m { + case schema.Maybe_Absent: + return ipld.Undef + case schema.Maybe_Null: + return ipld.Null + case schema.Maybe_Value: + return m.v + default: + panic("unreachable") + } +} +func (m MaybeString) Must() String { + if !m.Exists() { + panic("unbox of a maybe rejected") + } + return m.v +} + +var _ ipld.Node = (String)(&_String{}) +var _ schema.TypedNode = (String)(&_String{}) + +func (String) ReprKind() ipld.ReprKind { + return ipld.ReprKind_String +} +func (String) LookupString(string) (ipld.Node, error) { + return mixins.String{"gendemo.String"}.LookupString("") +} +func (String) Lookup(ipld.Node) (ipld.Node, error) { + return mixins.String{"gendemo.String"}.Lookup(nil) +} +func (String) LookupIndex(idx int) (ipld.Node, error) { + return mixins.String{"gendemo.String"}.LookupIndex(0) +} +func (String) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { + return mixins.String{"gendemo.String"}.LookupSegment(seg) +} +func (String) MapIterator() ipld.MapIterator { + return nil +} +func (String) ListIterator() ipld.ListIterator { + return nil +} +func (String) Length() int { + return -1 +} +func (String) IsUndefined() bool { + return false +} +func (String) IsNull() bool { + return false +} +func (String) AsBool() (bool, error) { + return mixins.String{"gendemo.String"}.AsBool() +} +func (String) AsInt() (int, error) { + return mixins.String{"gendemo.String"}.AsInt() +} +func (String) AsFloat() (float64, error) { + return mixins.String{"gendemo.String"}.AsFloat() +} +func (n String) AsString() (string, error) { + return n.x, nil +} +func (String) AsBytes() ([]byte, error) { + return mixins.String{"gendemo.String"}.AsBytes() +} +func (String) AsLink() (ipld.Link, error) { + return mixins.String{"gendemo.String"}.AsLink() +} +func (String) Style() ipld.NodeStyle { + return _String__Style{} +} + +type _String__Style struct{} + +func (_String__Style) NewBuilder() ipld.NodeBuilder { + var nb _String__Builder + nb.Reset() + return &nb +} + +type _String__Builder struct { + _String__Assembler +} + +func (nb *_String__Builder) Build() ipld.Node { + if *nb.m != schema.Maybe_Value { + panic("invalid state: cannot call Build on an assembler that's not finished") + } + return nb.w +} +func (nb *_String__Builder) Reset() { + var w _String + var m schema.Maybe + *nb = _String__Builder{_String__Assembler{w: &w, m: &m}} +} + +type _String__Assembler struct { + w *_String + m *schema.Maybe +} + +func (na *_String__Assembler) reset() {} +func (_String__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { + return mixins.StringAssembler{"gendemo.String"}.BeginMap(0) +} +func (_String__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { + return mixins.StringAssembler{"gendemo.String"}.BeginList(0) +} +func (na *_String__Assembler) AssignNull() error { + switch *na.m { + case allowNull: + *na.m = schema.Maybe_Null + return nil + case schema.Maybe_Absent: + return mixins.StringAssembler{"gendemo.String"}.AssignNull() + case schema.Maybe_Value, schema.Maybe_Null: + panic("invalid state: cannot assign into assembler that's already finished") + } + panic("unreachable") +} +func (_String__Assembler) AssignBool(bool) error { + return mixins.StringAssembler{"gendemo.String"}.AssignBool(false) +} +func (_String__Assembler) AssignInt(int) error { + return mixins.StringAssembler{"gendemo.String"}.AssignInt(0) +} +func (_String__Assembler) AssignFloat(float64) error { + return mixins.StringAssembler{"gendemo.String"}.AssignFloat(0) +} +func (na *_String__Assembler) AssignString(v string) error { + switch *na.m { + case schema.Maybe_Value, schema.Maybe_Null: + panic("invalid state: cannot assign into assembler that's already finished") + } + if na.w == nil { + na.w = &_String{} + } + na.w.x = v + *na.m = schema.Maybe_Value + return nil +} +func (_String__Assembler) AssignBytes([]byte) error { + return mixins.StringAssembler{"gendemo.String"}.AssignBytes(nil) +} +func (_String__Assembler) AssignLink(ipld.Link) error { + return mixins.StringAssembler{"gendemo.String"}.AssignLink(nil) +} +func (na *_String__Assembler) AssignNode(v ipld.Node) error { + if v.IsNull() { + return na.AssignNull() + } + if v2, ok := v.(*_String); ok { + switch *na.m { + case schema.Maybe_Value, schema.Maybe_Null: + panic("invalid state: cannot assign into assembler that's already finished") + } + if na.w == nil { + na.w = v2 + *na.m = schema.Maybe_Value + return nil + } + *na.w = *v2 + *na.m = schema.Maybe_Value + return nil + } + if v2, err := v.AsString(); err != nil { + return err + } else { + return na.AssignString(v2) + } +} +func (_String__Assembler) Style() ipld.NodeStyle { + return _String__Style{} +} +func (String) Type() schema.Type { + return nil /*TODO:typelit*/ +} +func (n String) Representation() ipld.Node { + return (*_String__Repr)(n) +} + +type _String__Repr = _String + +var _ ipld.Node = &_String__Repr{} + +type _String__ReprStyle = _String__Style +type _String__ReprAssembler = _String__Assembler diff --git a/node/gendemo/typeTable.go b/node/gendemo/typeTable.go new file mode 100644 index 00000000..acd3d866 --- /dev/null +++ b/node/gendemo/typeTable.go @@ -0,0 +1,24 @@ +package gendemo + +// Type is a struct embeding a NodeStyle/Type for every Node implementation in this package. +// One of its major uses is to start the construction of a value. +// You can use it like this: +// +// gendemo.Type.YourTypeName.NewBuilder().BeginMap() //... +// +// and: +// +// gendemo.Type.OtherTypeName.NewBuilder().AssignString("x") // ... +// +var Type typeSlab + +type typeSlab struct { + Int _Int__Style + Int__Repr _Int__ReprStyle + Map__String__Msg3 _Map__String__Msg3__Style + Map__String__Msg3__Repr _Map__String__Msg3__ReprStyle + Msg3 _Msg3__Style + Msg3__Repr _Msg3__ReprStyle + String _String__Style + String__Repr _String__ReprStyle +} diff --git a/node/mixins/delimHandling.go b/node/mixins/delimHandling.go new file mode 100644 index 00000000..ed65d82a --- /dev/null +++ b/node/mixins/delimHandling.go @@ -0,0 +1,33 @@ +package mixins + +// This file is a little different than most of its siblings in this package. +// It's not really much of a "mixin". More of a util function junkdrawer. +// +// Implementations of Data Model Nodes are unlikely to need these. +// Implementations of Schema-level Node *are* likely to need these, however. +// +// Our codegen implementation emits calls to these functions. +// (And having these functions in a package that's already an unconditional +// import in files emitted by codegen makes the codegen significantly simpler.) + +import ( + "fmt" + "strings" +) + +// SplitExact is much like strings.Split but will error if the number of +// substrings is other than the expected count. +// +// SplitExact is used by the 'stringjoin' representation for structs. +// +// The 'count' parameter is a length. In other words, if you expect +// the zero'th index to be present in the result, you should ask for +// a count of at least '1'. +// Using this function with 'count' less than 2 is rather strange. +func SplitExact(s string, sep string, count int) ([]string, error) { + ss := strings.Split(s, sep) + if len(ss) != count { + return nil, fmt.Errorf("expected %d instances of the delimiter, found %d", count-1, len(ss)-1) + } + return ss, nil +} diff --git a/node/mixins/delimHandling_test.go b/node/mixins/delimHandling_test.go new file mode 100644 index 00000000..e4bde075 --- /dev/null +++ b/node/mixins/delimHandling_test.go @@ -0,0 +1,34 @@ +package mixins + +import ( + "fmt" + "testing" + + . "github.com/warpfork/go-wish" +) + +func TestSplitExact(t *testing.T) { + type expect struct { + value []string + err error + } + type tcase struct { + s string + sep string + count int + expect expect + } + for _, ent := range []tcase{ + {"", "", 0, expect{[]string{}, nil}}, + {"", ":", 1, expect{[]string{""}, nil}}, + {"x", ":", 1, expect{[]string{"x"}, nil}}, + {"x:y", ":", 2, expect{[]string{"x", "y"}, nil}}, + {"x:y:", ":", 2, expect{nil, fmt.Errorf("expected 1 instances of the delimiter, found 2")}}, + {":x:y", ":", 2, expect{nil, fmt.Errorf("expected 1 instances of the delimiter, found 2")}}, + {"x:y:", ":", 3, expect{[]string{"x", "y", ""}, nil}}, + } { + value, err := SplitExact(ent.s, ent.sep, ent.count) + ent2 := tcase{ent.s, ent.sep, ent.count, expect{value, err}} + Wish(t, ent2, ShouldEqual, ent) + } +} diff --git a/schema/errors.go b/schema/errors.go index d0f908ee..f30d8978 100644 --- a/schema/errors.go +++ b/schema/errors.go @@ -14,5 +14,8 @@ type ErrNoSuchField struct { } func (e ErrNoSuchField) Error() string { + if e.Type == nil { + return fmt.Sprintf("no such field: {typeinfomissing}.%s", e.FieldName) + } return fmt.Sprintf("no such field: %s.%s", e.Type.Name(), e.FieldName) } diff --git a/schema/gen/go/HACKME.md b/schema/gen/go/HACKME.md index 057651a6..d037e8e1 100644 --- a/schema/gen/go/HACKME.md +++ b/schema/gen/go/HACKME.md @@ -21,18 +21,20 @@ versus almost any other tradeoff which affects the outputs, we prioritize the ou of them... while making sure they contain very specific references. This leads to some seemingly redundant code, but good error messages are worth it.) +See [README_behaviors](README_behaviors.md) for notes about the behaviors of the code output by the generator; +this document is about the generator code itself and the design thereof. + Entrypoints ----------- -The `gen_test.go` file is the effective "main" method right now. -It contains substantial amounts of hardcoded testcases. - -Run the tests in the `./_test` subpackage explicitly to make sure the -generated code passes its own interface contracts and tests. +The most important intefaces are all in [`generators.go`](generators.go). -If you want to try hacking together your own generated types, the easiest -way is to use the functions used by gen_test.go -- `EmitFileHeader`, `EmitMinima`, and `EmitEntireType` +The function you're most likely looking for that "does the thing" is the +`Generate(outputPath string, pkgName string, schema.TypeSystem, *AdjunctCfg)` method, +which can be found in the [`generate.go`](generate.go) file. +You can take any of the functions inside of that and use them as well, +if you want more granular control over what content ends up in which files. The eventual plan is be able to drive this whole apparatus around via a CLI which consumes IPLD Schema files. @@ -51,9 +53,9 @@ There are roughly *seven* categories of API to generate per type: - 1: the readonly thing a native caller uses - 2: the builder thing a native caller uses - 3: the readonly typed node -- 4: the builder for typed node +- 4: the builder/assembler for typed node - 5: the readonly representation node -- 6: the builder via representation +- 6: the builder/assembler via representation - 7: and a maybe wrapper (And these are just the ones nominally visible in the exported API surface! @@ -66,29 +68,68 @@ These numbers will be used to describe some further organization. There are three noteworthy types of generator internals: -- `typedNodeGenerator` -- `nodeGenerator` -- `nodebuilderGenerator` +- `TypeGenerator` +- `NodeGenerator` +- `NodebuilderGenerator` The first one is where you start; the latter two do double duty for each type. -Exported types for purpose 1, 2, 3, and 7 are emitted from `typedNodeGenerator` (3 from the embedded `nodeGenerator`). +Exported types for purpose 1, 2, 3, and 7 are emitted from `TypeGenerator` (3 from the embedded `NodeGenerator`). -The exported type for purpose 5 is emitted from another `nodeGenerator` instance. +The exported type for purpose 5 is emitted from another `NodeGenerator` instance. -The exported types for purposes 4 and 6 are emitted from two distinct `nodebuilderGenerator` instances. +The exported types for purposes 4 and 6 are emitted from two distinct `NodebuilderGenerator` instances. -For kinds that have more than one known representation strategy, -there may be more than two implementations of `nodeGenerator` and `nodebuilderGenerator`! -(There's always one for the type-semantics node+builder, -and then one more *for each* representation strategy.) +For every variation in type kind and representation strategy for that type kind, +one type implementing `TypeGenerator` is composed, and it has functions which +yield all the other interfaces for addressing the various purposes. ### How are files and their contents grouped? Most of the files in this package are following a pattern: - for each kind: - - `genKind{Kind}.go` -- has emitters for the native type parts (1, 2, 7). - - `genKind{Kind}Node.go` -- has emitters for the typed node parts (3, 4), and the entrypoint to (5). + - `gen{Kind}.go` -- has emitters for the native type parts (1, 2, 7) and type-level node behaviors (3, 4). - for each representation that kind can have: - - `genKind{Kind}Repr{ReprStrat}.go` -- has emitters for (5, 6). + - `gen{Kind}Repr{ReprStrat}.go` -- has emitters for (5, 6). + +A `mixins` sub-package contains some code which is used and embedded in the generators in this package. +These features are mostly per-kind -- representation kind, not type-level kind. +For example, you'll see "map" behaviors from the mixins package added to "struct" generators. + +### What are all these abbreviations? + +See [HACKME_abbrevs.md](HACKME_abbrevs.md). + +### Code architecture + +See [HACKME_tradeoffs.md](HACKME_tradeoffs.md) for an overview of tradeoffs, +and which priorities we selected in this package. +(There are *many* tradeoffs.) + +See [HACKME_memorylayout.md](HACKME_memorylayout.md) for a (large) amount of +exposition on how this code is designed in order to be allocation-avoidant +and fast in general. + +See [HACKME_templates.md](HACKME_templates.md) for some overview on how we've +used templates, and what forms of reuse and abstraction there are. + +See [HACKME_scalars.md](HACKME_scalars.md) for some discussion of scalars +and (why we generate more of them than you might expect). + +See [HACKME_maybe.md](HACKME_maybe.md) for notes how how the 'maybe' feature +(how we describe `nullable` and `optional` schema features in generated golang code) +has evolved. + + +Testing +------- + +See [HACKME_testing.md](HACKME_testing.md) for some details about how this works. + +In general, try to copy some of the existing tests and get things to suit. + +Be advised that we use the golang plugin feature, and that has some additional +requirements of your development environment than is usual in golang. +(Namely, you have to be on linux and you have to have a c compiler!) + diff --git a/schema/gen/go/HACKME_abbrevs.md b/schema/gen/go/HACKME_abbrevs.md new file mode 100644 index 00000000..23b38f98 --- /dev/null +++ b/schema/gen/go/HACKME_abbrevs.md @@ -0,0 +1,25 @@ +abbreviations +============= + +A lot of abbreviations are used the generated code in the interest of minimizing the size of the output. + +This is a short index of the most common ones: + +- `n` -- **n**ode, of course -- the accessor functions on node implementations usually refer to their 'this' as 'n'. +- `na` -- **n**ode **a**ssembler + - `la` or `ma` may also be seen for **l**ist and **m**ap in some contexts (but may refer to same type being called `na` in other contexts). +- `w` -- **w**ork-in-progress node -- you'll see this in nearly every assembler (e.g. `na.w` is a very common string). + +inside nodes: + +- `x` -- a placeholder for "the thing" for types that contain only one element of data (e.g., the string inside a codegen'd node of string kind). +- `t` -- **t**able -- the slice inside most map nodes that is used for alloc amortizations and maintaining order. +- `m` -- **m**ap -- the actual map inside most map nodes (seealso `t`, which is usually a sibling). + +inside assemblers: + +- `va` -- **v**alue **a**ssembler -- an assembler for values in lists or maps (often embedded in the node asembler, e.g. `ma.va` and `la.va` are common strings). +- `ka` -- **k**ey **a**ssembler -- an assembler for keys in maps (often embedded in the node asembler, e.g. `ma.ka` is a common string). +- `ca_*` -- **c**hild **a**ssembler -- the same concept as `ka` and `va`, but appearing in structs and other types that have differentiated children. +- `cm` -- **c**hild **m**aybe -- a piece of memory sometimes found in a node assembler for statekeeping for child assemblers. +- `m` -- **m**aybe pointer -- a pointer to where an assembler should put a mark when it's finished. (this is often a pointer to a parent structure's 'cm'!) diff --git a/schema/gen/go/HACKME_maybe.md b/schema/gen/go/HACKME_maybe.md new file mode 100644 index 00000000..09beda5b --- /dev/null +++ b/schema/gen/go/HACKME_maybe.md @@ -0,0 +1,392 @@ +How do maybe/nullable/optional work? +==================================== + +(No, this document is not about things that we should "maybe" hack on. +It's about the feature we use to describe `nullable` and `optional` fields +in generated golang code.) + +background +---------- + +You'll need to understand what the `nullable` and `optional` modifiers in IPLD schemas mean. +The https://specs.ipld.io/ site has more content about that. + +### how this works outside of schemas + +There are concepts of null and of absent present in the core `Node` and `NodeAssembler` interfaces. +`Node` specifies `IsNull() bool` and `IsUndefined() bool` predicates; +and `NodeAssembler` specifies an `AssignNull` function. + +There are also singleton values available called `ipld.Null` and `ipld.Undef` +which report true for `IsNull` and `IsUndefined`, respectively. +These singletons can be used by an function that need to return a null or absence indicator. + +There's really no reason for any package full of `Node` implementations need to make their own types for these values, +since the singletons are always fine to use. +However, there's also nothing stopping a `Node` implementation from doing interesting +custom internal memory layouts to describe whether they contain nulls, etc -- +and there's nothing particularly blessed about the `ipld.Null` singleton. +Any value reporting `IsNull` to be `true` must be treated indistinguishably from `ipld.Null`. + +This indistinguishability is bidirectional. +For example, if you have some `myFancyNodeType`, and it answers `IsNull` as `true`, +and you insert this into a `basicnode.Map`, then ask for that value back from the map later... +you're very likely to get `ipld.Null`, and not your concrete value of `myFancyNodeType` back again. +(This contract is important because some node implementations may compress +the concept of null into a bitmask, or otherwise similarly optimize things internally.) + +#### null + +The concept of "null" has a Kind in the IPLD Data Model. +It's implemented by the `ipld.nullNode` type (which has no fields -- it's a "unit" type), +and is exposed as the `ipld.Null` singleton value. + +(More generally, `ipld.Node` can be null by having its `Kind()` method return `ipld.ReprKind_Null`, +and having the `IsNull()` method return `true`. +However, most code prefers to return the `ipld.Null` singleton value whenever it can.) + +Null values can be easily produced: the `AssignNull()` method on `ipld.NodeAssembler` produces nulls; +and many codecs have some concept of null, meaning deserialization can produce them. + +Null values work essentially the same way in both the plain Data Model and when working with Schemas. + +#### absent + +There's also a concept of "absent". +"Absent" is separate and distinct from the concept of "null" -- null is still a _value_; absent just means _nothing there_. + +(Those familiar with javascript might note that javascript also has concepts of "null" versus "undefined". +It's the same idea.) + +Absent is implemented by the `ipld.undefNode` type (which has no fields -- it's a "unit" type), +and is exposed as the `ipld.Undef` singleton value. + +(More generally, an `ipld.Node` can describe itself as containing "absent" by having the `IsUndefined()` method return `true`. +(The `Kind()` method still returns `ipld.ReprKind_Null`, for lack of better option.) +However, most code prefers to return the `ipld.Undef` singleton value whenever it can.) + +Absent values aren't really used at the Data Model level. +If you ask for a map key that isn't present in the map, the lookup method will return `nil` and `ErrNotExists`. + +Absent values *do* show up at the Schema level, however. +Specifically, in structs: a struct can have a field which is `optional`, +one of the values such an optional field may report itself as having is `ipld.Undef`. +This represents when a value *wasn't present* in the serialized form of the struct, +even though the schema lets us know that it could be, and that it's part of the struct's type. +(Accordingly, no `ErrNotExists` is returned for a lookup of that field -- +the field is always considered to _exist_... the value is just _absent_.) +Iterators will also return the field name key, together with `ipld.Undef` as the value. + +However, absent values can't really be *created*. +There's no such thing as an `AssignAbsent` or `AssignUndef` method on the `ipld.NodeAssembler` interface. +Codecs similarly can't produce absent as a value (obviously -- codecs work over `ipld.NodeAssembler`, so how could they?). +Absent values are just produced by implication, when a field is defined, but its value isn't set. + +Despite absent values not being used or produced at the Data Model, we still have methods like `IsUndefined` specified +as part of the `ipld.Node` interface so that it's possible to write code which is generic over +either plain Data Model or Schema data while using just that interface. + +### the above is all regarding generic interfaces + +As long as we're talking about the `ipld.Node` _interface_, +we talk about the `ipld.Null` and `ipld.Undef` singletons, and their contracts in terms of the interface. + +(Part of the reason this works is because an interface, in golang, +comes in two parts: a pointer to the typeinfo of the inhabitant value, +and a pointer to the value itself. +This means anywhere we have an `ipld.Node` return type, we can toss `ipld.Null` +or `ipld.Undef` into it with no additional overhead!) + +When we talk about concrete types, rather than the `ipld.Node` _interface_ -- +as we're going to, in codegen -- it's a different scenario. +We can't just return `ipld.Null` pointers for a `genresult.Foo` value; +if `genresult.Foo` is a concrete type, that's just flat out a compile error. + +So what shall we do? + +We introduce the "maybe" types. + + + +the maybe types +--------------- + +The general rule of "return `ipld.Null` whenever you have a null value" +holds up only as long as our API is returning monomorphized `ipld.Node` interfaces -- +in that situation, `ipld.Null` fits within `ipld.Node`, and there's no trouble. + +This doesn't hold up when we get to codegen. +Or rather, more specifically, it even holds up for codegen... +as long as we're still returning monomorphized `ipld.Node` interfaces (and a decent amount of the API surface still does so). +At the moment we want to return a concrete native type, it breaks. + +We call methods created by codegen that use specific types +(e.g., methods that you _couldn't have_ without codegen) +"speciated" methods. And we do want them! + +So we have to decide how to handle null and absent for these speciated methods. + +### goals of the maybe types + +There are a couple of things we want to accomplish with the maybe types: + +- Be able to have speciated methods that return a specific type (for doc, editor autocomplete, etc purposes). +- Be able to have speciated methods that return specific *concrete* type (i.e. not only do we want to be more specific than `ipld.Node`, we don't want an interface _at all_ -- so that the compiler can do inlining and optimization and so forth). +- Make reading and writing code that uses speciated methods and handles nullable or optional fields be reasonably ergonomic (and as always, this may vary by "taste"). + +And we'll consider one more fourth, bonus goal: + +- It would be nice if the maybe types can clearly discuss whether the type is `(null|value)` vs `(absent|value)` vs `(absent|null|value)`, because this would let the golang compiler help check more of our logical correctness in code written using optionals and nullables. + +### there is only one type generated for each maybe + +For every type generated, there is one maybe type also generated. +(At least this much is clearly necesary to satisfy the goals about "specific types".) + +This means *we dropped the bonus goal* above. +Making `(null|value)` vs `(absent|value)` vs `(absent|null|value)` distinguishable to the golang compiler +would require three *additional* generated types (for obvious reasons) for each type specified by the Schema. +We decided that's simply too onerous. + +(A different codegen project could certainly make a different choice here, though.) + +### the symbol for maybe types + +For some type named `T` generated into a package named `gen`... + +- the main type symbol is `gen.T`; +- the maybe for that type is `gen.MaybeT`; + +Beware that this may spell trouble if your schema contains any types +with names starting in "Maybe". +(You can use adjunct config to change symbols for those types, if necessary.) + +(There are also internal symbols for the same things, +but these are prefixed in such a way as to make collision not a concern.) + +### maybe types don't implement the full Node interface + +The "maybe" types don't implement the full `ipld.Node` interface. +They could have! They don't. + +Arguments that went in favor of implementing `Node`: + +- generally "seem fine" +- certainly makes sense to be able to 'IsNull' on it like any other Node. +- if in practice the maybe is embeded, we can return an internal pointer to it just fine, so there's no obvious runtime perf reason not to. + +Arguments against: + +- it's another type with a ton of methods. or two, or four. + - may increase the binary size. possibly by a significant constant multiplier. + - definitely increases the gsloc size, significantly. +- would it have a 'Type' accessor on it? + - if so, what does it say? +- simply not sure how useful this is! + - istm one will often either be passing the MaybeT to other speciated functions, or, fairly immediately de-maybing it. + - if this is true, the number of times anyone wants to treat it as a Node in practice are near zero. +- does this imply the existence of a _MaybeT__Assembler type, as well? + - binary and gsloc size still drifting up; this needs to justify itself and provide value to be worth it. + - what would be the expected behavior of handing a _MaybeT__Assembler to something like unmarshalling? + - if you have a null in the root, you can describe this with a kinded union, and probably would be better off for it. + - if you have can absent value in the root of a document you're unmarshalling... what? That's called "EOF". + - does a _MaybeT__Assembler show up usefully in the middle of a tree? + - it does not! there's always a _P_ValueAssembler type involved there anyway (this is needed for parent state machine purposes), and it largely delegates to the _T__Assembler, but is already a perfect position to add on the "maybe" semantics if the P type has them for its children. + +The arguments against carried the day. + +### the maybe type is emebbedable + +It's important that the "maybe" types be embeddable, for all the same reasons that +[we normally want embeddable types](./HACKME_memorylayout.md#embed-by-default). + +It's interesting to consider the alternatives, though: + +We could've bitpacked the isUndef or isNull flags for a field into one word at the top of a struct, for example. +But, there are numerous drawbacks to this: + +- the complexity of this is high. +- it would be exposed to anyone who writes addntl code in-package, which is asking for errors. +- the only thing this buys us is *slightly* less resident memory size. + - and long story short: if you look at how many other programming language do this, pareto-wise, no one in the world at large appears to care. +- it only applies to structs! maps or lists would require yet more custom bitpacking of a different arrangement. + +If someone wants to do another codegen project someday, or make PRs to this one, which does choose bitpacking, +it would probably be neat. It's just a lot of effort for a payout that doesn't seem to often be worth it. + +(We also ended up using pointers to a field with a `schema.Maybe` type _heavily_ +in the internals of our codegen outputs, in order to let child and parent assemblers coordinate. +Rebuilding this to work with a bitpacking alignment and yet still be composable enough to do its job... uufdah. Tricky. +It might be possible to use the current system in the assembler state, but flip it bitpack in the resulting immutable nodes, +and thereby get the best of both worlds. If you who reads this is enthusiastic, feel free to explore it.) + +### ...but the user is only exposed to the pointer form + +This is the same story as for the main types: it's covered in +[unexported implementations, exported aliases](./HACKME_memorylayout.md#unexported-implementations-exported-aliases). + +Genenerally, this "shielded" type means you can only have a MaybeT with valid contents, +because no one can ever produce the uninitialized "zero" value of the type. +This means there's no "invalid" state which can kick you in the shins at runtime, +and we generally regard that as a good thing. + +It also just keeps things syntactically simple. +One always refers to "MaybeT"; never with a star. + +### whether or not the maybe's inhabitant type is embedded is based on adjunct config + +Although the maybe type itself is embeddable, its _inhabitant_ may be +either embedded in the maybe type or be a pointer, at your option. + +This is clearest to explain in code: you can have either: + +```go +type MaybeFoo struct { + m schema.Maybe // enum bit for present|absent|null + v Foo // the inhabitant (here, embedded) +} +``` + +or: + +```go +type MaybeFoo struct { + m schema.Maybe // enum bit for present|absent|null + v *Foo // the inhabitant (here, a pointer!) +} +``` + +(Yes, we're talking about a one-character difference in the code.) + +Which of these two forms is generated can be selected by adjunct config. +("Adjunct" config just means: it's not part of the schema; it's part of the +config for this codegen tool.) + +There are advantages to each: + +- the embedded form is ([as usual](./HACKME_memorylayout.md#embed-by-default)), faster for workloads where the value is usually present (it provokes fewer allocations). +- the pointer form may use less memory when the value is absent; it works for cyclic structures; and if assigning a whole subtree at once, it allows faster assignment. + +Also, for cyclic structures, such as `type Foo {String:nullable Foo}`, or `type Bar struct{ recurse optional Bar }`, the pointer form is *required*. +(Otherwise... how big of a slab of memory would we be allocating? Infinite? Nope; compile error.) + +By default, we generate the pointer form. +However, your application may experience significant performance improvements by selectively using the embed form. +Check it out and tune for what's right for your application. + +(FUTURE: we should make more clever defaults: it's reasonable to default to embed form for any type that is of scalar kind.) + + + +implementation detail notes +--------------------------- + +### how state machines and maybes work + +Assemblers for recursive stuff have state machines that are used to insure +orderly transitions between each key and value assembly, +and that a complete entry has been assembled before the next entry or the finish. +(For example, you can't go key-then-key in a map, +nor start a value and then start another value before finishing the first one in a list, +nor finish a map when you've just inserted a key and no value, and so forth.) + +One part of this is straightforward: we simply implement state machines, +using bog-standard patterns around a typed uint and logical transition guards +in all the relevant functions. Done and done. Except... + +How do child assemblers signal to their parent that they've become finished? +Theoretically, easy; in practice, to work efficiently... +This poses a bit of an implementation challenge. + +One obvious solution is to put a callback field in assemblers, and have +the parent assembler supply the child assembler with a callback that can +update the parent's state machine when the child becomes finished. +This is logically correct, but practically, problematic and Not Fast: +it requires generating a closure of some kind which composes the function +pointer with the pointer to that parent assembler: and since this is two words +of memory, it implies an allocation and (unfortunately) a heap escape. +An allocation per child key and value in a recursive structure is unacceptable; +we want to set a _much_ higher bar for performance here. + +So, we move on to less obvious solutions: we're all in the same package here, +so we can twiddle the bits of our neighboring structures quite directly, yes? +What if we just have assemblers contain pointers to a state machine uint, +and they do a fixed-value compare-and-swap when they're done? +This is terrifyingly direct and has no abstractions, yes indeed: but +we do generally assume control all the code in this package for any of our +correctness constraints, so this is in-bounds (if admittedly uncomfortable). + +Now let's combine that with one more concern: nullables. When an assembler +is not at the root of a document, it may need to accept null values. +We could do this by generating distinct assembler types for use in positions +where nulls are allowed; but though such an approach would work, it is bulky. +We'd much rather be able to reuse assembler types in either scenario. + +So, let's have assemblers contain two pointers: +the already-familiar 'w' pointer, and also an 'm' pointer. +The 'm' pointer effectively communicates up whether the child has become finished +when it becomes either 'Maybe_Null' or 'Maybe_Value'. + +We add a few new states to the 'm' value, and use it to hint in both directions: +assemblers will assume nulls are not an acceptable transition *unless* the 'm' +value comes initialized with a hint that we are in a situation where they work. + +The costs here are "some": it's another pointer indirection and memory set. +However, compared to the alternatives, it's pretty good: versus an allocation +(in the callback approach), this is a huge win; and we're even pretty safe to +bet that that pointer indirection is going to land in a cache line already hot. + +You can find the additional magic consts crammed into `schema.Maybe` fields +for this statekeeping during assembly defined in the "minima" file in codegen output. +They are named `midvalue` and `allowNull`. + + + +this could have been different +------------------------------ + +There are many ways this design could've been different: + +### we could have every maybe type implement Node + +As already discussed above, it would cause a lot of extra boilerplate methods, +increasing both the generated code source size and binary size; +but on the plus side, it would've been in some ways arguably more consistent. + +We didn't. + +### we could've generated three maybes per type + +Already discussed above. + +We didn't. + +### we could've designed schemas differently + +A lot of the twists of the design originate from the fact that both `optional` +and `nullable` are both rather special as well as very contextual in IPLD Schemas +(e.g., `optional` is only permitted in a very few special places in a schema). +If we had built a very different type system, maybe things would come out differently. + +Some of this has some exploration in some gists: + +- https://gist.github.com/warpfork/9dd8b68deff2b90f96167c900ea31eec#dubious-soln-drop-nullable-completely-make-inline-anonymous-union-syntax-instead +- https://gist.github.com/warpfork/9dd8b68deff2b90f96167c900ea31eec#soln-change-how-schemas-regard-nullable-and-optional +- https://gist.github.com/warpfork/9dd8b68deff2b90f96167c900ea31eec#soln-support-absent-as-a-discriminator-in-kinded-unions + +But suffice to say, that's a very big topic. + +Optionals and nullables are the way they are because they seemed like useful +concepts for describing the structure of data which has serial forms; +how they map onto any particular programming language (such as Go) was a secondary concern. +This design for a golang library is trying to do its best within that. + +### we could've done X with techinque Y + +Probably, yes :) + +This is just one implementation of codegen for Golang for IPLD Schemas. +Competing implementations that make different choices are absolutely welcome :) + + + diff --git a/schema/gen/go/HACKME_memorylayout.md b/schema/gen/go/HACKME_memorylayout.md index 72eb91ac..fcd984d2 100644 --- a/schema/gen/go/HACKME_memorylayout.md +++ b/schema/gen/go/HACKME_memorylayout.md @@ -8,6 +8,12 @@ For the most part, we try to hide these details; or, failing that, at least make them appear consistent. There's some deeper logic required to *pick* which way we do things, though. +This document was written to describe codegen and all of the tradeoffs here, +but much of it (particularly the details about embedding and internal pointers) +also strongly informed the design of the core NodeAssembler semantics, +and thus also may be useful reading to understand some of the forces that +shaped even the various un-typed node implementations. + Prerequisite understandings @@ -222,16 +228,39 @@ Resultant Design Features We generate a concrete type for each type in the schema. Using a concrete type means methods on it are possible to inline. -This is interesting because most of the methods are "accessors" -- that is, +This is important to us because most of the methods are "accessors" -- that is, a style of function that has a small body and does little work -- and these are precisely the sort of function where inlining can add up. -There is one one downside to using an exported concrete type (rather than -keeping it unexported and hidden behind and exported interface): -it means any code external to the package can produce Golang's natural "zero" -for the type. This is problematic because it's true even if the Golang "zero" -value for the type doesn't correspond to a valid value. -This is an unfortunate but overall practical tradeoff. +### natively-typed methods in addition to the general interface + +We generate two sets of methods: **both** the general interface methods to +comply with Node and NodeBuilder interfaces, **and** also natively-typed +variants of the same methods (e.g. a `Lookup` method for maps that takes +the concrete type key and returns the concrete type value, rather than +taking and returning `Node` interfaces). + +While both sets of methods can accomplish the same end goals, both are needed. +There are two distinct advantages to natively-typed methods; +and at the same time, the need for the general methods is system critical. + +Firstly, to programmers writing code that can use the concrete types, the +natively-typed methods provide more value in the form of compile-time type +checking, autocompletion and other tooling assist opportunities, and +less verbosity. + +Secondly, natively-typed funtions on concrete types can be higher performance: +since they're not [virtual function calls](#virtual-function-calls), we +can expect [inlining](#inlining-functions) to work. We might expect this to +be particularly consequential in builders and in accessor methods, since these +involve numerous calls to methods with small bodies -- precisely the sort of +situation that often substantially benefits from inlining. + +At the same time, it goes without saying that we need the general Node and +NodeBuilder interfaces to be satisfied, so that we can write generic library +code such as reusable traversals, etc. It is not possible to satisfy both +needs with a single set of methods with the Golang typesystem; therefore, +we generate both. ### embed by default @@ -243,131 +272,115 @@ end up filled anyway, so reserving that memory up front is reasonable. (Indeed, unfilled fields are only possible for nullable or optional fields which are implemented as embedded.) -Assignment into embedded fields may have the cost of a memcopy. -(By contrast, if fields were pointers, assigning them would be cheap... -though at the same time, we would've had to pay the allocation cost, elsewhere.) -However, combined with (other tricks)[#child-nodebuilders-point-into-embedded-fields], -a shortcut becomes possible: if we at some point used shared memory as the -scratch space for the child nodebuilder... and it's since been finalized... -and that very same pointer (into ourselves!) is now being assigned to us... -we can cheaply detect that and fastpath it. (This sounds contrived, but it's -actually the common case.) - -### nullable and optional struct fields embed too - -TODO intro - -There is some chance of over-allocation in the event of nullable or optional -fields. We support tuning that via adjunct configuration to the code generator -which allows you to opt in to using pointers for fields; choosing to do this -will of course cause you to lose out on alloc amortization features in exchange. - -TODO also resolve the loops note, at bottom +If assigning whole sub-trees at once, assignment into embedded fields +incurs the cost of a memcopy (whereas by contrast, if fields were pointers, +assigning them would be cheap... it's just that we would've had to pay +a (possibly _extra_) allocation cost elsewhere earlier.) +However, this is usually a worthy trade. +Linear memcpy in practice can be significantly cheaper than extra allocations +(especially if it's one long memcpy vs many allocations); +and if we assume a balance of use cases such as "unmarshal happens more often +than sub-tree-assignment", then it's pretty clear we should prioritize getting +allocation minimization for unmarshal rather than fret sub-tree assignment. ### nodebuilders point to the concrete type We generate NodeBuilder types which contain a pointer to the type they build. +This means we can hold onto the Node pointer when its building is completed, +and discard the NodeBuilder. (Or, reset and reuse the NodeBuilder.) +Garbage collection can apply on the NodeBuilder independently of the lifespan +of the Node it built. + This means a single NodeBuilder and its produced Node will require **two** allocations -- one for the NodeBuilder, and a separate one for the Node. -An alternative would be to embed the concrete Node value in the NodeBuilder, +(An alternative would be to embed the concrete Node value in the NodeBuilder, and return a pointer to when finalizing the creation of the Node; however, because due to the garbage collection semantics around [internal pointers](#internal-pointers), such a design would cause the entirety of the memory needed in the NodeBuilder to remain uncollectable as long as -completed Node is reachable! This would be an unfortunate trade -- -we can do better, and will... via [racking builders](#racking-builders). +completed Node is reachable! This would be an unfortunate trade.) -### child nodebuilders point into embedded fields +While we pay two allocations for the Node and its Builder, we earn that back +in spades via our approach to recursion with +[NodeAssemblers](#nodeassemblers-accumulate-mutations), and specifically, how +[NodeAssemblers embed more NodeAssemblers](#nodeassemblers-embed-nodeassemblers). +Long story short: we pay two allocations, yes. But it's *fixed* at two, +no matter how large and complex the structure is. -TODO this is the critical secret sauce +### nodeassemblers accumulate mutations -### racking builders +The NodeBuilder type is only used at the root of construction of a value. +After that, recursion works with an interface called NodeAssembler isntead. -(This where things start to get decidedly less-than-obvious.) +A NodeAssembler is essentially the same thing as a NodeBuilder, except +_it doesn't return a Node_. -After generating the NodeBuilder for each type, we **additionally** generate -a "racker" type. This "racker" is a struct which embeds the NodeBuilder... -and the racker (and thus NodeBuilder) for each of the fields within a struct. -This lets us amortize the allocations for all the *builders* in the same way -as embedding in the actual value structs let us amortized allocations there. +This means we can use the NodeAssembler interface to describe constructing +the data in the middle of some complex value, and we're not burdened by the +need to be able to return the finished product. (Sufficient state-keeping +and defensive checks to ensure we don't leak mutable references would not +come for free; reducing the number of points we might need to do this makes +it possible to create a more efficient system overall.) -With racking builders, we can amortize all the allocations of working memory -needed for a whole family of NodeBuilders... **and** amortize all the -allocations for the value structures into a second allocation... -and that's it, it's just those two. Further more, the separation means that -once the construction of the Node is done, we can drop all the NodeBuilder -memory and expect it to be immediately garbage collectable. Win! +The documentation on the ipld.NodeAssembler type gives some general +description of this. -The code for this gets a little complex, and the result also carries several -additional limitations to the usage, but it does keep the allocations finite, -and thus makes the overall performance fast. +NodeBuilder types end up being just a NodeAssembler embed, plus a few methods +for exposing the final results and optionally resetting the whole system. -### visibility rules +### nodeassemblers embed nodeassemblers -It's perfectly fine to let builders accumulate mutations... right up until -the moment where a Node is returned. +In addition to each NodeAssembler containing a pointer to the value they modify +(the same as [NodeBuilders](#nodebuilders-point-to-the-concrete-type))... +for assemblers that work with recursive structures, they also embed another +NodeAssembler for each of their child values. -(While it's less than ideal that different nodebuilders might interact with -each other... it's technically not a violation of terms: the one final -concern is whether or not Node immutablity is violated. Experiencing -spooky-action-at-a-distance between NodeBuilder instances is irrelevant.) +This lets us amortize the allocations for all the *assemblers* in the same way +as embedding in the actual value structs let us amortized allocations there. -So, we reach the following rules: +The code for this gets a little complex, and the result also carries several +additional limitations to the usage, but it does keep the allocations finite, +and thus makes the overall performance fast. -- when a NodeBuilder.Build method returns a Node, that memory must be frozen: - - that NodeBuilder of course sets its target pointer to nil, jamming itself; - - no other set methods on the *parent* NodeBuilder may assign to that field; - - and the *parent* NodeBuilder may never return another child NodeBuilder for that field. +(To be more specific, for recursive types that are infinite (namely, maps and +lists; whereas structs and unions are finite), the NodeAssembler embeds +*one* NodeAssembler for all values. (Obviously, we can't embed an infinite +number of them, right?) This leads to a restriction: you can't assemble +multiple children of an infinite recursive value simultaneously.) -This set of rules around visibility lets us do amortized allocations -of a big hunk of working memory, and still comply with the familiar -small-pieces-first creation model of the NodeBuilder interface -by returning piecemeal read-only pointers into that big amortized memory hunk. +### nullable and optional struct fields embed too -In order to satisfy these rules (namely, ensuring we never return a NodeBuilder -that addresses memory that's already been frozen) -- and do so without -consuming linearly more memory to track it! -- maps and lists end up with some -notable limitations: +TODO intro -- Lists can only be appended linearly, not populated in free order. - (This means we can condense the 'isFrozen' attribute to an int offset.) -- Maps can only build one new value at a time. - (Even though we usually handle map contents as pointers, being able to build - more than one value at a time would require unknown amounts of memory for - the any NodeBuilder state after the first, which is undesirable.) -- Structs need no special handling -- they can still be regarded in any order. - (We know how much memory we need at compile time, so we can swallow that.) +There is some chance of over-allocation in the event of nullable or optional +fields. We support tuning that via adjunct configuration to the code generator +which allows you to opt in to using pointers for fields; choosing to do this +will of course cause you to lose out on alloc amortization features in exchange. -### natively-typed methods in addition to the general interface +TODO also resolve the loops note, at bottom -We generate two sets of methods: **both** the general interface methods to -comply with Node and NodeBuilder interfaces, **and** also natively-typed -variants of the same methods (e.g. a `Lookup` method for maps that takes -the concrete type key and returns the concrete type value, rather than -taking and returning `Node` interfaces). +### unexported implementations, exported aliases -While both sets of methods can accomplish the same end goals, both are needed. -There are two distinct advantages to natively-typed methods; -and at the same time, the need for the general methods is system critical. +Our concrete types are unexported. For those that need to be exported, +we export an alias to the pointer type. -Firstly, to programmers writing code that can use the concrete types, the -natively-typed methods provide more value in the form of compile-time type -checking, autocompletion and other tooling assist opportunities, and -less verbosity. +This has an interesting set of effects: -Secondly, natively-typed funtions on concrete types can be higher performance: -since they're not [virtual function calls](#virtual-function-calls), we -can expect [inlining](#inlining-functions) to work. We might expect this to -be particularly consequential in builders and in accessor methods, since these -involve numerous calls to methods with small bodies -- precisely the sort of -situation that often substantially benefits from inlining. +- copy-by-value from outside the package becomes impossible; +- creating zero values from outside the package becomes impossible; +- and yet refering to the type for type assertions remains possible. -At the same time, it goes without saying that we need the general Node and -NodeBuilder interfaces to be satisfied, so that we can write generic library -code such as reusable traversals, etc. It is not possible to satisfy both -needs with a single set of methods with the Golang typesystem. +This addresses one downside to using [concrete implementations](#concrete-implementations): +if the concrete implementation is an exported symbol, it means any code external +to the package can produce Golang's natural "zero" for the type. +This is problematic because it's true even if the Golang "zero" value for the +type doesn't correspond to a valid value. +While keeping an unexported implementation and an exported interface makes +external fabrication of zero values impossible, it breaks inlining. +Exporting an alias of the pointer type, however, strikes both goals at once: +external fabrication of zero values is blocked, and yet inlining works. @@ -394,6 +407,8 @@ Prototypes and research examples can be found in the In particular, the "multihoisting" and "nodeassembler" packages are relevant, containing research that lead to the drafting of this doc, as well as some partially-worked alternative interface drafts. +(You may have to search back through git history to find these directories; +they're removed after some time, when the lessons have been applied.) Tests there include some benchmarks (self-explanitory); some tests based on runtime memory stats inspection; diff --git a/node/gendemo/HACKME_scalars.md b/schema/gen/go/HACKME_scalars.md similarity index 97% rename from node/gendemo/HACKME_scalars.md rename to schema/gen/go/HACKME_scalars.md index 62b4152f..d902af0a 100644 --- a/node/gendemo/HACKME_scalars.md +++ b/schema/gen/go/HACKME_scalars.md @@ -109,6 +109,7 @@ type Int int type Bool bool type Float float type String string +type Bytes bytes ``` #### note about schema syntax @@ -125,6 +126,8 @@ as a type identifier. At any rate, it seems clear that you can mentally capitalize the 's' at any time you see this debatable syntax. +(We should resolve this issue in the specs, which are in the `ipld/specs` repo.) + #### plain scalars appear in specialized method argument types and return types This is the same story as for named scalars. @@ -138,16 +141,18 @@ type Foomp map {String:String} ... then you'll get codegen output code which includes a method on Foomp: ```go -func (x *Foomp) Lookup(k *String) (*String) { /*...*/ } +func (x *Foomp) Lookup(k String) (String) { /*...*/ } ``` +(The exact symbols involved and whether or not they're pointers may vary.) + The type might carry less semantic information than it does when a named scalar shows up in the same position, but we still use a generated type (and a pointer) here for two reasons: first of all, and more simply, consistency; but secondly, for the same performance reasons as applied to named scalars (if we need to treat this value as an `ipld.Node` again in the future, it's much better if we already have a heap pointer rather -than a bare primitive value (`runtime.convT*` functions are not a thing your +than a bare primitive value (`runtime.convT*` functions are often not your favorite thing to see in a pprof flamegraph)). (FUTURE: this is still worth review. We might actually want to use diff --git a/schema/gen/go/HACKME_templates.md b/schema/gen/go/HACKME_templates.md new file mode 100644 index 00000000..3471f1fb --- /dev/null +++ b/schema/gen/go/HACKME_templates.md @@ -0,0 +1,119 @@ +Notes about how Templates are used in this package +================================================== + +This package makes heavy use of go's `text/template`. + +Some of it is not pretty. But beware of trying to trade elegance for legibility. + +An overview of the choices that got us here: + +### string templating is fine + +String templating for code is fine, actually. + +An alternative would be to use the golang AST packages. +While those are nice... for our purposes, they're a bit verbose. +It's not necessarily helpful to have the visual distance between our +generation code and the actual result _increase_ (at least, without a good reason). + +### don't make indirections around uncomplicated munges + +Don't make indirections that aren't needed for simple string operations. +(This goes for both the template funcmap, or in the form of reusable templates.) + +One very common situation is that for some type `T`, there's a related thing to be generated +called `T__Foo`, where "Foo" is some kind of modifier that is completely predictable +and not user-selected nor configurable. + +In this situation: simply conjoining `{{ .T }}__Foo` as string in the template is fine! +Don't turn it into a shell game with functions that make the reader jump around more to see what's happening. +(Even when refactoring, it's easy enough to use simple string replace on these patterns; +extracting a function to enable "changing it in one place" doesn't really add much value.) + +If there's something more complicated going on, or it's user-configurable? +Fine, get more functions involved. But be judicious about it. + +(An earlier draft of the code introduced a new method for each modifier form in the example above. +The result was... just repeating the modifiers in the function name in the template! +It produced more code, yet no additional flexibility. +It is advisable to resist making this mistake again ;)) + +### maintain distinction between **symbol** versus **name** + +- **symbol** is what is used in type and function names in code. +- **name** is the string that comes from the schema; it is never modified nor overridable. + +Symbols can change based on adjunct config. +(In theory, they could also be munged to avoid collisions with language constructs +or favor certain library conventions... however, this is currently not automatic.) + +Names can't change. They're _exactly_ what was written in in the Schema. + +Types and functions and most of the _code_ part of codegen use Symbols. +Error messages, reflectable type information, etc, use Names. + +Symbols may also need to be _generated_ for types that don't have names. +For example, `type Foo struct { field {String:Int} }` might result in codegen +creating *two* symbols: `Foo`, and `Map__String__Int`. + +One way to check that the goal is being satisfied is to consider that +someone just experiencing error messages from a program should not end up exposed to any information about: + +- what language the program is written in, +- or which codegen tool was used (or even *if* a codegen tool was used), +- or what adjunct config was present when codegen was performed. + +(n.b. this consideration does not include errors with stack traces -- then +of course you will unavoidably see symbols appear.) + +### anything that is configurable goes through adjunctCfg; adjunctCfg is accessed via the template funcmap + +For example, all Symbol processing all pipes through an adjunct configuration object. +We make this available in the templates via a funcmap so it's available context-free as a nice tidy pipe syntax. + +### there are several kinds of reuse + +(Oh, boy.) + +It may be wise to read the ["values we can balance"](./HACKME_tradeoffs.md#values-we-can-balance) document before continuing. +There's also a _seventh_ tradeoff to consider, in addition to those from that document: +how much reuse there is in our _template_ code, and (_eighth!_) how _readable_ and maintainable the template code is. +Also, (_ninth!!_) how _fast_ the template code is. + +We've generally favored *all* of the priorities for the output code (speed, size, allocation count, etc) over the niceness of the codegen. +We've also _completely_ disregarded speed of the template code (it's always going to be "fast enough"; you don't run this in a hot loop!). +When there's a tension between readability and reuse, we've often favored readability. +That means sometimes text is outright duplicated, if it seemed like extracting it would make it harder to read the template. + +Here's what we've ended up with: + +- There are mixins from the `node/mixins` package, which save some amount of code and standardize some behaviors. + - These end up in the final result code. + - It doesn't save *much* code, and they generally don't save *any* binary size (because it all gets inlined). + - The consistency is the main virtue of these. + - These are used mainly for error handling (specifically, returning of errors for methods called on nodes that have the wrong kind for that method). +- There are mixins from the `schema/gen/go/mixins` package. + - These are in the template code only -- they don't show up in the final result code. + - These attempt to make it easier to create new 'generator' types. (There's a 'generator' type for each kind-plus-representation.) + - They only attempt to take away some boilerplate, and you don't _have_ to use them. +- There are functions in the template funcmap. + - ... not many of them, though. +- There's the idea of using associated templates (templates that are invoked by other templates). + - There's currently none of this in use. Might it be helpful? Maybe. +- There are functions which apply well-known templates to a generator. + - These compose at the golang level, so it's easy to have the compiler check that they're all in order without running them (unlike templates, which have to be exercised in order to detect even basic problems like "does this named template exist"). + - Many of these assume some pattern of methods on the generator objects. (Not of all these are super well documented.) + - Generators usually call out to one or more of these from within the methods that their interface requires them to have. +- The generator types themselves are usually split into two parts: the mechanisms for type-plus-repr, and just mechanisms for the type. + - The mechanisms for the type alone aren't actually a full generator. The type-plus-repr thing just embeds the type-level semantics in itself. + +*Mostly*, it's general aim has been to keep relatively close to the structure of the code being generated. +When reading a generator, one generally has to do *zero* or *one* jump-to-definition in order to see the fulltext of a template -- no more than that. +(And so far, all those jump-to-definition lookups are on _go code_, not inside the template -- so an IDE can help you.) + +By example: if there are things which turn out common between _representation kinds_, +those will probably end up in a function containing a well-known template, +and that will end up being called from the generator type in one of the functions its required to have per its interface contract. + +This all probably has plenty of room for improvement! +But know you know the reasoning that got things to be in the shape they are. diff --git a/schema/gen/go/HACKME_testing.md b/schema/gen/go/HACKME_testing.md new file mode 100644 index 00000000..6e971ccb --- /dev/null +++ b/schema/gen/go/HACKME_testing.md @@ -0,0 +1,91 @@ +Testing Generated Code +====================== + +Getting nice testing scenarios for generated code is tricky. + +There are three major phases of testing we can do: + +1. unit tests on the codegen tooling itself +2. tests that the emitted generated code can compile +3. behavioral tests on the emitted generated code + +Groups 1 and 2 are pretty easy (and we don't have a lot of group 1, +because group 2 covers it pretty nicely). + +Group 3, however, is tricky. +The rest of the document will talk more about this kind of testing. + + +Behavioral Tests +---------------- + +### Behavioral tests are run via plugins + +This package does some fancy footwork with the golang `plugin` system +and `go build -buildmode=plugin` in order to compile and load the +generated code into the same memory as the test process, +thereby letting us do behavioral tests on the gen'd code quite seamlessly. + +This does have some downsides -- namely, the `plugin` system requires +the use of `cgo`. This means you'll need more things installed on your +host system than just the go toolchain -- you might need `gcc` and friends too. +The `plugin` system also (at time of writing) doesn't support windows. +You can skip the behavioral tests if this is a problem: see the next section. + +### Skipping the behavioral tests + +You can skip behavioral tests by adding a build tag of `"skipgenbehavtests"`. +They'll also be automatically skipped if you're running in `"!cgo"` mode -- +however, the `go` tools don't automatically set `"!cgo"` just because it +doesn't have enough tools, so you'll still need to be explicit about this. + +The ability of the generated code to be compiled will still be checked, +even if the behavioral tests are skipped. + +You can grep through your test output for "bhvtest=skip" to see at-a-glance +if the behavioral tests are being skipped. + +### Plugins don't jive with race detection + +Long story short: if running tests with the race detector, skip the gen tests. +Any `go test -race` is going to need `go test -race -tags 'skipgenbehavtests'`. + +The go plugin system requires the plugin and host process have the same "runtime". +The way '-race' works makes for an effectively distinct runtime. + +### Alternatives to plugins + +Are there other ways this could be done? Well, surely. There always are. + +Invoking 'go test' as a subprocess is usually central to alternative ideas, +but this has several downsides I haven't yet figured out how to counter: + +- it tends to result in very difficult-to-wrangle output; +- it ends up imposing a lot of constraints on file organization, + which in turn makes writing tests into a very high-friction endeavour; +- passing down flags to the test process (e.g., '-v' and friends) + begins to require additional infrastructure; +- some flags such as '-run' are even yet more difficult to pass down usefully; +- every single behavioral test has to have an exported top-level function, + making some things that are trivial with closures now difficult... +- You get the idea. + +You can see some attempts around +commit 79de0e26469f0d2899c813a2c70d921fe5946f23 and its halfdozen or so +parents; remarks can be found in the git commit messages. + +There are probably yet more variations in ways files and functions could +be reorganized, particularly to minimize the downsides of the file and +package splitting requirements, but if you're looking at this scenario and +wanting to propose one... Do read those commits to avoid getting into a +Chesterton's Fence situation, and kindly try it before proposing it. +Not all of the hurdles are immediately obvious. + +### Plugins are only used in testing + +This might not need a clarification statement, but just in case it does: +**plugins** (and by extention, cgo) **are not necessary** +for **doing** codegen nor for **using** the resulting generated code. +They are _only_ used for our testing of the codegen tooling +(and specifically, at that, for the behavioral tests). +We would not foist a dependency like cgo on codegen users. diff --git a/node/gendemo/HACKME_tradeoffs.md b/schema/gen/go/HACKME_tradeoffs.md similarity index 100% rename from node/gendemo/HACKME_tradeoffs.md rename to schema/gen/go/HACKME_tradeoffs.md diff --git a/schema/gen/go/HACKME_wip.md b/schema/gen/go/HACKME_wip.md new file mode 100644 index 00000000..dc00bf3f --- /dev/null +++ b/schema/gen/go/HACKME_wip.md @@ -0,0 +1,33 @@ + +### absent values + +The handling of absent values is still not consistent. + +Currently: + +- reading (via accessors or iterators) yields `ipld.Undef` values for absent fields +- putting those ipld.Undef values via NodeAssembler.AssignNode will result in `ErrWrongKind`. +- *the recursive copies embedded in AssignNode methods don't handle undefs either_. + +The first two are defensible and consistent (if not necessarily ergonomic). +The third is downright a bug, and needs to be fixed. + +How we fix it is not entirely settled. + +- Option 1: keep the hostility to undef assignment +- Option 2: *require* explicit undef assignment +- Option 3: become indifferent to undef assignment when it's valid +- Option 4: don't yield values that are undef during iteration at all + +Option 3 seems the most preferrable (and least user-hostile). +(Options 1 and 2 create work for end users; +Option 4 has questionable logical consistency.) + +Updating the codegen to do Option 3 needs some work, though. + +It's likely that the way to go about this would involve adding two more valid +bit states to the extended schema.Maybe values: one for allowAbsent (similar to +the existing allowNull), and another for both (for "nullable optional" fields). +Every NodeAssembler would then have to support that, just as they each support allowNull now. + +I think the above design is valid, but it's not implemented nor tested yet. diff --git a/schema/gen/go/README.md b/schema/gen/go/README.md index b64a2704..120207aa 100644 --- a/schema/gen/go/README.md +++ b/schema/gen/go/README.md @@ -8,5 +8,109 @@ It is at present a partially complete proof-of-concept. Use at your own risk. There is not yet a user-facing CLI; you have to write code to use it. +See [README_behaviors](README_behaviors.md) for notes about the behaviors of the code output by the generator. + Check out the [HACKME](HACKME.md) document for more info about the internals, how they're organized, and how to hack on this package. + + +aims +---- + +`gengo` aims to: + +- generate native Golang code +- that faithfully represents the data structuring specified by an IPLD Schema, +- operating efficiently, both in speed (both creating and inspecting) and memory compactness; +- producing a better type system for Golang (we've got unions and enums!) +- that is both powerful and generic (when you need it) +- and minimalist (when you don't), +- with immutable data structures, +- good validation primitives and type-supported safety systems, +- and is friendly to embellishments of other hand-written Golang code. + +Some of these aims should be satisfied. + +Some are still a stretch ;) (we definitely don't have "minimalist" outputs, yet. +Making this reachable by tuning is a goal, however!) + + +completeness +------------ + +Legend: + +- `✔` - supported! +- `✘` - not currently supported. +- `⚠` - not currently supported -- and might not be obvious; be careful. +- `-` - is not applicable +- `?` - feature definition needed! (applies to many of the "native extras" rows -- often there's partial features, but also room for more.) +- ` ` - table is not finished, please refer to the code and help fix the table :) + +| feature | accessors | builders | +|:-------------------------------|:---------:|:--------:| +| structs | ... | ... | +| ... type level | ✔ | ✔ | +| ... native extras | ? | ? | +| ... map representation | ✔ | ✔ | +| ... ... including optional | ✔ | ✔ | +| ... ... including renames | ✔ | ✔ | +| ... ... including implicits | ⚠ | ⚠ | +| ... tuple representation | ✘ | ✘ | +| ... ... including optional | | | +| ... ... including renames | | | +| ... ... including implicits | | | +| ... stringjoin representation | ✔ | ✔ | +| ... ... including optional | - | - | +| ... ... including renames | - | - | +| ... ... including implicits | - | - | +| ... stringpairs representation | ✘ | ✘ | +| ... ... including optional | | | +| ... ... including renames | | | +| ... ... including implicits | | | +| ... listpairs representation | ✘ | ✘ | +| ... ... including optional | | | +| ... ... including renames | | | +| ... ... including implicits | | | + +| feature | accessors | builders | +|:-------------------------------|:---------:|:--------:| +| lists | ... | ... | +| ... type level | ✔ | ✔ | +| ... native extras | ? | ? | +| ... list representation | ✔ | ✔ | + +| feature | accessors | builders | +|:-------------------------------|:---------:|:--------:| +| maps | ... | ... | +| ... type level | ✔ | ✔ | +| ... native extras | ? | ? | +| ... map representation | ✔ | ✔ | +| ... stringpairs representation | ✘ | ✘ | +| ... listpairs representation | ✘ | ✘ | + +| feature | accessors | builders | +|:-------------------------------|:---------:|:--------:| +| unions | ... | ... | +| ... type level | ✘ | ✘ | +| ... keyed representation | ✘ | ✘ | +| ... envelope representation | ✘ | ✘ | +| ... kinded representation | ✘ | ✘ | +| ... inline representation | ✘ | ✘ | +| ... byteprefix representation | ✘ | ✘ | + +| feature | accessors | builders | +|:-------------------------------|:---------:|:--------:| +| strings | ✔ | ✔ | +| bytes | ✔ | ✔ | +| ints | ✔ | ✔ | +| floats | ✔ | ✔ | +| bools | ✔ | ✔ | +| links | ✔ | ✔ | + +| feature | accessors | builders | +|:-------------------------------|:---------:|:--------:| +| enums | ... | ... | +| ... type level | ✘ | ✘ | +| ... string representation | ✘ | ✘ | +| ... int representation | ✘ | ✘ | diff --git a/schema/gen/go/README_behaviors.md b/schema/gen/go/README_behaviors.md new file mode 100644 index 00000000..f2c35f21 --- /dev/null +++ b/schema/gen/go/README_behaviors.md @@ -0,0 +1,38 @@ +Behaviors of Generated Types +============================ + +Types generated by `gogen` obey the rules that already exist for the IPLD Data Model, +and the rules that exist for Schema typed nodes. + +There are some further details of behavior that might be worth noting, though. + +Some of these details aren't necessarily programmer-friendly(!), +and arise from prioritizing performance over other concerns; +so especially watch out for these as you develop against the code output by this tool. + +### retaining assemblers beyond their intended lifespan is not guaranteed to be safe + +There is no promise of nice-to-read errors if you over-hold child assemblers beyond their valid lifespan. +`NodeAssembler` values should not be retained for any longer than they're actively in use. + +- We **do** care about making things fail hard and fast rather than potentially leak inappropriate mutability. +- We do **not** care about making these errors pretty (it's high cost to do so, and code that hits this path is almost certainly statically (and hopefully fairly obviously) wrong). + +In some cases it may also be the case that a `NodeAssembler` that populates the internals of some large structure +may become invalid (because of state transitions that block inappropriate mutability), +and yet become possible to use again later (because of coincidences of how we reuse memory internally for efficiency reasons). +We don't reliably raise errors in some of these situations, for efficiency reasons, but wish we could. +Users of the generated code should not rely on these behaviors: +it results in difficult-to-read code in any case, +and such internal details should not be considered part of the intended public API (e.g., such details may be subject to change without notice). + +### absent values + +Iterating a type-level node with optional fields will yield the field key and the `ipld.Undef` constant as a value. +Getting a such a field will also yield the `ipld.Undef` constant as a value, and will not return a "not found" error. + +Attempting to *assign* an `ipld.Undef` value, however -- +via the `NodeAssembler.AssignNode` function (none of the other function signatures permit expressing this) -- +will result in an `ipld.ErrWrongKind` error. + +// Seealso some unresolved todos in the [HACKME_wip](HACKME_wip.md) document regarding how absent values are handled. diff --git a/schema/gen/go/README_wishes.md b/schema/gen/go/README_wishes.md new file mode 100644 index 00000000..6ec8b14a --- /dev/null +++ b/schema/gen/go/README_wishes.md @@ -0,0 +1,18 @@ +wishes and dreams of other things that could be +=============================================== + +### minimal gen for regular code + +(In other short words: gen without the monomorphization.) + +It'd be neat to have a mode that generates all public fields (shrugging on immutability), +and can also toggle off generating all the data model Node interface satisfaction methods. +This would let us use IPLD Schemas to define types (including enums and unions) and get Golang code out, +and easily use it in programs even where we don't particularly care about the Node interface. +(E.g., suppose we want an enum or a union type -- which Go doesn't naturally have -- but aren't going to use it +for serialization or anything else. We've already got a lot of codegen here; why not make it help there too?) + +It's unclear if we can load that much into this gen project, or if it'd be easier to make another one for this. +The output code would definitely be substantially structurally different; +and it would also tend to be useless/silly to generate different parts of a type system in each of the different modes, +so it's pretty likely that any practical user story would involve different process invocations to use them anyway. diff --git a/schema/gen/go/_test/wow_test.go b/schema/gen/go/_test/wow_test.go deleted file mode 100644 index 3dcacf5f..00000000 --- a/schema/gen/go/_test/wow_test.go +++ /dev/null @@ -1,316 +0,0 @@ -package whee - -import ( - "bytes" - "testing" - - "github.com/polydawn/refmt/json" - "github.com/polydawn/refmt/tok" - - . "github.com/warpfork/go-wish" - - ipld "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/encoding" - "github.com/ipld/go-ipld-prime/fluent" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" - "github.com/ipld/go-ipld-prime/schema" -) - -// TokenSourceBucket acts like a TokenSource by yielding tokens from a pre-made -// slice; and also keeps track of how far it's been read. -type TokenSourceBucket struct { - tokens []tok.Token - read int -} - -func (tb *TokenSourceBucket) Step(yield *tok.Token) (done bool, err error) { - *yield = tb.tokens[tb.read] - tb.read++ - return tb.read > len(tb.tokens), nil -} - -func plz(n ipld.Node, e error) ipld.Node { - if e != nil { - panic(e) - } - return n -} - -func erp(n ipld.Node, e error) interface{} { - if e != nil { - return e - } - return n -} - -func TestScalarUnmarshal(t *testing.T) { - t.Run("string node", func(t *testing.T) { - tb := &TokenSourceBucket{tokens: []tok.Token{ - {Type: tok.TString, Str: "zooooom"}, - }} - nb := String__NodeBuilder() - n, err := encoding.Unmarshal(nb, tb) - Wish(t, err, ShouldEqual, nil) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_String) - Wish(t, fluent.WrapNode(n).AsString(), ShouldEqual, "zooooom") - Wish(t, tb.read, ShouldEqual, 1) - }) -} - -// Test generated structs (n, nb, rn, rnb). -// -// Does not exercise iterators (marshal/unmarshal tests do that heavily anyway). -// -// Yes, it's big. Proding all cases around optionals and nullables is fun. -func TestGeneratedStructs(t *testing.T) { - t.Run("struct with map repr", func(t *testing.T) { - var ( - v0, v1, v2, v3, v4 schema.TypedNode - ) - t.Run("type-level build and read", func(t *testing.T) { - t.Run("all fields set", func(t *testing.T) { - mb, err := Stroct__NodeBuilder().CreateMap() - Require(t, err, ShouldEqual, nil) - mb.Insert(ipldfree.String("f1"), plz(String__NodeBuilder().CreateString("a"))) - mb.Insert(ipldfree.String("f2"), plz(String__NodeBuilder().CreateString("b"))) - mb.Insert(ipldfree.String("f3"), plz(String__NodeBuilder().CreateString("c"))) - mb.Insert(ipldfree.String("f4"), plz(String__NodeBuilder().CreateString("d"))) - n, err := mb.Build() - v0 = n.(schema.TypedNode) - - Wish(t, err, ShouldEqual, nil) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, plz(n.LookupString("f1")), ShouldEqual, plz(String__NodeBuilder().CreateString("a"))) - Wish(t, plz(n.LookupString("f2")), ShouldEqual, plz(String__NodeBuilder().CreateString("b"))) - Wish(t, plz(n.LookupString("f3")), ShouldEqual, plz(String__NodeBuilder().CreateString("c"))) - Wish(t, plz(n.LookupString("f4")), ShouldEqual, plz(String__NodeBuilder().CreateString("d"))) - }) - t.Run("using null nullable", func(t *testing.T) { - mb, err := Stroct__NodeBuilder().CreateMap() - Require(t, err, ShouldEqual, nil) - mb.Insert(ipldfree.String("f1"), plz(String__NodeBuilder().CreateString("a"))) - mb.Insert(ipldfree.String("f2"), plz(String__NodeBuilder().CreateString("b"))) - mb.Insert(ipldfree.String("f3"), plz(String__NodeBuilder().CreateString("c"))) - mb.Insert(ipldfree.String("f4"), ipld.Null) - n, err := mb.Build() - v1 = n.(schema.TypedNode) - - Wish(t, err, ShouldEqual, nil) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, n.Length(), ShouldEqual, 4) - Wish(t, plz(n.LookupString("f1")), ShouldEqual, plz(String__NodeBuilder().CreateString("a"))) - Wish(t, plz(n.LookupString("f2")), ShouldEqual, plz(String__NodeBuilder().CreateString("b"))) - Wish(t, plz(n.LookupString("f3")), ShouldEqual, plz(String__NodeBuilder().CreateString("c"))) - Wish(t, plz(n.LookupString("f4")), ShouldEqual, ipld.Null) - }) - t.Run("using null optional nullable", func(t *testing.T) { - mb, err := Stroct__NodeBuilder().CreateMap() - Require(t, err, ShouldEqual, nil) - mb.Insert(ipldfree.String("f1"), plz(String__NodeBuilder().CreateString("a"))) - mb.Insert(ipldfree.String("f2"), plz(String__NodeBuilder().CreateString("b"))) - mb.Insert(ipldfree.String("f3"), ipld.Null) - mb.Insert(ipldfree.String("f4"), plz(String__NodeBuilder().CreateString("d"))) - n, err := mb.Build() - v2 = n.(schema.TypedNode) - - Wish(t, err, ShouldEqual, nil) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, n.Length(), ShouldEqual, 4) - Wish(t, plz(n.LookupString("f1")), ShouldEqual, plz(String__NodeBuilder().CreateString("a"))) - Wish(t, plz(n.LookupString("f2")), ShouldEqual, plz(String__NodeBuilder().CreateString("b"))) - Wish(t, plz(n.LookupString("f3")), ShouldEqual, ipld.Null) - Wish(t, plz(n.LookupString("f4")), ShouldEqual, plz(String__NodeBuilder().CreateString("d"))) - }) - t.Run("using skipped optional", func(t *testing.T) { - mb, err := Stroct__NodeBuilder().CreateMap() - Require(t, err, ShouldEqual, nil) - mb.Insert(ipldfree.String("f1"), plz(String__NodeBuilder().CreateString("a"))) - mb.Insert(ipldfree.String("f3"), plz(String__NodeBuilder().CreateString("c"))) - mb.Insert(ipldfree.String("f4"), plz(String__NodeBuilder().CreateString("d"))) - n, err := mb.Build() - v3 = n.(schema.TypedNode) - - Wish(t, err, ShouldEqual, nil) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, n.Length(), ShouldEqual, 4) - Wish(t, plz(n.LookupString("f1")), ShouldEqual, plz(String__NodeBuilder().CreateString("a"))) - Wish(t, plz(n.LookupString("f2")), ShouldEqual, ipld.Undef) - Wish(t, plz(n.LookupString("f3")), ShouldEqual, plz(String__NodeBuilder().CreateString("c"))) - Wish(t, plz(n.LookupString("f4")), ShouldEqual, plz(String__NodeBuilder().CreateString("d"))) - }) - t.Run("using skipped optional nullable", func(t *testing.T) { - mb, err := Stroct__NodeBuilder().CreateMap() - Require(t, err, ShouldEqual, nil) - mb.Insert(ipldfree.String("f1"), plz(String__NodeBuilder().CreateString("a"))) - mb.Insert(ipldfree.String("f2"), plz(String__NodeBuilder().CreateString("b"))) - mb.Insert(ipldfree.String("f4"), plz(String__NodeBuilder().CreateString("d"))) - n, err := mb.Build() - v4 = n.(schema.TypedNode) - - Wish(t, err, ShouldEqual, nil) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, n.Length(), ShouldEqual, 4) - Wish(t, plz(n.LookupString("f1")), ShouldEqual, plz(String__NodeBuilder().CreateString("a"))) - Wish(t, plz(n.LookupString("f2")), ShouldEqual, plz(String__NodeBuilder().CreateString("b"))) - Wish(t, plz(n.LookupString("f3")), ShouldEqual, ipld.Undef) - Wish(t, plz(n.LookupString("f4")), ShouldEqual, plz(String__NodeBuilder().CreateString("d"))) - }) - }) - t.Run("representation read", func(t *testing.T) { - t.Run("all fields set", func(t *testing.T) { - n := v0.Representation() - - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, n.Length(), ShouldEqual, 4) - Wish(t, plz(n.LookupString("f1")), ShouldEqual, plz(String__NodeBuilder().CreateString("a"))) - Wish(t, plz(n.LookupString("f2")), ShouldEqual, plz(String__NodeBuilder().CreateString("b"))) - Wish(t, plz(n.LookupString("f3")), ShouldEqual, plz(String__NodeBuilder().CreateString("c"))) - Wish(t, plz(n.LookupString("f4")), ShouldEqual, plz(String__NodeBuilder().CreateString("d"))) - }) - t.Run("using null nullable", func(t *testing.T) { - n := v1.Representation() - - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, n.Length(), ShouldEqual, 4) - Wish(t, plz(n.LookupString("f1")), ShouldEqual, plz(String__NodeBuilder().CreateString("a"))) - Wish(t, plz(n.LookupString("f2")), ShouldEqual, plz(String__NodeBuilder().CreateString("b"))) - Wish(t, plz(n.LookupString("f3")), ShouldEqual, plz(String__NodeBuilder().CreateString("c"))) - Wish(t, plz(n.LookupString("f4")), ShouldEqual, ipld.Null) - }) - t.Run("using null optional nullable", func(t *testing.T) { - n := v2.Representation() - - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, n.Length(), ShouldEqual, 4) - Wish(t, plz(n.LookupString("f1")), ShouldEqual, plz(String__NodeBuilder().CreateString("a"))) - Wish(t, plz(n.LookupString("f2")), ShouldEqual, plz(String__NodeBuilder().CreateString("b"))) - Wish(t, plz(n.LookupString("f3")), ShouldEqual, ipld.Null) - Wish(t, plz(n.LookupString("f4")), ShouldEqual, plz(String__NodeBuilder().CreateString("d"))) - }) - t.Run("using skipped optional", func(t *testing.T) { - n := v3.Representation() - - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, n.Length(), ShouldEqual, 3) // note this is shorter, even though it's not at the type level! - Wish(t, plz(n.LookupString("f1")), ShouldEqual, plz(String__NodeBuilder().CreateString("a"))) - Wish(t, erp(n.LookupString("f2")), ShouldEqual, ipld.ErrNotExists{ipld.PathSegmentOfString("f2")}) - Wish(t, plz(n.LookupString("f3")), ShouldEqual, plz(String__NodeBuilder().CreateString("c"))) - Wish(t, plz(n.LookupString("f4")), ShouldEqual, plz(String__NodeBuilder().CreateString("d"))) - }) - t.Run("using skipped optional nullable", func(t *testing.T) { - n := v4.Representation() - - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, n.Length(), ShouldEqual, 3) // note this is shorter, even though it's not at the type level! - Wish(t, plz(n.LookupString("f1")), ShouldEqual, plz(String__NodeBuilder().CreateString("a"))) - Wish(t, plz(n.LookupString("f2")), ShouldEqual, plz(String__NodeBuilder().CreateString("b"))) - Wish(t, erp(n.LookupString("f3")), ShouldEqual, ipld.ErrNotExists{ipld.PathSegmentOfString("f3")}) - Wish(t, plz(n.LookupString("f4")), ShouldEqual, plz(String__NodeBuilder().CreateString("d"))) - }) - // TODO will need even more cases to probe implicits - }) - t.Run("representation build", func(t *testing.T) { - t.Run("all fields set", func(t *testing.T) { - mb, err := Stroct__ReprBuilder().CreateMap() - Require(t, err, ShouldEqual, nil) - mb.Insert(ipldfree.String("f1"), plz(String__NodeBuilder().CreateString("a"))) - mb.Insert(ipldfree.String("f2"), plz(String__NodeBuilder().CreateString("b"))) - mb.Insert(ipldfree.String("f3"), plz(String__NodeBuilder().CreateString("c"))) - mb.Insert(ipldfree.String("f4"), plz(String__NodeBuilder().CreateString("d"))) - n, err := mb.Build() - - Wish(t, err, ShouldEqual, nil) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, n, ShouldEqual, v0) - }) - t.Run("using null nullable", func(t *testing.T) { - mb, err := Stroct__ReprBuilder().CreateMap() - Require(t, err, ShouldEqual, nil) - mb.Insert(ipldfree.String("f1"), plz(String__NodeBuilder().CreateString("a"))) - mb.Insert(ipldfree.String("f2"), plz(String__NodeBuilder().CreateString("b"))) - mb.Insert(ipldfree.String("f3"), plz(String__NodeBuilder().CreateString("c"))) - mb.Insert(ipldfree.String("f4"), ipld.Null) - n, err := mb.Build() - - Wish(t, err, ShouldEqual, nil) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, n.Length(), ShouldEqual, 4) - Wish(t, n, ShouldEqual, v1) - }) - t.Run("using null optional nullable", func(t *testing.T) { - mb, err := Stroct__ReprBuilder().CreateMap() - Require(t, err, ShouldEqual, nil) - mb.Insert(ipldfree.String("f1"), plz(String__NodeBuilder().CreateString("a"))) - mb.Insert(ipldfree.String("f2"), plz(String__NodeBuilder().CreateString("b"))) - mb.Insert(ipldfree.String("f3"), ipld.Null) - mb.Insert(ipldfree.String("f4"), plz(String__NodeBuilder().CreateString("d"))) - n, err := mb.Build() - - Wish(t, err, ShouldEqual, nil) - Wish(t, n, ShouldEqual, v2) - }) - t.Run("using skipped optional", func(t *testing.T) { - mb, err := Stroct__ReprBuilder().CreateMap() - Require(t, err, ShouldEqual, nil) - mb.Insert(ipldfree.String("f1"), plz(String__NodeBuilder().CreateString("a"))) - mb.Insert(ipldfree.String("f3"), plz(String__NodeBuilder().CreateString("c"))) - mb.Insert(ipldfree.String("f4"), plz(String__NodeBuilder().CreateString("d"))) - n, err := mb.Build() - - Wish(t, err, ShouldEqual, nil) - Wish(t, n, ShouldEqual, v3) - }) - t.Run("using skipped optional nullable", func(t *testing.T) { - mb, err := Stroct__ReprBuilder().CreateMap() - Require(t, err, ShouldEqual, nil) - mb.Insert(ipldfree.String("f1"), plz(String__NodeBuilder().CreateString("a"))) - mb.Insert(ipldfree.String("f2"), plz(String__NodeBuilder().CreateString("b"))) - mb.Insert(ipldfree.String("f4"), plz(String__NodeBuilder().CreateString("d"))) - n, err := mb.Build() - - Wish(t, err, ShouldEqual, nil) - Wish(t, n, ShouldEqual, v4) - - }) - // TODO will need even more cases to probe implicits - }) - }) -} - -func TestStructUnmarshal(t *testing.T) { - t.Run("stroct", func(t *testing.T) { - t.Run("all fields set", func(t *testing.T) { - tb := &TokenSourceBucket{tokens: []tok.Token{ - {Type: tok.TMapOpen, Length: 4}, - {Type: tok.TString, Str: "f1"}, {Type: tok.TString, Str: "a"}, - {Type: tok.TString, Str: "f2"}, {Type: tok.TString, Str: "b"}, - {Type: tok.TString, Str: "f3"}, {Type: tok.TString, Str: "c"}, - {Type: tok.TString, Str: "f4"}, {Type: tok.TString, Str: "d"}, - {Type: tok.TMapClose}, - }} - nb := Stroct__NodeBuilder() - n, err := encoding.Unmarshal(nb, tb) - - Require(t, err, ShouldEqual, nil) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, plz(n.LookupString("f1")), ShouldEqual, plz(String__NodeBuilder().CreateString("a"))) - Wish(t, plz(n.LookupString("f2")), ShouldEqual, plz(String__NodeBuilder().CreateString("b"))) - Wish(t, plz(n.LookupString("f3")), ShouldEqual, plz(String__NodeBuilder().CreateString("c"))) - Wish(t, plz(n.LookupString("f4")), ShouldEqual, plz(String__NodeBuilder().CreateString("d"))) - }) - }) -} - -func BenchmarkStructUnmarshal(b *testing.B) { - bs := []byte(`{"f1":"a","f2":"b","f3":"c","f4":"d"}`) - for i := 0; i < b.N; i++ { - nb := Stroct__NodeBuilder() - n, err := encoding.Unmarshal(nb, json.NewDecoder(bytes.NewReader(bs))) - if err != nil { - panic(err) - } - sink = n - } -} - -var sink interface{} diff --git a/schema/gen/go/adjunctCfg.go b/schema/gen/go/adjunctCfg.go new file mode 100644 index 00000000..b8dad883 --- /dev/null +++ b/schema/gen/go/adjunctCfg.go @@ -0,0 +1,66 @@ +package gengo + +import ( + "strings" + + "github.com/ipld/go-ipld-prime/schema" +) + +type FieldTuple struct { + TypeName schema.TypeName + FieldName string +} + +type AdjunctCfg struct { + typeSymbolOverrides map[schema.TypeName]string + fieldSymbolLowerOverrides map[FieldTuple]string + fieldSymbolUpperOverrides map[FieldTuple]string + maybeUsesPtr map[schema.TypeName]bool // treat absent as true + + // note: PkgName doesn't appear in here, because it's... + // not adjunct data. it's a generation invocation parameter. +} + +// TypeSymbol returns the symbol for a type; +// by default, it's the same string as its name in the schema, +// but it can be overriden. +// +// This is the base, unembellished symbol. +// It's frequently augmented: +// prefixing an underscore to make it unexported; +// suffixing "__Something" to make the name of a supporting type; +// etc. +// (Most such augmentations are not configurable.) +func (cfg *AdjunctCfg) TypeSymbol(t schema.Type) string { + if x, ok := cfg.typeSymbolOverrides[t.Name()]; ok { + return x + } + return string(t.Name()) // presumed already upper +} + +func (cfg *AdjunctCfg) FieldSymbolLower(f schema.StructField) string { + if x, ok := cfg.fieldSymbolLowerOverrides[FieldTuple{f.Type().Name(), f.Name()}]; ok { + return x + } + return f.Name() // presumed already lower +} + +func (cfg *AdjunctCfg) FieldSymbolUpper(f schema.StructField) string { + if x, ok := cfg.fieldSymbolUpperOverrides[FieldTuple{f.Type().Name(), f.Name()}]; ok { + return x + } + return strings.Title(f.Name()) +} + +func (cfg *AdjunctCfg) MaybeUsesPtr(t schema.Type) bool { + if x, ok := cfg.maybeUsesPtr[t.Name()]; ok { + return x + } + // FUTURE: we could make this default vary based on sizeof the type. + // It's generally true that for scalars it should be false by default; and that's easy to do. + // It would actually *remove* special cases from the prelude, which would be a win. + // Maps and lists should also probably default off...? + // (I have a feeling something might get touchy there. Review when implementing those.) + // Perhaps structs and unions are the only things likely to benefit from pointers. + return true +} diff --git a/schema/gen/go/gen.go b/schema/gen/go/gen.go deleted file mode 100644 index c423cb9b..00000000 --- a/schema/gen/go/gen.go +++ /dev/null @@ -1,193 +0,0 @@ -package gengo - -import ( - "fmt" - "io" -) - -// typedNodeGenerator declares a standard names for a bunch of methods for generating -// code for our schema types. There's still numerous places where other casts -// to more specific interfaces will be required (so, technically, it's not a -// very powerful interface; it's not so much that the abstractions leak as that -// the floodgates are outright open), but this at least forces consistency onto -// the parts where we can. -// -// All Emit{foo} methods should emit one trailing and one leading linebreak, or, -// nothing (e.g. string kinds don't need to produce a dummy map iterator, so -// such a method can just emit nothing, and the extra spacing between sections -// shouldn't accumulate). -// -// None of these methods return error values because we panic in this package. -// -type typedNodeGenerator interface { - - // -- the natively-typed apis --> - // (might be more readable to group these in another interface and have it - // return a `typedNodeGenerator` with the rest? but structurally same.) - - EmitNativeType(io.Writer) - EmitNativeAccessors(io.Writer) // depends on the kind -- field accessors for struct, typed iterators for map, etc. - EmitNativeBuilder(io.Writer) // typically emits some kind of struct that has a Build method. - EmitNativeMaybe(io.Writer) // a pointer-free 'maybe' mechanism is generated for all types. - - // -- the schema.TypedNode.Type method and vars --> - - EmitTypedNodeMethodType(io.Writer) // these emit dummies for now - - // -- all node methods --> - // (and note that the nodeBuilder for this one should be the "semantic" one, - // e.g. it *always* acts like a map for structs, even if the repr is different.) - - nodeGenerator - - // -- and the representation and its node and nodebuilder --> - - EmitTypedNodeMethodRepresentation(io.Writer) - 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) - EmitNodeMethodLookupString(io.Writer) - EmitNodeMethodLookup(io.Writer) - EmitNodeMethodLookupIndex(io.Writer) - EmitNodeMethodLookupSegment(io.Writer) - EmitNodeMethodMapIterator(io.Writer) // also iterator itself - EmitNodeMethodListIterator(io.Writer) // also iterator itself - EmitNodeMethodLength(io.Writer) - EmitNodeMethodIsUndefined(io.Writer) - EmitNodeMethodIsNull(io.Writer) - EmitNodeMethodAsBool(io.Writer) - EmitNodeMethodAsInt(io.Writer) - EmitNodeMethodAsFloat(io.Writer) - EmitNodeMethodAsString(io.Writer) - EmitNodeMethodAsBytes(io.Writer) - EmitNodeMethodAsLink(io.Writer) - EmitNodeMethodNodeBuilder(io.Writer) - - GetNodeBuilderGen() nodebuilderGenerator -} - -type nodebuilderGenerator interface { - EmitNodebuilderType(io.Writer) - EmitNodebuilderConstructor(io.Writer) - - EmitNodebuilderMethodCreateMap(io.Writer) // also mapbuilder itself - EmitNodebuilderMethodAmendMap(io.Writer) // also listbuilder itself - EmitNodebuilderMethodCreateList(io.Writer) - EmitNodebuilderMethodAmendList(io.Writer) - EmitNodebuilderMethodCreateNull(io.Writer) - EmitNodebuilderMethodCreateBool(io.Writer) - EmitNodebuilderMethodCreateInt(io.Writer) - EmitNodebuilderMethodCreateFloat(io.Writer) - EmitNodebuilderMethodCreateString(io.Writer) - EmitNodebuilderMethodCreateBytes(io.Writer) - EmitNodebuilderMethodCreateLink(io.Writer) -} - -// EmitFileHeader emits a baseline package header that will -// allow a file with a generated type to compile -func EmitFileHeader(packageName string, w io.Writer) { - fmt.Fprintf(w, "package %s\n\n", packageName) - 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/schema\"\n") - fmt.Fprintf(w, ")\n\n") - fmt.Fprintf(w, "// Code generated go-ipld-prime DO NOT EDIT.\n\n") -} - -// EmitEntireType outputs every possible type of code generation for a -// typedNodeGenerator -func EmitEntireType(tg typedNodeGenerator, w io.Writer) { - tg.EmitNativeType(w) - tg.EmitNativeAccessors(w) - tg.EmitNativeBuilder(w) - tg.EmitNativeMaybe(w) - tg.EmitNodeType(w) - tg.EmitTypedNodeMethodType(w) - tg.EmitNodeMethodReprKind(w) - tg.EmitNodeMethodLookupString(w) - tg.EmitNodeMethodLookup(w) - tg.EmitNodeMethodLookupIndex(w) - tg.EmitNodeMethodLookupSegment(w) - tg.EmitNodeMethodMapIterator(w) - tg.EmitNodeMethodListIterator(w) - tg.EmitNodeMethodLength(w) - tg.EmitNodeMethodIsUndefined(w) - tg.EmitNodeMethodIsNull(w) - tg.EmitNodeMethodAsBool(w) - tg.EmitNodeMethodAsInt(w) - tg.EmitNodeMethodAsFloat(w) - tg.EmitNodeMethodAsString(w) - tg.EmitNodeMethodAsBytes(w) - tg.EmitNodeMethodAsLink(w) - - tg.EmitNodeMethodNodeBuilder(w) - tnbg := tg.GetNodeBuilderGen() - tnbg.EmitNodebuilderType(w) - tnbg.EmitNodebuilderConstructor(w) - tnbg.EmitNodebuilderMethodCreateMap(w) - tnbg.EmitNodebuilderMethodAmendMap(w) - tnbg.EmitNodebuilderMethodCreateList(w) - tnbg.EmitNodebuilderMethodAmendList(w) - tnbg.EmitNodebuilderMethodCreateNull(w) - tnbg.EmitNodebuilderMethodCreateBool(w) - tnbg.EmitNodebuilderMethodCreateInt(w) - tnbg.EmitNodebuilderMethodCreateFloat(w) - tnbg.EmitNodebuilderMethodCreateString(w) - 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 - return - } - rng.EmitNodeType(w) - rng.EmitNodeMethodReprKind(w) - rng.EmitNodeMethodLookupString(w) - rng.EmitNodeMethodLookup(w) - rng.EmitNodeMethodLookupIndex(w) - rng.EmitNodeMethodLookupSegment(w) - rng.EmitNodeMethodMapIterator(w) - rng.EmitNodeMethodListIterator(w) - rng.EmitNodeMethodLength(w) - rng.EmitNodeMethodIsUndefined(w) - rng.EmitNodeMethodIsNull(w) - rng.EmitNodeMethodAsBool(w) - rng.EmitNodeMethodAsInt(w) - rng.EmitNodeMethodAsFloat(w) - rng.EmitNodeMethodAsString(w) - rng.EmitNodeMethodAsBytes(w) - rng.EmitNodeMethodAsLink(w) - - rng.EmitNodeMethodNodeBuilder(w) - rnbg := rng.GetNodeBuilderGen() - rnbg.EmitNodebuilderType(w) - rnbg.EmitNodebuilderConstructor(w) - rnbg.EmitNodebuilderMethodCreateMap(w) - rnbg.EmitNodebuilderMethodAmendMap(w) - rnbg.EmitNodebuilderMethodCreateList(w) - rnbg.EmitNodebuilderMethodAmendList(w) - rnbg.EmitNodebuilderMethodCreateNull(w) - rnbg.EmitNodebuilderMethodCreateBool(w) - rnbg.EmitNodebuilderMethodCreateInt(w) - rnbg.EmitNodebuilderMethodCreateFloat(w) - rnbg.EmitNodebuilderMethodCreateString(w) - rnbg.EmitNodebuilderMethodCreateBytes(w) - rnbg.EmitNodebuilderMethodCreateLink(w) -} diff --git a/schema/gen/go/genBool.go b/schema/gen/go/genBool.go new file mode 100644 index 00000000..d335f213 --- /dev/null +++ b/schema/gen/go/genBool.go @@ -0,0 +1,119 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +type boolGenerator struct { + AdjCfg *AdjunctCfg + mixins.BoolTraits + PkgName string + Type schema.TypeBool +} + +func (boolGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +// --- native content and specializations ---> + +func (g boolGenerator) EmitNativeType(w io.Writer) { + emitNativeType_scalar(w, g.AdjCfg, g) +} +func (g boolGenerator) EmitNativeAccessors(w io.Writer) { + emitNativeAccessors_scalar(w, g.AdjCfg, g) +} +func (g boolGenerator) EmitNativeBuilder(w io.Writer) { + emitNativeBuilder_scalar(w, g.AdjCfg, g) +} + +func (g boolGenerator) EmitNativeMaybe(w io.Writer) { + emitNativeMaybe(w, g.AdjCfg, g) +} + +// --- type info ---> + +func (g boolGenerator) EmitTypeConst(w io.Writer) { + doTemplate(` + // TODO EmitTypeConst + `, w, g.AdjCfg, g) +} + +// --- TypedNode boolerface satisfaction ---> + +func (g boolGenerator) EmitTypedNodeMethodType(w io.Writer) { + doTemplate(` + func ({{ .Type | TypeSymbol }}) Type() schema.Type { + return nil /*TODO:typelit*/ + } + `, w, g.AdjCfg, g) +} + +func (g boolGenerator) EmitTypedNodeMethodRepresentation(w io.Writer) { + emitTypicalTypedNodeMethodRepresentation(w, g.AdjCfg, g) +} + +// --- Node boolerface satisfaction ---> + +func (g boolGenerator) EmitNodeType(w io.Writer) { + // No additional types needed. Methods all attach to the native type. +} +func (g boolGenerator) EmitNodeTypeAssertions(w io.Writer) { + emitNodeTypeAssertions_typical(w, g.AdjCfg, g) +} +func (g boolGenerator) EmitNodeMethodAsBool(w io.Writer) { + emitNodeMethodAsKind_scalar(w, g.AdjCfg, g) +} +func (g boolGenerator) EmitNodeMethodStyle(w io.Writer) { + emitNodeMethodStyle_typical(w, g.AdjCfg, g) +} +func (g boolGenerator) EmitNodeStyleType(w io.Writer) { + emitNodeStyleType_typical(w, g.AdjCfg, g) +} + +// --- NodeBuilder and NodeAssembler ---> + +func (g boolGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return boolBuilderGenerator{ + g.AdjCfg, + mixins.BoolAssemblerTraits{ + g.PkgName, + g.TypeName, + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__", + }, + g.PkgName, + g.Type, + } +} + +type boolBuilderGenerator struct { + AdjCfg *AdjunctCfg + mixins.BoolAssemblerTraits + PkgName string + Type schema.TypeBool +} + +func (boolBuilderGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +func (g boolBuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g boolBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(w, g.AdjCfg, g) +} +func (g boolBuilderGenerator) EmitNodeAssemblerType(w io.Writer) { + emitNodeAssemblerType_scalar(w, g.AdjCfg, g) +} +func (g boolBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_scalar(w, g.AdjCfg, g) +} +func (g boolBuilderGenerator) EmitNodeAssemblerMethodAssignBool(w io.Writer) { + emitNodeAssemblerMethodAssignKind_scalar(w, g.AdjCfg, g) +} +func (g boolBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) { + emitNodeAssemblerMethodAssignNode_scalar(w, g.AdjCfg, g) +} +func (g boolBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + // Nothing needed here for bool kinds. +} diff --git a/schema/gen/go/genBoolReprBool.go b/schema/gen/go/genBoolReprBool.go new file mode 100644 index 00000000..6d64f6e3 --- /dev/null +++ b/schema/gen/go/genBoolReprBool.go @@ -0,0 +1,108 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +var _ TypeGenerator = &boolReprBoolGenerator{} + +func NewBoolReprBoolGenerator(pkgName string, typ schema.TypeBool, adjCfg *AdjunctCfg) TypeGenerator { + return boolReprBoolGenerator{ + boolGenerator{ + adjCfg, + mixins.BoolTraits{ + pkgName, + string(typ.Name()), + adjCfg.TypeSymbol(typ), + }, + pkgName, + typ, + }, + } +} + +type boolReprBoolGenerator struct { + boolGenerator +} + +func (g boolReprBoolGenerator) GetRepresentationNodeGen() NodeGenerator { + return boolReprBoolReprGenerator{ + g.AdjCfg, + g.Type, + } +} + +type boolReprBoolReprGenerator struct { + AdjCfg *AdjunctCfg + Type schema.TypeBool +} + +func (g boolReprBoolReprGenerator) 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 boolReprBoolReprGenerator) EmitNodeTypeAssertions(w io.Writer) { + doTemplate(` + var _ ipld.Node = &_{{ .Type | TypeSymbol }}__Repr{} + `, w, g.AdjCfg, g) +} +func (boolReprBoolReprGenerator) EmitNodeMethodReprKind(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodLookupString(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodLookup(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodLookupIndex(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodLookupSegment(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodMapIterator(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodListIterator(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodLength(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodIsUndefined(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodIsNull(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodAsBool(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodAsInt(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodAsFloat(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodAsString(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodAsBytes(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodAsLink(io.Writer) {} +func (boolReprBoolReprGenerator) EmitNodeMethodStyle(io.Writer) {} +func (g boolReprBoolReprGenerator) 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 boolReprBoolReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return boolReprBoolReprBuilderGenerator{g.AdjCfg, g.Type} +} + +type boolReprBoolReprBuilderGenerator struct { + AdjCfg *AdjunctCfg + Type schema.TypeBool +} + +func (boolReprBoolReprBuilderGenerator) EmitNodeBuilderType(io.Writer) {} +func (boolReprBoolReprBuilderGenerator) EmitNodeBuilderMethods(io.Writer) {} +func (g boolReprBoolReprBuilderGenerator) EmitNodeAssemblerType(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 }}__ReprAssembler = _{{ .Type | TypeSymbol }}__Assembler + `, w, g.AdjCfg, g) +} +func (boolReprBoolReprBuilderGenerator) EmitNodeAssemblerMethodBeginMap(io.Writer) {} +func (boolReprBoolReprBuilderGenerator) EmitNodeAssemblerMethodBeginList(io.Writer) {} +func (boolReprBoolReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(io.Writer) {} +func (boolReprBoolReprBuilderGenerator) EmitNodeAssemblerMethodAssignBool(io.Writer) {} +func (boolReprBoolReprBuilderGenerator) EmitNodeAssemblerMethodAssignInt(io.Writer) {} +func (boolReprBoolReprBuilderGenerator) EmitNodeAssemblerMethodAssignFloat(io.Writer) {} +func (boolReprBoolReprBuilderGenerator) EmitNodeAssemblerMethodAssignString(io.Writer) {} +func (boolReprBoolReprBuilderGenerator) EmitNodeAssemblerMethodAssignBytes(io.Writer) {} +func (boolReprBoolReprBuilderGenerator) EmitNodeAssemblerMethodAssignLink(io.Writer) {} +func (boolReprBoolReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(io.Writer) {} +func (boolReprBoolReprBuilderGenerator) EmitNodeAssemblerMethodStyle(io.Writer) {} +func (boolReprBoolReprBuilderGenerator) EmitNodeAssemblerOtherBits(io.Writer) {} diff --git a/schema/gen/go/genBytes.go b/schema/gen/go/genBytes.go new file mode 100644 index 00000000..b09c68bd --- /dev/null +++ b/schema/gen/go/genBytes.go @@ -0,0 +1,119 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +type bytesGenerator struct { + AdjCfg *AdjunctCfg + mixins.BytesTraits + PkgName string + Type schema.TypeBytes +} + +func (bytesGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +// --- native content and specializations ---> + +func (g bytesGenerator) EmitNativeType(w io.Writer) { + emitNativeType_scalar(w, g.AdjCfg, g) +} +func (g bytesGenerator) EmitNativeAccessors(w io.Writer) { + emitNativeAccessors_scalar(w, g.AdjCfg, g) +} +func (g bytesGenerator) EmitNativeBuilder(w io.Writer) { + emitNativeBuilder_scalar(w, g.AdjCfg, g) +} + +func (g bytesGenerator) EmitNativeMaybe(w io.Writer) { + emitNativeMaybe(w, g.AdjCfg, g) +} + +// --- type info ---> + +func (g bytesGenerator) EmitTypeConst(w io.Writer) { + doTemplate(` + // TODO EmitTypeConst + `, w, g.AdjCfg, g) +} + +// --- TypedNode byteserface satisfaction ---> + +func (g bytesGenerator) EmitTypedNodeMethodType(w io.Writer) { + doTemplate(` + func ({{ .Type | TypeSymbol }}) Type() schema.Type { + return nil /*TODO:typelit*/ + } + `, w, g.AdjCfg, g) +} + +func (g bytesGenerator) EmitTypedNodeMethodRepresentation(w io.Writer) { + emitTypicalTypedNodeMethodRepresentation(w, g.AdjCfg, g) +} + +// --- Node byteserface satisfaction ---> + +func (g bytesGenerator) EmitNodeType(w io.Writer) { + // No additional types needed. Methods all attach to the native type. +} +func (g bytesGenerator) EmitNodeTypeAssertions(w io.Writer) { + emitNodeTypeAssertions_typical(w, g.AdjCfg, g) +} +func (g bytesGenerator) EmitNodeMethodAsBytes(w io.Writer) { + emitNodeMethodAsKind_scalar(w, g.AdjCfg, g) +} +func (g bytesGenerator) EmitNodeMethodStyle(w io.Writer) { + emitNodeMethodStyle_typical(w, g.AdjCfg, g) +} +func (g bytesGenerator) EmitNodeStyleType(w io.Writer) { + emitNodeStyleType_typical(w, g.AdjCfg, g) +} + +// --- NodeBuilder and NodeAssembler ---> + +func (g bytesGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return bytesBuilderGenerator{ + g.AdjCfg, + mixins.BytesAssemblerTraits{ + g.PkgName, + g.TypeName, + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__", + }, + g.PkgName, + g.Type, + } +} + +type bytesBuilderGenerator struct { + AdjCfg *AdjunctCfg + mixins.BytesAssemblerTraits + PkgName string + Type schema.TypeBytes +} + +func (bytesBuilderGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +func (g bytesBuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g bytesBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(w, g.AdjCfg, g) +} +func (g bytesBuilderGenerator) EmitNodeAssemblerType(w io.Writer) { + emitNodeAssemblerType_scalar(w, g.AdjCfg, g) +} +func (g bytesBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_scalar(w, g.AdjCfg, g) +} +func (g bytesBuilderGenerator) EmitNodeAssemblerMethodAssignBytes(w io.Writer) { + emitNodeAssemblerMethodAssignKind_scalar(w, g.AdjCfg, g) +} +func (g bytesBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) { + emitNodeAssemblerMethodAssignNode_scalar(w, g.AdjCfg, g) +} +func (g bytesBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + // Nothing needed here for bytes kinds. +} diff --git a/schema/gen/go/genBytesReprBytes.go b/schema/gen/go/genBytesReprBytes.go new file mode 100644 index 00000000..96f7da80 --- /dev/null +++ b/schema/gen/go/genBytesReprBytes.go @@ -0,0 +1,108 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +var _ TypeGenerator = &bytesReprBytesGenerator{} + +func NewBytesReprBytesGenerator(pkgName string, typ schema.TypeBytes, adjCfg *AdjunctCfg) TypeGenerator { + return bytesReprBytesGenerator{ + bytesGenerator{ + adjCfg, + mixins.BytesTraits{ + pkgName, + string(typ.Name()), + adjCfg.TypeSymbol(typ), + }, + pkgName, + typ, + }, + } +} + +type bytesReprBytesGenerator struct { + bytesGenerator +} + +func (g bytesReprBytesGenerator) GetRepresentationNodeGen() NodeGenerator { + return bytesReprBytesReprGenerator{ + g.AdjCfg, + g.Type, + } +} + +type bytesReprBytesReprGenerator struct { + AdjCfg *AdjunctCfg + Type schema.TypeBytes +} + +func (g bytesReprBytesReprGenerator) 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 bytesReprBytesReprGenerator) EmitNodeTypeAssertions(w io.Writer) { + doTemplate(` + var _ ipld.Node = &_{{ .Type | TypeSymbol }}__Repr{} + `, w, g.AdjCfg, g) +} +func (bytesReprBytesReprGenerator) EmitNodeMethodReprKind(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodLookupString(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodLookup(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodLookupIndex(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodLookupSegment(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodMapIterator(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodListIterator(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodLength(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodIsUndefined(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodIsNull(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodAsBool(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodAsInt(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodAsFloat(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodAsString(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodAsBytes(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodAsLink(io.Writer) {} +func (bytesReprBytesReprGenerator) EmitNodeMethodStyle(io.Writer) {} +func (g bytesReprBytesReprGenerator) 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 bytesReprBytesReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return bytesReprBytesReprBuilderGenerator{g.AdjCfg, g.Type} +} + +type bytesReprBytesReprBuilderGenerator struct { + AdjCfg *AdjunctCfg + Type schema.TypeBytes +} + +func (bytesReprBytesReprBuilderGenerator) EmitNodeBuilderType(io.Writer) {} +func (bytesReprBytesReprBuilderGenerator) EmitNodeBuilderMethods(io.Writer) {} +func (g bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerType(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 }}__ReprAssembler = _{{ .Type | TypeSymbol }}__Assembler + `, w, g.AdjCfg, g) +} +func (bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerMethodBeginMap(io.Writer) {} +func (bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerMethodBeginList(io.Writer) {} +func (bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(io.Writer) {} +func (bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerMethodAssignBool(io.Writer) {} +func (bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerMethodAssignInt(io.Writer) {} +func (bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerMethodAssignFloat(io.Writer) {} +func (bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerMethodAssignString(io.Writer) {} +func (bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerMethodAssignBytes(io.Writer) {} +func (bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerMethodAssignLink(io.Writer) {} +func (bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(io.Writer) {} +func (bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerMethodStyle(io.Writer) {} +func (bytesReprBytesReprBuilderGenerator) EmitNodeAssemblerOtherBits(io.Writer) {} diff --git a/schema/gen/go/genCommon.go b/schema/gen/go/genCommon.go deleted file mode 100644 index 7b157a89..00000000 --- a/schema/gen/go/genCommon.go +++ /dev/null @@ -1,427 +0,0 @@ -package gengo - -import ( - "io" - - ipld "github.com/ipld/go-ipld-prime" -) - -type generateKindedRejections struct { - TypeIdent string // the identifier in code (sometimes is munged internals like "_Thing__Repr" corresponding to no publicly admitted schema.Type.Name). - TypeProse string // as will be printed in messages (e.g. can be goosed up a bit, like "Thing.Repr" instead of "_Thing__Repr"). - Kind ipld.ReprKind -} - -func (d generateKindedRejections) emitNodeMethodLookupString(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) LookupString(string) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "LookupString", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodLookup(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) Lookup(ipld.Node) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "Lookup", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodLookupIndex(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) LookupIndex(idx int) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "LookupIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodLookupSegment(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "LookupSegment", AppropriateKind: ipld.ReprKindSet_Recursive, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodMapIterator(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) MapIterator() ipld.MapIterator { - return mapIteratorReject{ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "MapIterator", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: {{ .Kind | ReprKindConst }}}} - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodListIterator(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) ListIterator() ipld.ListIterator { - return listIteratorReject{ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "ListIterator", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: {{ .Kind | ReprKindConst }}}} - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodLength(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) Length() int { - return -1 - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodIsUndefined(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) IsUndefined() bool { - return false - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodIsNull(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) IsNull() bool { - return false - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodAsBool(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) AsBool() (bool, error) { - return false, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "AsBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodAsInt(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) AsInt() (int, error) { - return 0, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "AsInt", AppropriateKind: ipld.ReprKindSet_JustInt, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodAsFloat(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) AsFloat() (float64, error) { - return 0, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "AsFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodAsString(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) AsString() (string, error) { - return "", ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "AsString", AppropriateKind: ipld.ReprKindSet_JustString, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodAsBytes(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) AsBytes() ([]byte, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "AsBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} - -func (d generateKindedRejections) emitNodeMethodAsLink(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) AsLink() (ipld.Link, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "AsLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} - -// Embeddable to do all the "nope" methods at once. -type generateKindedRejections_String struct { - TypeIdent string // see doc in generateKindedRejections - TypeProse string // see doc in generateKindedRejections -} - -func (gk generateKindedRejections_String) EmitNodeMethodLookupString(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodLookupString(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodLookup(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodLookup(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodLookupIndex(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodLookupIndex(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodLookupSegment(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodLookupSegment(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodMapIterator(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodMapIterator(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodListIterator(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodListIterator(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodLength(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodLength(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodIsUndefined(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodIsUndefined(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodIsNull(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodIsNull(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodAsBool(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodAsBool(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodAsInt(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodAsInt(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodAsFloat(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodAsFloat(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodAsBytes(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodAsBytes(w) -} -func (gk generateKindedRejections_String) EmitNodeMethodAsLink(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodAsLink(w) -} - -// Embeddable to do all the "nope" methods at once. -// -// Used for anything that "acts like" map (so, also struct). -type generateKindedRejections_Map struct { - TypeIdent string // see doc in generateKindedRejections - TypeProse string // see doc in generateKindedRejections -} - -func (gk generateKindedRejections_Map) EmitNodeMethodLookupIndex(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodeMethodLookupIndex(w) -} -func (gk generateKindedRejections_Map) EmitNodeMethodLookupSegment(w io.Writer) { - doTemplate(` - func (n {{ .TypeIdent }}) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - return n.LookupString(seg.String()) - } - `, w, gk) -} -func (gk generateKindedRejections_Map) EmitNodeMethodListIterator(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodeMethodListIterator(w) -} -func (gk generateKindedRejections_Map) EmitNodeMethodIsUndefined(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodeMethodIsUndefined(w) -} -func (gk generateKindedRejections_Map) EmitNodeMethodIsNull(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodeMethodIsNull(w) -} -func (gk generateKindedRejections_Map) EmitNodeMethodAsBool(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodeMethodAsBool(w) -} -func (gk generateKindedRejections_Map) EmitNodeMethodAsInt(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodeMethodAsInt(w) -} -func (gk generateKindedRejections_Map) EmitNodeMethodAsFloat(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodeMethodAsFloat(w) -} -func (gk generateKindedRejections_Map) EmitNodeMethodAsString(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodeMethodAsString(w) -} -func (gk generateKindedRejections_Map) EmitNodeMethodAsBytes(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodeMethodAsBytes(w) -} -func (gk generateKindedRejections_Map) EmitNodeMethodAsLink(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodeMethodAsLink(w) -} - -// Embeddable to do all the "nope" methods at once. -type generateKindedRejections_Int struct { - TypeIdent string // see doc in generateKindedRejections - TypeProse string // see doc in generateKindedRejections -} - -func (gk generateKindedRejections_Int) EmitNodeMethodLookupString(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodLookupString(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodLookup(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodLookup(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodLookupIndex(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodLookupIndex(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodLookupSegment(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodLookupSegment(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodMapIterator(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodMapIterator(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodListIterator(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodListIterator(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodLength(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodLength(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodIsUndefined(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodIsUndefined(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodIsNull(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodIsNull(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodAsBool(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodAsBool(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodAsString(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodAsString(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodAsFloat(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodAsFloat(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodAsBytes(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodAsBytes(w) -} -func (gk generateKindedRejections_Int) EmitNodeMethodAsLink(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodeMethodAsLink(w) -} - -// Embeddable to do all the "nope" methods at once. -type generateKindedRejections_Bytes struct { - TypeIdent string // see doc in generateKindedRejections - TypeProse string // see doc in generateKindedRejections -} - -func (gk generateKindedRejections_Bytes) EmitNodeMethodLookupString(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodLookupString(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodLookup(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodLookup(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodLookupIndex(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodLookupIndex(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodLookupSegment(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodLookupSegment(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodMapIterator(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodMapIterator(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodListIterator(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodListIterator(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodLength(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodLength(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodIsUndefined(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodIsUndefined(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodIsNull(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodIsNull(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodAsBool(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodAsBool(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodAsInt(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodAsInt(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodAsFloat(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodAsFloat(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodAsString(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodAsString(w) -} -func (gk generateKindedRejections_Bytes) EmitNodeMethodAsLink(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodeMethodAsLink(w) -} - -// Embeddable to do all the "nope" methods at once. -type generateKindedRejections_Link struct { - TypeIdent string // see doc in generateKindedRejections - TypeProse string // see doc in generateKindedRejections -} - -func (gk generateKindedRejections_Link) EmitNodeMethodLookupString(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodLookupString(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodLookup(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodLookup(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodLookupIndex(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodLookupIndex(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodLookupSegment(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodLookupSegment(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodMapIterator(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodMapIterator(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodListIterator(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodListIterator(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodLength(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodLength(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodIsUndefined(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodIsUndefined(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodIsNull(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodIsNull(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodAsBool(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodAsBool(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodAsInt(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodAsInt(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodAsFloat(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodAsFloat(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodAsBytes(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodAsBytes(w) -} -func (gk generateKindedRejections_Link) EmitNodeMethodAsString(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodeMethodAsString(w) -} - -// Embeddable to do all the "nope" methods at once. -// -// Used for anything that "acts like" list (mostly this is just lists, -// but even e.g. structs-with-a-tuple-representation can end up using this -// as part of emitting their representation nodes). -type generateKindedRejections_List struct { - TypeIdent string // see doc in generateKindedRejections - TypeProse string // see doc in generateKindedRejections -} - -func (gk generateKindedRejections_List) EmitNodeMethodLookupString(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodeMethodLookupString(w) -} -func (gk generateKindedRejections_List) EmitNodeMethodLookupSegment(w io.Writer) { - doTemplate(` - func (n {{ .TypeIdent }}) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - idx, err := seg.Index() - if err != nil { - return nil, err - } - return n.LookupIndex(idx) - } - `, w, gk) -} -func (gk generateKindedRejections_List) EmitNodeMethodMapIterator(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodeMethodMapIterator(w) -} -func (gk generateKindedRejections_List) EmitNodeMethodIsUndefined(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodeMethodIsUndefined(w) -} -func (gk generateKindedRejections_List) EmitNodeMethodIsNull(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodeMethodIsNull(w) -} -func (gk generateKindedRejections_List) EmitNodeMethodAsBool(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodeMethodAsBool(w) -} -func (gk generateKindedRejections_List) EmitNodeMethodAsInt(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodeMethodAsInt(w) -} -func (gk generateKindedRejections_List) EmitNodeMethodAsFloat(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodeMethodAsFloat(w) -} -func (gk generateKindedRejections_List) EmitNodeMethodAsString(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodeMethodAsString(w) -} -func (gk generateKindedRejections_List) EmitNodeMethodAsBytes(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodeMethodAsBytes(w) -} -func (gk generateKindedRejections_List) EmitNodeMethodAsLink(w io.Writer) { - generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodeMethodAsLink(w) -} diff --git a/schema/gen/go/genCommonNb.go b/schema/gen/go/genCommonNb.go deleted file mode 100644 index bb0a269a..00000000 --- a/schema/gen/go/genCommonNb.go +++ /dev/null @@ -1,308 +0,0 @@ -package gengo - -import ( - "io" - - ipld "github.com/ipld/go-ipld-prime" -) - -type genKindedNbRejections struct { - TypeIdent string // the identifier in code (sometimes is munged internals like "_Thing__Repr" corresponding to no publicly admitted schema.Type.Name). - TypeProse string // as will be printed in messages (e.g. can be goosed up a bit, like "Thing.Repr" instead of "_Thing__Repr"). - Kind ipld.ReprKind -} - -func (d genKindedNbRejections) emitNodebuilderMethodCreateMap(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) CreateMap() (ipld.MapBuilder, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "CreateMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} -func (d genKindedNbRejections) emitNodebuilderMethodAmendMap(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) AmendMap() (ipld.MapBuilder, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "AmendMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} -func (d genKindedNbRejections) emitNodebuilderMethodCreateList(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) CreateList() (ipld.ListBuilder, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "CreateList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} -func (d genKindedNbRejections) emitNodebuilderMethodAmendList(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) AmendList() (ipld.ListBuilder, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "AmendList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} -func (d genKindedNbRejections) emitNodebuilderMethodCreateNull(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) CreateNull() (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "CreateNull", AppropriateKind: ipld.ReprKindSet_JustNull, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} -func (d genKindedNbRejections) emitNodebuilderMethodCreateBool(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) CreateBool(bool) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "CreateBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} -func (d genKindedNbRejections) emitNodebuilderMethodCreateInt(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) CreateInt(int) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "CreateInt", AppropriateKind: ipld.ReprKindSet_JustInt, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} -func (d genKindedNbRejections) emitNodebuilderMethodCreateFloat(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) CreateFloat(float64) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "CreateFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} -func (d genKindedNbRejections) emitNodebuilderMethodCreateString(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) CreateString(string) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "CreateString", AppropriateKind: ipld.ReprKindSet_JustString, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} -func (d genKindedNbRejections) emitNodebuilderMethodCreateBytes(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) CreateBytes([]byte) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "CreateBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} -func (d genKindedNbRejections) emitNodebuilderMethodCreateLink(w io.Writer) { - doTemplate(` - func ({{ .TypeIdent }}) CreateLink(ipld.Link) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "CreateLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: {{ .Kind | ReprKindConst }}} - } - `, w, d) -} - -// Embeddable to do all the "nope" methods at once. -type genKindedNbRejections_String struct { - TypeIdent string // see doc in generateKindedRejections - TypeProse string // see doc in generateKindedRejections -} - -func (gk genKindedNbRejections_String) EmitNodebuilderMethodCreateMap(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodebuilderMethodCreateMap(w) -} -func (gk genKindedNbRejections_String) EmitNodebuilderMethodAmendMap(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodebuilderMethodAmendMap(w) -} -func (gk genKindedNbRejections_String) EmitNodebuilderMethodCreateList(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodebuilderMethodCreateList(w) -} -func (gk genKindedNbRejections_String) EmitNodebuilderMethodAmendList(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodebuilderMethodAmendList(w) -} -func (gk genKindedNbRejections_String) EmitNodebuilderMethodCreateNull(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodebuilderMethodCreateNull(w) -} -func (gk genKindedNbRejections_String) EmitNodebuilderMethodCreateBool(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodebuilderMethodCreateBool(w) -} -func (gk genKindedNbRejections_String) EmitNodebuilderMethodCreateInt(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodebuilderMethodCreateInt(w) -} -func (gk genKindedNbRejections_String) EmitNodebuilderMethodCreateFloat(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodebuilderMethodCreateFloat(w) -} -func (gk genKindedNbRejections_String) EmitNodebuilderMethodCreateBytes(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodebuilderMethodCreateBytes(w) -} -func (gk genKindedNbRejections_String) EmitNodebuilderMethodCreateLink(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodebuilderMethodCreateLink(w) -} - -// Embeddable to do all the "nope" methods at once. -type genKindedNbRejections_Map struct { - TypeIdent string // see doc in generateKindedRejections - TypeProse string // see doc in generateKindedRejections -} - -func (gk genKindedNbRejections_Map) EmitNodebuilderMethodCreateList(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodebuilderMethodCreateList(w) -} -func (gk genKindedNbRejections_Map) EmitNodebuilderMethodAmendList(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodebuilderMethodAmendList(w) -} -func (gk genKindedNbRejections_Map) EmitNodebuilderMethodCreateNull(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodebuilderMethodCreateNull(w) -} -func (gk genKindedNbRejections_Map) EmitNodebuilderMethodCreateBool(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodebuilderMethodCreateBool(w) -} -func (gk genKindedNbRejections_Map) EmitNodebuilderMethodCreateInt(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodebuilderMethodCreateInt(w) -} -func (gk genKindedNbRejections_Map) EmitNodebuilderMethodCreateFloat(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodebuilderMethodCreateFloat(w) -} -func (gk genKindedNbRejections_Map) EmitNodebuilderMethodCreateString(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodebuilderMethodCreateString(w) -} -func (gk genKindedNbRejections_Map) EmitNodebuilderMethodCreateBytes(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodebuilderMethodCreateBytes(w) -} -func (gk genKindedNbRejections_Map) EmitNodebuilderMethodCreateLink(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodebuilderMethodCreateLink(w) -} - -// Embeddable to do all the "nope" methods at once. -type genKindedNbRejections_Int struct { - TypeIdent string // see doc in generateKindedRejections - TypeProse string // see doc in generateKindedRejections -} - -func (gk genKindedNbRejections_Int) EmitNodebuilderMethodCreateMap(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodebuilderMethodCreateMap(w) -} - -func (gk genKindedNbRejections_Int) EmitNodebuilderMethodAmendMap(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodebuilderMethodAmendMap(w) -} -func (gk genKindedNbRejections_Int) EmitNodebuilderMethodCreateList(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodebuilderMethodCreateList(w) -} -func (gk genKindedNbRejections_Int) EmitNodebuilderMethodAmendList(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodebuilderMethodAmendList(w) -} -func (gk genKindedNbRejections_Int) EmitNodebuilderMethodCreateNull(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodebuilderMethodCreateNull(w) -} -func (gk genKindedNbRejections_Int) EmitNodebuilderMethodCreateBool(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodebuilderMethodCreateBool(w) -} -func (gk genKindedNbRejections_Int) EmitNodebuilderMethodCreateString(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodebuilderMethodCreateString(w) -} -func (gk genKindedNbRejections_Int) EmitNodebuilderMethodCreateFloat(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodebuilderMethodCreateFloat(w) -} -func (gk genKindedNbRejections_Int) EmitNodebuilderMethodCreateBytes(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodebuilderMethodCreateBytes(w) -} -func (gk genKindedNbRejections_Int) EmitNodebuilderMethodCreateLink(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Int}.emitNodebuilderMethodCreateLink(w) -} - -// Embeddable to do all the "nope" methods at once. -type genKindedNbRejections_Bytes struct { - TypeIdent string // see doc in generateKindedRejections - TypeProse string // see doc in generateKindedRejections -} - -func (gk genKindedNbRejections_Bytes) EmitNodebuilderMethodCreateMap(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodebuilderMethodCreateMap(w) -} -func (gk genKindedNbRejections_Bytes) EmitNodebuilderMethodAmendMap(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodebuilderMethodAmendMap(w) -} -func (gk genKindedNbRejections_Bytes) EmitNodebuilderMethodCreateList(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodebuilderMethodCreateList(w) -} -func (gk genKindedNbRejections_Bytes) EmitNodebuilderMethodAmendList(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodebuilderMethodAmendList(w) -} -func (gk genKindedNbRejections_Bytes) EmitNodebuilderMethodCreateNull(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodebuilderMethodCreateNull(w) -} -func (gk genKindedNbRejections_Bytes) EmitNodebuilderMethodCreateBool(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodebuilderMethodCreateBool(w) -} -func (gk genKindedNbRejections_Bytes) EmitNodebuilderMethodCreateInt(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodebuilderMethodCreateInt(w) -} -func (gk genKindedNbRejections_Bytes) EmitNodebuilderMethodCreateFloat(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodebuilderMethodCreateFloat(w) -} -func (gk genKindedNbRejections_Bytes) EmitNodebuilderMethodCreateString(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodebuilderMethodCreateString(w) -} -func (gk genKindedNbRejections_Bytes) EmitNodebuilderMethodCreateLink(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Bytes}.emitNodebuilderMethodCreateLink(w) -} - -// Embeddable to do all the "nope" methods at once. -type genKindedNbRejections_Link struct { - TypeIdent string // see doc in generateKindedRejections - TypeProse string // see doc in generateKindedRejections -} - -func (gk genKindedNbRejections_Link) EmitNodebuilderMethodCreateMap(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodebuilderMethodCreateMap(w) -} -func (gk genKindedNbRejections_Link) EmitNodebuilderMethodAmendMap(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodebuilderMethodAmendMap(w) -} -func (gk genKindedNbRejections_Link) EmitNodebuilderMethodCreateList(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodebuilderMethodCreateList(w) -} -func (gk genKindedNbRejections_Link) EmitNodebuilderMethodAmendList(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodebuilderMethodAmendList(w) -} -func (gk genKindedNbRejections_Link) EmitNodebuilderMethodCreateNull(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodebuilderMethodCreateNull(w) -} -func (gk genKindedNbRejections_Link) EmitNodebuilderMethodCreateBool(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodebuilderMethodCreateBool(w) -} -func (gk genKindedNbRejections_Link) EmitNodebuilderMethodCreateInt(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodebuilderMethodCreateInt(w) -} -func (gk genKindedNbRejections_Link) EmitNodebuilderMethodCreateFloat(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodebuilderMethodCreateFloat(w) -} -func (gk genKindedNbRejections_Link) EmitNodebuilderMethodCreateBytes(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodebuilderMethodCreateBytes(w) -} -func (gk genKindedNbRejections_Link) EmitNodebuilderMethodCreateString(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Link}.emitNodebuilderMethodCreateString(w) -} - -// Embeddable to do all the "nope" methods at once. -type genKindedNbRejections_List struct { - TypeIdent string // see doc in generateKindedRejections - TypeProse string // see doc in generateKindedRejections -} - -func (gk genKindedNbRejections_List) EmitNodebuilderMethodCreateMap(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodebuilderMethodCreateMap(w) -} -func (gk genKindedNbRejections_List) EmitNodebuilderMethodAmendMap(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodebuilderMethodAmendMap(w) -} -func (gk genKindedNbRejections_List) EmitNodebuilderMethodCreateNull(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodebuilderMethodCreateNull(w) -} -func (gk genKindedNbRejections_List) EmitNodebuilderMethodCreateBool(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodebuilderMethodCreateBool(w) -} -func (gk genKindedNbRejections_List) EmitNodebuilderMethodCreateInt(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodebuilderMethodCreateInt(w) -} -func (gk genKindedNbRejections_List) EmitNodebuilderMethodCreateFloat(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodebuilderMethodCreateFloat(w) -} -func (gk genKindedNbRejections_List) EmitNodebuilderMethodCreateString(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodebuilderMethodCreateString(w) -} -func (gk genKindedNbRejections_List) EmitNodebuilderMethodCreateBytes(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodebuilderMethodCreateBytes(w) -} -func (gk genKindedNbRejections_List) EmitNodebuilderMethodCreateLink(w io.Writer) { - genKindedNbRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_List}.emitNodebuilderMethodCreateLink(w) -} diff --git a/schema/gen/go/genFloat.go b/schema/gen/go/genFloat.go new file mode 100644 index 00000000..0aff2109 --- /dev/null +++ b/schema/gen/go/genFloat.go @@ -0,0 +1,119 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +type float64Generator struct { + AdjCfg *AdjunctCfg + mixins.FloatTraits + PkgName string + Type schema.TypeFloat +} + +func (float64Generator) IsRepr() bool { return false } // hint used in some generalized templates. + +// --- native content and specializations ---> + +func (g float64Generator) EmitNativeType(w io.Writer) { + emitNativeType_scalar(w, g.AdjCfg, g) +} +func (g float64Generator) EmitNativeAccessors(w io.Writer) { + emitNativeAccessors_scalar(w, g.AdjCfg, g) +} +func (g float64Generator) EmitNativeBuilder(w io.Writer) { + emitNativeBuilder_scalar(w, g.AdjCfg, g) +} + +func (g float64Generator) EmitNativeMaybe(w io.Writer) { + emitNativeMaybe(w, g.AdjCfg, g) +} + +// --- type info ---> + +func (g float64Generator) EmitTypeConst(w io.Writer) { + doTemplate(` + // TODO EmitTypeConst + `, w, g.AdjCfg, g) +} + +// --- TypedNode float64erface satisfaction ---> + +func (g float64Generator) EmitTypedNodeMethodType(w io.Writer) { + doTemplate(` + func ({{ .Type | TypeSymbol }}) Type() schema.Type { + return nil /*TODO:typelit*/ + } + `, w, g.AdjCfg, g) +} + +func (g float64Generator) EmitTypedNodeMethodRepresentation(w io.Writer) { + emitTypicalTypedNodeMethodRepresentation(w, g.AdjCfg, g) +} + +// --- Node float64erface satisfaction ---> + +func (g float64Generator) EmitNodeType(w io.Writer) { + // No additional types needed. Methods all attach to the native type. +} +func (g float64Generator) EmitNodeTypeAssertions(w io.Writer) { + emitNodeTypeAssertions_typical(w, g.AdjCfg, g) +} +func (g float64Generator) EmitNodeMethodAsFloat(w io.Writer) { + emitNodeMethodAsKind_scalar(w, g.AdjCfg, g) +} +func (g float64Generator) EmitNodeMethodStyle(w io.Writer) { + emitNodeMethodStyle_typical(w, g.AdjCfg, g) +} +func (g float64Generator) EmitNodeStyleType(w io.Writer) { + emitNodeStyleType_typical(w, g.AdjCfg, g) +} + +// --- NodeBuilder and NodeAssembler ---> + +func (g float64Generator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return float64BuilderGenerator{ + g.AdjCfg, + mixins.FloatAssemblerTraits{ + g.PkgName, + g.TypeName, + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__", + }, + g.PkgName, + g.Type, + } +} + +type float64BuilderGenerator struct { + AdjCfg *AdjunctCfg + mixins.FloatAssemblerTraits + PkgName string + Type schema.TypeFloat +} + +func (float64BuilderGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +func (g float64BuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g float64BuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(w, g.AdjCfg, g) +} +func (g float64BuilderGenerator) EmitNodeAssemblerType(w io.Writer) { + emitNodeAssemblerType_scalar(w, g.AdjCfg, g) +} +func (g float64BuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_scalar(w, g.AdjCfg, g) +} +func (g float64BuilderGenerator) EmitNodeAssemblerMethodAssignFloat(w io.Writer) { + emitNodeAssemblerMethodAssignKind_scalar(w, g.AdjCfg, g) +} +func (g float64BuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) { + emitNodeAssemblerMethodAssignNode_scalar(w, g.AdjCfg, g) +} +func (g float64BuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + // Nothing needed here for float64 kinds. +} diff --git a/schema/gen/go/genFloatReprFloat.go b/schema/gen/go/genFloatReprFloat.go new file mode 100644 index 00000000..ee9fa356 --- /dev/null +++ b/schema/gen/go/genFloatReprFloat.go @@ -0,0 +1,108 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +var _ TypeGenerator = &float64ReprFloatGenerator{} + +func NewFloatReprFloatGenerator(pkgName string, typ schema.TypeFloat, adjCfg *AdjunctCfg) TypeGenerator { + return float64ReprFloatGenerator{ + float64Generator{ + adjCfg, + mixins.FloatTraits{ + pkgName, + string(typ.Name()), + adjCfg.TypeSymbol(typ), + }, + pkgName, + typ, + }, + } +} + +type float64ReprFloatGenerator struct { + float64Generator +} + +func (g float64ReprFloatGenerator) GetRepresentationNodeGen() NodeGenerator { + return float64ReprFloatReprGenerator{ + g.AdjCfg, + g.Type, + } +} + +type float64ReprFloatReprGenerator struct { + AdjCfg *AdjunctCfg + Type schema.TypeFloat +} + +func (g float64ReprFloatReprGenerator) 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 float64ReprFloatReprGenerator) EmitNodeTypeAssertions(w io.Writer) { + doTemplate(` + var _ ipld.Node = &_{{ .Type | TypeSymbol }}__Repr{} + `, w, g.AdjCfg, g) +} +func (float64ReprFloatReprGenerator) EmitNodeMethodReprKind(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodLookupString(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodLookup(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodLookupIndex(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodLookupSegment(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodMapIterator(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodListIterator(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodLength(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodIsUndefined(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodIsNull(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodAsBool(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodAsInt(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodAsFloat(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodAsString(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodAsBytes(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodAsLink(io.Writer) {} +func (float64ReprFloatReprGenerator) EmitNodeMethodStyle(io.Writer) {} +func (g float64ReprFloatReprGenerator) 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 float64ReprFloatReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return float64ReprFloatReprBuilderGenerator{g.AdjCfg, g.Type} +} + +type float64ReprFloatReprBuilderGenerator struct { + AdjCfg *AdjunctCfg + Type schema.TypeFloat +} + +func (float64ReprFloatReprBuilderGenerator) EmitNodeBuilderType(io.Writer) {} +func (float64ReprFloatReprBuilderGenerator) EmitNodeBuilderMethods(io.Writer) {} +func (g float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerType(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 }}__ReprAssembler = _{{ .Type | TypeSymbol }}__Assembler + `, w, g.AdjCfg, g) +} +func (float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerMethodBeginMap(io.Writer) {} +func (float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerMethodBeginList(io.Writer) {} +func (float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(io.Writer) {} +func (float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerMethodAssignBool(io.Writer) {} +func (float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerMethodAssignInt(io.Writer) {} +func (float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerMethodAssignFloat(io.Writer) {} +func (float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerMethodAssignString(io.Writer) {} +func (float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerMethodAssignBytes(io.Writer) {} +func (float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerMethodAssignLink(io.Writer) {} +func (float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(io.Writer) {} +func (float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerMethodStyle(io.Writer) {} +func (float64ReprFloatReprBuilderGenerator) EmitNodeAssemblerOtherBits(io.Writer) {} diff --git a/schema/gen/go/genInt.go b/schema/gen/go/genInt.go new file mode 100644 index 00000000..db15662e --- /dev/null +++ b/schema/gen/go/genInt.go @@ -0,0 +1,119 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +type intGenerator struct { + AdjCfg *AdjunctCfg + mixins.IntTraits + PkgName string + Type schema.TypeInt +} + +func (intGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +// --- native content and specializations ---> + +func (g intGenerator) EmitNativeType(w io.Writer) { + emitNativeType_scalar(w, g.AdjCfg, g) +} +func (g intGenerator) EmitNativeAccessors(w io.Writer) { + emitNativeAccessors_scalar(w, g.AdjCfg, g) +} +func (g intGenerator) EmitNativeBuilder(w io.Writer) { + emitNativeBuilder_scalar(w, g.AdjCfg, g) +} + +func (g intGenerator) EmitNativeMaybe(w io.Writer) { + emitNativeMaybe(w, g.AdjCfg, g) +} + +// --- type info ---> + +func (g intGenerator) EmitTypeConst(w io.Writer) { + doTemplate(` + // TODO EmitTypeConst + `, w, g.AdjCfg, g) +} + +// --- TypedNode interface satisfaction ---> + +func (g intGenerator) EmitTypedNodeMethodType(w io.Writer) { + doTemplate(` + func ({{ .Type | TypeSymbol }}) Type() schema.Type { + return nil /*TODO:typelit*/ + } + `, w, g.AdjCfg, g) +} + +func (g intGenerator) EmitTypedNodeMethodRepresentation(w io.Writer) { + emitTypicalTypedNodeMethodRepresentation(w, g.AdjCfg, g) +} + +// --- Node interface satisfaction ---> + +func (g intGenerator) EmitNodeType(w io.Writer) { + // No additional types needed. Methods all attach to the native type. +} +func (g intGenerator) EmitNodeTypeAssertions(w io.Writer) { + emitNodeTypeAssertions_typical(w, g.AdjCfg, g) +} +func (g intGenerator) EmitNodeMethodAsInt(w io.Writer) { + emitNodeMethodAsKind_scalar(w, g.AdjCfg, g) +} +func (g intGenerator) EmitNodeMethodStyle(w io.Writer) { + emitNodeMethodStyle_typical(w, g.AdjCfg, g) +} +func (g intGenerator) EmitNodeStyleType(w io.Writer) { + emitNodeStyleType_typical(w, g.AdjCfg, g) +} + +// --- NodeBuilder and NodeAssembler ---> + +func (g intGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return intBuilderGenerator{ + g.AdjCfg, + mixins.IntAssemblerTraits{ + g.PkgName, + g.TypeName, + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__", + }, + g.PkgName, + g.Type, + } +} + +type intBuilderGenerator struct { + AdjCfg *AdjunctCfg + mixins.IntAssemblerTraits + PkgName string + Type schema.TypeInt +} + +func (intBuilderGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +func (g intBuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g intBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(w, g.AdjCfg, g) +} +func (g intBuilderGenerator) EmitNodeAssemblerType(w io.Writer) { + emitNodeAssemblerType_scalar(w, g.AdjCfg, g) +} +func (g intBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_scalar(w, g.AdjCfg, g) +} +func (g intBuilderGenerator) EmitNodeAssemblerMethodAssignInt(w io.Writer) { + emitNodeAssemblerMethodAssignKind_scalar(w, g.AdjCfg, g) +} +func (g intBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) { + emitNodeAssemblerMethodAssignNode_scalar(w, g.AdjCfg, g) +} +func (g intBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + // Nothing needed here for int kinds. +} diff --git a/schema/gen/go/genIntReprInt.go b/schema/gen/go/genIntReprInt.go new file mode 100644 index 00000000..fc350ec9 --- /dev/null +++ b/schema/gen/go/genIntReprInt.go @@ -0,0 +1,108 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +var _ TypeGenerator = &intReprIntGenerator{} + +func NewIntReprIntGenerator(pkgName string, typ schema.TypeInt, adjCfg *AdjunctCfg) TypeGenerator { + return intReprIntGenerator{ + intGenerator{ + adjCfg, + mixins.IntTraits{ + pkgName, + string(typ.Name()), + adjCfg.TypeSymbol(typ), + }, + pkgName, + typ, + }, + } +} + +type intReprIntGenerator struct { + intGenerator +} + +func (g intReprIntGenerator) GetRepresentationNodeGen() NodeGenerator { + return intReprIntReprGenerator{ + g.AdjCfg, + g.Type, + } +} + +type intReprIntReprGenerator struct { + AdjCfg *AdjunctCfg + Type schema.TypeInt +} + +func (g intReprIntReprGenerator) 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 intReprIntReprGenerator) EmitNodeTypeAssertions(w io.Writer) { + doTemplate(` + var _ ipld.Node = &_{{ .Type | TypeSymbol }}__Repr{} + `, w, g.AdjCfg, g) +} +func (intReprIntReprGenerator) EmitNodeMethodReprKind(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodLookupString(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodLookup(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodLookupIndex(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodLookupSegment(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodMapIterator(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodListIterator(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodLength(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodIsUndefined(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodIsNull(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodAsBool(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodAsInt(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodAsFloat(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodAsString(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodAsBytes(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodAsLink(io.Writer) {} +func (intReprIntReprGenerator) EmitNodeMethodStyle(io.Writer) {} +func (g intReprIntReprGenerator) 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 intReprIntReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return intReprIntReprBuilderGenerator{g.AdjCfg, g.Type} +} + +type intReprIntReprBuilderGenerator struct { + AdjCfg *AdjunctCfg + Type schema.TypeInt +} + +func (intReprIntReprBuilderGenerator) EmitNodeBuilderType(io.Writer) {} +func (intReprIntReprBuilderGenerator) EmitNodeBuilderMethods(io.Writer) {} +func (g intReprIntReprBuilderGenerator) EmitNodeAssemblerType(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 }}__ReprAssembler = _{{ .Type | TypeSymbol }}__Assembler + `, w, g.AdjCfg, g) +} +func (intReprIntReprBuilderGenerator) EmitNodeAssemblerMethodBeginMap(io.Writer) {} +func (intReprIntReprBuilderGenerator) EmitNodeAssemblerMethodBeginList(io.Writer) {} +func (intReprIntReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(io.Writer) {} +func (intReprIntReprBuilderGenerator) EmitNodeAssemblerMethodAssignBool(io.Writer) {} +func (intReprIntReprBuilderGenerator) EmitNodeAssemblerMethodAssignInt(io.Writer) {} +func (intReprIntReprBuilderGenerator) EmitNodeAssemblerMethodAssignFloat(io.Writer) {} +func (intReprIntReprBuilderGenerator) EmitNodeAssemblerMethodAssignString(io.Writer) {} +func (intReprIntReprBuilderGenerator) EmitNodeAssemblerMethodAssignBytes(io.Writer) {} +func (intReprIntReprBuilderGenerator) EmitNodeAssemblerMethodAssignLink(io.Writer) {} +func (intReprIntReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(io.Writer) {} +func (intReprIntReprBuilderGenerator) EmitNodeAssemblerMethodStyle(io.Writer) {} +func (intReprIntReprBuilderGenerator) EmitNodeAssemblerOtherBits(io.Writer) {} diff --git a/schema/gen/go/genKindBytes.go b/schema/gen/go/genKindBytes.go deleted file mode 100644 index 3ec09e28..00000000 --- a/schema/gen/go/genKindBytes.go +++ /dev/null @@ -1,61 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -func NewGeneratorForKindBytes(t schema.Type) typedNodeGenerator { - return generateKindBytes{ - t.(schema.TypeBytes), - generateKindedRejections_Bytes{ - mungeTypeNodeIdent(t), - string(t.Name()), - }, - } -} - -type generateKindBytes struct { - Type schema.TypeBytes - generateKindedRejections_Bytes - // 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 generateKindBytes) EmitNativeType(w io.Writer) { - doTemplate(` - type {{ .Type | mungeTypeNodeIdent }} struct{ x []byte } - - `, w, gk) -} - -func (gk generateKindBytes) EmitNativeAccessors(w io.Writer) { - doTemplate(` - // TODO generateKindBytes.EmitNativeAccessors - `, w, gk) -} - -func (gk generateKindBytes) EmitNativeBuilder(w io.Writer) { - doTemplate(` - // TODO generateKindBytes.EmitNativeBuilder - `, w, gk) -} - -func (gk generateKindBytes) EmitNativeMaybe(w io.Writer) { - // TODO this can most likely be extracted and DRY'd, just not 100% sure yet - doTemplate(` - type Maybe{{ .Type | mungeTypeNodeIdent }} struct { - Maybe schema.Maybe - Value {{ .Type | mungeTypeNodeIdent }} - } - - func (m Maybe{{ .Type | mungeTypeNodeIdent }}) Must() {{ .Type | mungeTypeNodeIdent }} { - if m.Maybe != schema.Maybe_Value { - panic("unbox of a maybe rejected") - } - return m.Value - } - - `, w, gk) -} diff --git a/schema/gen/go/genKindBytesNode.go b/schema/gen/go/genKindBytesNode.go deleted file mode 100644 index 7fed94ed..00000000 --- a/schema/gen/go/genKindBytesNode.go +++ /dev/null @@ -1,102 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -// --- type-semantics node interface satisfaction ---> - -func (gk generateKindBytes) EmitNodeType(w io.Writer) { - doTemplate(` - var _ ipld.Node = {{ .Type | mungeTypeNodeIdent }}{} - var _ schema.TypedNode = {{ .Type | mungeTypeNodeIdent }}{} - - `, w, gk) -} - -func (gk generateKindBytes) EmitTypedNodeMethodType(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) Type() schema.Type { - return nil /*TODO:typelit*/ - } - `, w, gk) -} - -func (gk generateKindBytes) EmitNodeMethodReprKind(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) ReprKind() ipld.ReprKind { - return ipld.ReprKind_Bytes - } - `, w, gk) -} - -func (gk generateKindBytes) EmitNodeMethodAsBytes(w io.Writer) { - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) AsBytes() ([]byte, error) { - return x.x, nil - } - `, w, gk) -} - -// --- type-semantics nodebuilder ---> - -func (gk generateKindBytes) EmitNodeMethodNodeBuilder(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) NodeBuilder() ipld.NodeBuilder { - return {{ .Type | mungeTypeNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateKindBytes) GetNodeBuilderGen() nodebuilderGenerator { - return generateNbKindBytes{ - gk.Type, - genKindedNbRejections_Bytes{ - mungeTypeNodebuilderIdent(gk.Type), - string(gk.Type.Name()) + ".Builder", - }, - } -} - -type generateNbKindBytes struct { - Type schema.TypeBytes - genKindedNbRejections_Bytes -} - -func (gk generateNbKindBytes) EmitNodebuilderType(w io.Writer) { - doTemplate(` - type {{ .Type | mungeTypeNodebuilderIdent }} struct{} - `, w, gk) -} - -func (gk generateNbKindBytes) EmitNodebuilderConstructor(w io.Writer) { - doTemplate(` - func {{ .Type | mungeNodebuilderConstructorIdent }}() ipld.NodeBuilder { - return {{ .Type | mungeTypeNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateNbKindBytes) EmitNodebuilderMethodCreateBytes(w io.Writer) { - doTemplate(` - func (nb {{ .Type | mungeTypeNodebuilderIdent }}) CreateBytes(v []byte) (ipld.Node, error) { - return {{ .Type | mungeTypeNodeIdent }}{v}, nil - } - `, w, gk) -} - -// --- entrypoints to representation ---> - -func (gk generateKindBytes) EmitTypedNodeMethodRepresentation(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) Representation() ipld.Node { - panic("TODO representation") - } - `, w, gk) -} - -func (gk generateKindBytes) GetRepresentationNodeGen() nodeGenerator { - return nil // TODO of course -} diff --git a/schema/gen/go/genKindInt.go b/schema/gen/go/genKindInt.go deleted file mode 100644 index 3d97a33f..00000000 --- a/schema/gen/go/genKindInt.go +++ /dev/null @@ -1,66 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -func NewGeneratorForKindInt(t schema.Type) typedNodeGenerator { - return generateKindInt{ - t.(schema.TypeInt), - generateKindedRejections_Int{ - mungeTypeNodeIdent(t), - string(t.Name()), - }, - } -} - -type generateKindInt struct { - Type schema.TypeInt - generateKindedRejections_Int - // 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 generateKindInt) EmitNativeType(w io.Writer) { - doTemplate(` - type {{ .Type | mungeTypeNodeIdent }} struct{ x int } - - `, w, gk) -} - -func (gk generateKindInt) EmitNativeAccessors(w io.Writer) { - // The node interface's `AsInt` method is almost sufficient... but - // this method unboxes without needing to return an error that's statically impossible, - // which makes it easier to use in chaining. - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) Int() int { - return x.x - } - `, w, gk) -} - -func (gk generateKindInt) EmitNativeBuilder(w io.Writer) { - doTemplate(` - // TODO generateKindInt.EmitNativeBuilder - `, w, gk) -} - -func (gk generateKindInt) EmitNativeMaybe(w io.Writer) { - // TODO this can most likely be extracted and DRY'd, just not 100% sure yet - doTemplate(` - type Maybe{{ .Type | mungeTypeNodeIdent }} struct { - Maybe schema.Maybe - Value {{ .Type | mungeTypeNodeIdent }} - } - - func (m Maybe{{ .Type | mungeTypeNodeIdent }}) Must() {{ .Type | mungeTypeNodeIdent }} { - if m.Maybe != schema.Maybe_Value { - panic("unbox of a maybe rejected") - } - return m.Value - } - - `, w, gk) -} diff --git a/schema/gen/go/genKindIntNode.go b/schema/gen/go/genKindIntNode.go deleted file mode 100644 index 17d35659..00000000 --- a/schema/gen/go/genKindIntNode.go +++ /dev/null @@ -1,102 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -// --- type-semantics node interface satisfaction ---> - -func (gk generateKindInt) EmitNodeType(w io.Writer) { - doTemplate(` - var _ ipld.Node = {{ .Type | mungeTypeNodeIdent }}{} - var _ schema.TypedNode = {{ .Type | mungeTypeNodeIdent }}{} - - `, w, gk) -} - -func (gk generateKindInt) EmitTypedNodeMethodType(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) Type() schema.Type { - return nil /*TODO:typelit*/ - } - `, w, gk) -} - -func (gk generateKindInt) EmitNodeMethodReprKind(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) ReprKind() ipld.ReprKind { - return ipld.ReprKind_Int - } - `, w, gk) -} - -func (gk generateKindInt) EmitNodeMethodAsInt(w io.Writer) { - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) AsInt() (int, error) { - return x.x, nil - } - `, w, gk) -} - -// --- type-semantics nodebuilder ---> - -func (gk generateKindInt) EmitNodeMethodNodeBuilder(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) NodeBuilder() ipld.NodeBuilder { - return {{ .Type | mungeTypeNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateKindInt) GetNodeBuilderGen() nodebuilderGenerator { - return generateNbKindInt{ - gk.Type, - genKindedNbRejections_Int{ - mungeTypeNodebuilderIdent(gk.Type), - string(gk.Type.Name()) + ".Builder", - }, - } -} - -type generateNbKindInt struct { - Type schema.TypeInt - genKindedNbRejections_Int -} - -func (gk generateNbKindInt) EmitNodebuilderType(w io.Writer) { - doTemplate(` - type {{ .Type | mungeTypeNodebuilderIdent }} struct{} - `, w, gk) -} - -func (gk generateNbKindInt) EmitNodebuilderConstructor(w io.Writer) { - doTemplate(` - func {{ .Type | mungeNodebuilderConstructorIdent }}() ipld.NodeBuilder { - return {{ .Type | mungeTypeNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateNbKindInt) EmitNodebuilderMethodCreateInt(w io.Writer) { - doTemplate(` - func (nb {{ .Type | mungeTypeNodebuilderIdent }}) CreateInt(v int) (ipld.Node, error) { - return {{ .Type | mungeTypeNodeIdent }}{v}, nil - } - `, w, gk) -} - -// --- entrypoints to representation ---> - -func (gk generateKindInt) EmitTypedNodeMethodRepresentation(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) Representation() ipld.Node { - panic("TODO representation") - } - `, w, gk) -} - -func (gk generateKindInt) GetRepresentationNodeGen() nodeGenerator { - return nil // TODO of course -} diff --git a/schema/gen/go/genKindLink.go b/schema/gen/go/genKindLink.go deleted file mode 100644 index bfc57f2b..00000000 --- a/schema/gen/go/genKindLink.go +++ /dev/null @@ -1,61 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -func NewGeneratorForKindLink(t schema.Type) typedNodeGenerator { - return generateKindLink{ - t.(schema.TypeLink), - generateKindedRejections_Link{ - mungeTypeNodeIdent(t), - string(t.Name()), - }, - } -} - -type generateKindLink struct { - Type schema.TypeLink - generateKindedRejections_Link - // 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 generateKindLink) EmitNativeType(w io.Writer) { - doTemplate(` - type {{ .Type | mungeTypeNodeIdent }} struct{ x ipld.Link } - - `, w, gk) -} - -func (gk generateKindLink) EmitNativeAccessors(w io.Writer) { - doTemplate(` - // TODO generateKindLink.EmitNativeAccessors - `, w, gk) -} - -func (gk generateKindLink) EmitNativeBuilder(w io.Writer) { - doTemplate(` - // TODO generateKindLink.EmitNativeBuilder - `, w, gk) -} - -func (gk generateKindLink) EmitNativeMaybe(w io.Writer) { - // TODO this can most likely be extracted and DRY'd, just not 100% sure yet - doTemplate(` - type Maybe{{ .Type | mungeTypeNodeIdent }} struct { - Maybe schema.Maybe - Value {{ .Type | mungeTypeNodeIdent }} - } - - func (m Maybe{{ .Type | mungeTypeNodeIdent }}) Must() {{ .Type | mungeTypeNodeIdent }} { - if m.Maybe != schema.Maybe_Value { - panic("unbox of a maybe rejected") - } - return m.Value - } - - `, w, gk) -} diff --git a/schema/gen/go/genKindLinkNode.go b/schema/gen/go/genKindLinkNode.go deleted file mode 100644 index 2dd1a7ea..00000000 --- a/schema/gen/go/genKindLinkNode.go +++ /dev/null @@ -1,113 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -// --- type-semantics node interface satisfaction ---> - -func (gk generateKindLink) EmitNodeType(w io.Writer) { - doTemplate(` - var _ ipld.Node = {{ .Type | mungeTypeNodeIdent }}{} - var _ schema.TypedNode = {{ .Type | mungeTypeNodeIdent }}{} - - `, w, gk) -} - -func (gk generateKindLink) EmitTypedNodeMethodType(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) Type() schema.Type { - return nil /*TODO:typelit*/ - } - `, w, gk) -} - -func (gk generateKindLink) EmitNodeMethodReprKind(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) ReprKind() ipld.ReprKind { - return ipld.ReprKind_Link - } - `, w, gk) -} - -func (gk generateKindLink) EmitNodeMethodAsLink(w io.Writer) { - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) AsLink() (ipld.Link, error) { - return x.x, nil - } - `, w, gk) -} - -// --- type-semantics nodebuilder ---> - -func (gk generateKindLink) EmitNodeMethodNodeBuilder(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) NodeBuilder() ipld.NodeBuilder { - return {{ .Type | mungeTypeNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateKindLink) GetNodeBuilderGen() nodebuilderGenerator { - return generateNbKindLink{ - gk.Type, - genKindedNbRejections_Link{ - mungeTypeNodebuilderIdent(gk.Type), - string(gk.Type.Name()) + ".Builder", - }, - } -} - -type generateNbKindLink struct { - Type schema.TypeLink - genKindedNbRejections_Link -} - -func (gk generateNbKindLink) EmitNodebuilderType(w io.Writer) { - doTemplate(` - type {{ .Type | mungeTypeNodebuilderIdent }} struct{} - - `, w, gk) -} - -func (gk generateNbKindLink) EmitNodebuilderConstructor(w io.Writer) { - doTemplate(` - func {{ .Type | mungeNodebuilderConstructorIdent }}() ipld.NodeBuilder { - return {{ .Type | mungeTypeNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateNbKindLink) EmitNodebuilderMethodCreateLink(w io.Writer) { - doTemplate(` - func (nb {{ .Type | mungeTypeNodebuilderIdent }}) CreateLink(v ipld.Link) (ipld.Node, error) { - return {{ .Type | mungeTypeNodeIdent }}{v}, nil - } - `, w, gk) -} - -// --- entrypoints to representation ---> - -func (gk generateKindLink) EmitTypedNodeMethodRepresentation(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) Representation() ipld.Node { - panic("TODO representation") - } - `, 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 -} diff --git a/schema/gen/go/genKindList.go b/schema/gen/go/genKindList.go deleted file mode 100644 index 4950ba7e..00000000 --- a/schema/gen/go/genKindList.go +++ /dev/null @@ -1,65 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -func NewGeneratorForKindList(t schema.Type) typedNodeGenerator { - return generateKindList{ - t.(schema.TypeList), - generateKindedRejections_List{ - mungeTypeNodeIdent(t), - string(t.Name()), - }, - } -} - -type generateKindList struct { - Type schema.TypeList - generateKindedRejections_List - // 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 generateKindList) EmitNativeType(w io.Writer) { - // Observe that we get a '*' if the values are nullable. - // FUTURE: worth reviewing if this could or should use 'maybe' structs instead of pointers - // (which would effectively trade alloc count vs size for very different performance characteristics). - doTemplate(` - type {{ .Type | mungeTypeNodeIdent }} struct{ - x []{{if .Type.ValueIsNullable}}*{{end}}{{.Type.ValueType | mungeTypeNodeIdent}} - } - `, w, gk) -} - -func (gk generateKindList) EmitNativeAccessors(w io.Writer) { - doTemplate(` - // TODO generateKindList.EmitNativeAccessors - `, w, gk) -} - -func (gk generateKindList) EmitNativeBuilder(w io.Writer) { - doTemplate(` - // TODO generateKindList.EmitNativeBuilder - `, w, gk) -} - -func (gk generateKindList) EmitNativeMaybe(w io.Writer) { - // TODO this can most likely be extracted and DRY'd, just not 100% sure yet - doTemplate(` - type Maybe{{ .Type | mungeTypeNodeIdent }} struct { - Maybe schema.Maybe - Value {{ .Type | mungeTypeNodeIdent }} - } - - func (m Maybe{{ .Type | mungeTypeNodeIdent }}) Must() {{ .Type | mungeTypeNodeIdent }} { - if m.Maybe != schema.Maybe_Value { - panic("unbox of a maybe rejected") - } - return m.Value - } - - `, w, gk) -} diff --git a/schema/gen/go/genKindListNode.go b/schema/gen/go/genKindListNode.go deleted file mode 100644 index c51e4ba9..00000000 --- a/schema/gen/go/genKindListNode.go +++ /dev/null @@ -1,313 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -// --- type-semantics node interface satisfaction ---> - -func (gk generateKindList) EmitNodeType(w io.Writer) { - doTemplate(` - var _ ipld.Node = {{ .Type | mungeTypeNodeIdent }}{} - var _ schema.TypedNode = {{ .Type | mungeTypeNodeIdent }}{} - - `, w, gk) -} - -func (gk generateKindList) EmitTypedNodeMethodType(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) Type() schema.Type { - return nil /*TODO:typelit*/ - } - `, w, gk) -} - -func (gk generateKindList) EmitNodeMethodReprKind(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) ReprKind() ipld.ReprKind { - return ipld.ReprKind_List - } - `, w, gk) -} - -func (gk generateKindList) EmitNodeMethodLookupIndex(w io.Writer) { - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) LookupIndex(index int) (ipld.Node, error) { - if index >= len(x.x) { - return nil, ipld.ErrNotExists{ipld.PathSegmentOfInt(index)} - } - {{- if .Type.ValueIsNullable }} - if x.x[index] == nil { - return ipld.Null, nil - } - return *x.x[index], nil - {{- else }} - return x.x[index], nil - {{- end }} - } - `, w, gk) -} - -func (gk generateKindList) EmitNodeMethodLookup(w io.Writer) { - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) Lookup(key ipld.Node) (ipld.Node, error) { - ki, err := key.AsInt() - if err != nil { - return nil, ipld.ErrInvalidKey{"got " + key.ReprKind().String() + ", need Int"} - } - return x.LookupIndex(ki) - } - `, w, gk) -} - -func (gk generateKindList) EmitNodeMethodListIterator(w io.Writer) { - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) ListIterator() ipld.ListIterator { - return &{{ .Type | mungeTypeNodeItrIdent }}{&x, 0} - } - - type {{ .Type | mungeTypeNodeItrIdent }} struct { - node *{{ .Type | mungeTypeNodeIdent }} - idx int - } - - func (itr *{{ .Type | mungeTypeNodeItrIdent }}) Next() (idx int, value ipld.Node, _ error) { - if itr.idx >= len(itr.node.x) { - return 0, nil, ipld.ErrIteratorOverread{} - } - idx = itr.idx - {{- if .Type.ValueIsNullable }} - if itr.node.x[idx] == nil { - value = ipld.Null - } else { - value = *itr.node.x[idx] - } - {{- else }} - value = itr.node.x[idx] - {{- end }} - itr.idx++ - return - } - - func (itr *{{ .Type | mungeTypeNodeItrIdent }}) Done() bool { - return itr.idx >= len(itr.node.x) - } - - `, w, gk) -} - -func (gk generateKindList) EmitNodeMethodLength(w io.Writer) { - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) Length() int { - return len(x.x) - } - `, w, gk) -} - -// --- type-semantics nodebuilder ---> - -func (gk generateKindList) EmitNodeMethodNodeBuilder(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) NodeBuilder() ipld.NodeBuilder { - return {{ .Type | mungeTypeNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateKindList) GetNodeBuilderGen() nodebuilderGenerator { - return generateNbKindList{ - gk.Type, - genKindedNbRejections_List{ - mungeTypeNodebuilderIdent(gk.Type), - string(gk.Type.Name()) + ".Builder", - }, - } -} - -type generateNbKindList struct { - Type schema.TypeList - genKindedNbRejections_List -} - -func (gk generateNbKindList) EmitNodebuilderType(w io.Writer) { - doTemplate(` - type {{ .Type | mungeTypeNodebuilderIdent }} struct{} - - `, w, gk) -} - -func (gk generateNbKindList) EmitNodebuilderConstructor(w io.Writer) { - doTemplate(` - func {{ .Type | mungeNodebuilderConstructorIdent }}() ipld.NodeBuilder { - return {{ .Type | mungeTypeNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateNbKindList) EmitNodebuilderMethodCreateList(w io.Writer) { - // Some interesting edge cases to note: - // - This builder, being all about semantics and not at all about serialization, - // is order-insensitive. - // - We don't specially handle being given 'undef' as a value. - // It just falls into the "need a schema.TypedNode" error bucket. - // - We only accept *codegenerated values* -- a schema.TypedNode created - // in the same schema universe *isn't accepted*. - // REVIEW: We could try to accept those, but it might have perf/sloc costs, - // and it's hard to imagine a user story that gets here. - // - The has-been-set-if-required validation is fun; it only requires state - // for non-optional fields, and that often gets a little hard to follow - // because it gets wedged in with other logic tables around optionality. - // REVIEW: 'x, ok := v.({{ $field.Type.Name }})' might need some stars in it... sometimes. - // TODO : review the panic of `ErrNoSuchField` in `BuilderForValue` -- - // see the comments in the NodeBuilder interface for the open questions on this topic. - doTemplate(` - func (nb {{ .Type | mungeTypeNodebuilderIdent }}) CreateList() (ipld.ListBuilder, error) { - return &{{ .Type | mungeTypeNodeListBuilderIdent }}{v:&{{ .Type | mungeTypeNodeIdent }}{}}, nil - } - - type {{ .Type | mungeTypeNodeListBuilderIdent }} struct{ - v *{{ .Type | mungeTypeNodeIdent }} - } - - func (lb *{{ .Type | mungeTypeNodeListBuilderIdent }}) growList(k int) { - oldLen := len(lb.v.x) - minLen := k + 1 - if minLen > oldLen { - // Grow. - oldCap := cap(lb.v.x) - if minLen > oldCap { - // Out of cap; do whole new backing array allocation. - // Growth maths are per stdlib's reflect.grow. - // First figure out how much growth to do. - newCap := oldCap - if newCap == 0 { - newCap = minLen - } else { - for minLen > newCap { - if minLen < 1024 { - newCap += newCap - } else { - newCap += newCap / 4 - } - } - } - // Now alloc and copy over old. - newArr := make([]{{if .Type.ValueIsNullable}}*{{end}}{{.Type.ValueType | mungeTypeNodeIdent}}, minLen, newCap) - copy(newArr, lb.v.x) - lb.v.x = newArr - } else { - // Still have cap, just extend the slice. - lb.v.x = lb.v.x[0:minLen] - } - } - } - - func (lb *{{ .Type | mungeTypeNodeListBuilderIdent }}) validate(v ipld.Node) error { - {{- if .Type.ValueIsNullable }} - if v.IsNull() { - return nil - } - {{- else}} - if v.IsNull() { - panic("type mismatch on struct field assignment: cannot assign null to non-nullable field") // FIXME need an error type for this - } - {{- end}} - tv, ok := v.(schema.TypedNode) - if !ok { - panic("need schema.TypedNode for insertion into struct") // FIXME need an error type for this - } - _, ok = v.({{ .Type.ValueType | mungeTypeNodeIdent }}) - if !ok { - panic("value for type {{.Type.Name}} is type {{.Type.ValueType.Name}}; cannot assign "+tv.Type().Name()) // FIXME need an error type for this - } - return nil - } - - func (lb *{{ .Type | mungeTypeNodeListBuilderIdent }}) unsafeSet(idx int, v ipld.Node) { - {{- if .Type.ValueIsNullable }} - if v.IsNull() { - lb.v.x[idx] = nil - return - } - {{- end}} - x := v.({{ .Type.ValueType | mungeTypeNodeIdent }}) - {{- if .Type.ValueIsNullable }} - lb.v.x[idx] = &x - {{- else}} - lb.v.x[idx] = x - {{- end}} - } - - func (lb *{{ .Type | mungeTypeNodeListBuilderIdent }}) AppendAll(vs []ipld.Node) error { - for _, v := range vs { - err := lb.validate(v) - if err != nil { - return err - } - } - off := len(lb.v.x) - new := off + len(vs) - lb.growList(new-1) - for _, v := range vs { - lb.unsafeSet(off, v) - off++ - } - return nil - } - - func (lb *{{ .Type | mungeTypeNodeListBuilderIdent }}) Append(v ipld.Node) error { - err := lb.validate(v) - if err != nil { - return err - } - off := len(lb.v.x) - lb.growList(off) - lb.unsafeSet(off, v) - return nil - } - func (lb *{{ .Type | mungeTypeNodeListBuilderIdent }}) Set(idx int, v ipld.Node) error { - err := lb.validate(v) - if err != nil { - return err - } - lb.growList(idx) - lb.unsafeSet(idx, v) - return nil - } - - func (lb *{{ .Type | mungeTypeNodeListBuilderIdent }}) Build() (ipld.Node, error) { - v := *lb.v - lb = nil - return v, nil - } - - func (lb *{{ .Type | mungeTypeNodeListBuilderIdent }}) BuilderForValue(_ int) ipld.NodeBuilder { - return {{ .Type.ValueType | mungeNodebuilderConstructorIdent }}() - } - - `, w, gk) -} - -func (gk generateNbKindList) EmitNodebuilderMethodAmendList(w io.Writer) { - doTemplate(` - func (nb {{ .Type | mungeTypeNodebuilderIdent }}) AmendList() (ipld.ListBuilder, error) { - panic("TODO later") - } - `, w, gk) -} - -// --- entrypoints to representation ---> - -func (gk generateKindList) EmitTypedNodeMethodRepresentation(w io.Writer) { - doTemplate(` - func (n {{ .Type | mungeTypeNodeIdent }}) Representation() ipld.Node { - panic("TODO representation") - } - `, w, gk) -} - -func (gk generateKindList) GetRepresentationNodeGen() nodeGenerator { - return nil // TODO of course -} diff --git a/schema/gen/go/genKindListReprList.go b/schema/gen/go/genKindListReprList.go deleted file mode 100644 index 5d408ff8..00000000 --- a/schema/gen/go/genKindListReprList.go +++ /dev/null @@ -1,3 +0,0 @@ -package gengo - -// SOON: the generation for the additional node/nodebuilder for the representation go here diff --git a/schema/gen/go/genKindString.go b/schema/gen/go/genKindString.go deleted file mode 100644 index af342f3c..00000000 --- a/schema/gen/go/genKindString.go +++ /dev/null @@ -1,87 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -func NewGeneratorForKindString(t schema.Type) typedNodeGenerator { - return generateKindString{ - t.(schema.TypeString), - generateKindedRejections_String{ - mungeTypeNodeIdent(t), - string(t.Name()), - }, - } -} - -type generateKindString struct { - 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. -} - -func (gk generateKindString) EmitNativeType(w io.Writer) { - doTemplate(` - type {{ .Type | mungeTypeNodeIdent }} struct{ x string } - - `, w, gk) -} - -func (gk generateKindString) EmitNativeAccessors(w io.Writer) { - // The node interface's `AsString` method is almost sufficient... but - // this method unboxes without needing to return an error that's statically impossible, - // which makes it easier to use in chaining. - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) String() string { - return x.x - } - `, w, gk) -} - -func (gk generateKindString) EmitNativeBuilder(w io.Writer) { - // Having a builder for scalar kinds seems overkill, but it gives us a place to do validations, - // it keeps things consistent, and it lets us do 'Build' and 'MustBuild' without two top-level symbols. - // The ergonomics of this will be worth reviewing once we get a more holistic overview of the finished system, though. - doTemplate(` - type {{ .Type | mungeTypeNodeIdent }}__Content struct { - Value string - } - - func (b {{ .Type | mungeTypeNodeIdent }}__Content) Build() ({{ .Type | mungeTypeNodeIdent }}, error) { - x := {{ .Type | mungeTypeNodeIdent }}{ - b.Value, - } - // FUTURE : want to support customizable validation. - // but 'if v, ok := x.(schema.Validatable); ok {' doesn't fly: need a way to work on concrete types. - return x, nil - } - func (b {{ .Type | mungeTypeNodeIdent }}__Content) MustBuild() {{ .Type | mungeTypeNodeIdent }} { - if x, err := b.Build(); err != nil { - panic(err) - } else { - return x - } - } - - `, w, gk) -} - -func (gk generateKindString) EmitNativeMaybe(w io.Writer) { - doTemplate(` - type Maybe{{ .Type | mungeTypeNodeIdent }} struct { - Maybe schema.Maybe - Value {{ .Type | mungeTypeNodeIdent }} - } - - func (m Maybe{{ .Type | mungeTypeNodeIdent }}) Must() {{ .Type | mungeTypeNodeIdent }} { - if m.Maybe != schema.Maybe_Value { - panic("unbox of a maybe rejected") - } - return m.Value - } - - `, w, gk) -} diff --git a/schema/gen/go/genKindStringNode.go b/schema/gen/go/genKindStringNode.go deleted file mode 100644 index 0972d8a3..00000000 --- a/schema/gen/go/genKindStringNode.go +++ /dev/null @@ -1,101 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -// --- type-semantics node interface satisfaction ---> - -func (gk generateKindString) EmitNodeType(w io.Writer) { - doTemplate(` - var _ ipld.Node = {{ .Type | mungeTypeNodeIdent }}{} - var _ schema.TypedNode = {{ .Type | mungeTypeNodeIdent }}{} - - `, w, gk) -} - -func (gk generateKindString) EmitTypedNodeMethodType(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) Type() schema.Type { - return nil /*TODO:typelit*/ - } - `, w, gk) -} - -func (gk generateKindString) EmitNodeMethodReprKind(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) ReprKind() ipld.ReprKind { - return ipld.ReprKind_String - } - `, w, gk) -} - -func (gk generateKindString) EmitNodeMethodAsString(w io.Writer) { - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) AsString() (string, error) { - return x.x, nil - } - `, w, gk) -} - -// --- type-semantics nodebuilder ---> - -func (gk generateKindString) EmitNodeMethodNodeBuilder(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) NodeBuilder() ipld.NodeBuilder { - return {{ .Type | mungeTypeNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateKindString) GetNodeBuilderGen() nodebuilderGenerator { - return generateNbKindString{ - gk.Type, - genKindedNbRejections_String{ - mungeTypeNodebuilderIdent(gk.Type), - string(gk.Type.Name()) + ".Builder", - }, - } -} - -type generateNbKindString struct { - Type schema.TypeString - genKindedNbRejections_String -} - -func (gk generateNbKindString) EmitNodebuilderType(w io.Writer) { - doTemplate(` - type {{ .Type | mungeTypeNodebuilderIdent }} struct{} - `, w, gk) -} - -func (gk generateNbKindString) EmitNodebuilderConstructor(w io.Writer) { - doTemplate(` - func {{ .Type | mungeNodebuilderConstructorIdent }}() ipld.NodeBuilder { - return {{ .Type | mungeTypeNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateNbKindString) EmitNodebuilderMethodCreateString(w io.Writer) { - doTemplate(` - func (nb {{ .Type | mungeTypeNodebuilderIdent }}) CreateString(v string) (ipld.Node, error) { - return {{ .Type | mungeTypeNodeIdent }}{v}, nil - } - `, w, gk) -} - -// --- entrypoints to representation ---> - -func (gk generateKindString) EmitTypedNodeMethodRepresentation(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) Representation() ipld.Node { - panic("TODO representation") - } - `, w, gk) -} -func (gk generateKindString) GetRepresentationNodeGen() nodeGenerator { - return nil // TODO of course -} diff --git a/schema/gen/go/genKindStringReprString.go b/schema/gen/go/genKindStringReprString.go deleted file mode 100644 index 5d408ff8..00000000 --- a/schema/gen/go/genKindStringReprString.go +++ /dev/null @@ -1,3 +0,0 @@ -package gengo - -// SOON: the generation for the additional node/nodebuilder for the representation go here diff --git a/schema/gen/go/genKindStruct.go b/schema/gen/go/genKindStruct.go deleted file mode 100644 index a668c4cb..00000000 --- a/schema/gen/go/genKindStruct.go +++ /dev/null @@ -1,103 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -func NewGeneratorForKindStruct(t schema.Type) typedNodeGenerator { - return generateKindStruct{ - t.(schema.TypeStruct), - generateKindedRejections_Map{ - mungeTypeNodeIdent(t), - string(t.Name()), - }, - } -} - -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) EmitNativeType(w io.Writer) { - // The data is actually the content type, just embedded in an unexported field, - // which means we get immutability, plus initializing the object is essentially a memmove. - doTemplate(` - type {{ .Type | mungeTypeNodeIdent }} struct{ - d {{ .Type | mungeTypeNodeIdent }}__Content - } - - `, w, gk) -} - -func (gk generateKindStruct) EmitNativeAccessors(w io.Writer) { - doTemplate(` - {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} - {{- range $field := .Type.Fields -}} - func (x {{ $type | mungeTypeNodeIdent }}) Field{{ $field.Name | titlize }}() - {{- if or $field.IsOptional $field.IsNullable }}Maybe{{end}}{{ $field.Type | mungeTypeNodeIdent }} { - return x.d.{{ $field.Name | titlize }} - } - {{end}} - - `, w, gk) -} - -func (gk generateKindStruct) EmitNativeBuilder(w io.Writer) { - doTemplate(` - type {{ .Type | mungeTypeNodeIdent }}__Content struct { - {{- range $field := .Type.Fields}} - {{ $field.Name | titlize }} {{if or $field.IsOptional $field.IsNullable }}Maybe{{end}}{{ $field.Type | mungeTypeNodeIdent }} - {{- end}} - } - - func (b {{ .Type | mungeTypeNodeIdent }}__Content) Build() ({{ .Type | mungeTypeNodeIdent }}, error) { - {{- range $field := .Type.Fields -}} - {{- if or $field.IsOptional $field.IsNullable }} - {{- /* if both modifiers present, anything goes */ -}} - {{- else if $field.IsOptional }} - if b.{{ $field.Name | titlize }}.Maybe == schema.Maybe_Null { - return {{ $field.Type | mungeTypeNodeIdent }}{}, fmt.Errorf("cannot be absent") - } - {{- else if $field.IsNullable }} - if b.{{ $field.Name | titlize }}.Maybe == schema.Maybe_Absent { - return {{ $field.Type | mungeTypeNodeIdent }}{}, fmt.Errorf("cannot be null") - } - {{- end}} - {{- end}} - x := {{ .Type | mungeTypeNodeIdent }}{b} - // FUTURE : want to support customizable validation. - // but 'if v, ok := x.(schema.Validatable); ok {' doesn't fly: need a way to work on concrete types. - return x, nil - } - func (b {{ .Type | mungeTypeNodeIdent }}__Content) MustBuild() {{ .Type | mungeTypeNodeIdent }} { - if x, err := b.Build(); err != nil { - panic(err) - } else { - return x - } - } - - `, w, gk) -} - -func (gk generateKindStruct) EmitNativeMaybe(w io.Writer) { - doTemplate(` - type Maybe{{ .Type | mungeTypeNodeIdent }} struct { - Maybe schema.Maybe - Value {{ .Type | mungeTypeNodeIdent }} - } - - func (m Maybe{{ .Type | mungeTypeNodeIdent }}) Must() {{ .Type | mungeTypeNodeIdent }} { - if m.Maybe != schema.Maybe_Value { - panic("unbox of a maybe rejected") - } - return m.Value - } - - `, w, gk) -} diff --git a/schema/gen/go/genKindStructNode.go b/schema/gen/go/genKindStructNode.go deleted file mode 100644 index c9f8c8ef..00000000 --- a/schema/gen/go/genKindStructNode.go +++ /dev/null @@ -1,319 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -// --- type-semantics node interface satisfaction ---> - -func (gk generateKindStruct) EmitNodeType(w io.Writer) { - doTemplate(` - var _ ipld.Node = {{ .Type | mungeTypeNodeIdent }}{} - var _ schema.TypedNode = {{ .Type | mungeTypeNodeIdent }}{} - - `, w, gk) -} - -func (gk generateKindStruct) EmitTypedNodeMethodType(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) Type() schema.Type { - return nil /*TODO:typelit*/ - } - `, w, gk) -} - -func (gk generateKindStruct) EmitNodeMethodReprKind(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) ReprKind() ipld.ReprKind { - return ipld.ReprKind_Map - } - `, w, gk) -} - -func (gk generateKindStruct) EmitNodeMethodLookupString(w io.Writer) { - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) LookupString(key string) (ipld.Node, error) { - switch key { - {{- range $field := .Type.Fields }} - case "{{ $field.Name }}": - {{- if $field.IsOptional }} - if x.d.{{ $field.Name | titlize }}.Maybe == schema.Maybe_Absent { - return ipld.Undef, nil - } - {{- end}} - {{- if $field.IsNullable }} - if x.d.{{ $field.Name | titlize }}.Maybe == schema.Maybe_Null { - return ipld.Null, nil - } - {{- end}} - {{- if or $field.IsOptional $field.IsNullable }} - return x.d.{{ $field.Name | titlize}}.Value, nil - {{- else}} - return x.d.{{ $field.Name | titlize}}, nil - {{- end}} - {{- end}} - default: - return nil, schema.ErrNoSuchField{Type: nil /*TODO*/, FieldName: key} - } - } - `, w, gk) -} - -func (gk generateKindStruct) EmitNodeMethodLookup(w io.Writer) { - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) Lookup(key ipld.Node) (ipld.Node, error) { - ks, err := key.AsString() - if err != nil { - return nil, ipld.ErrInvalidKey{"got " + key.ReprKind().String() + ", need string"} - } - return x.LookupString(ks) - } - `, w, gk) -} - -func (gk generateKindStruct) EmitNodeMethodMapIterator(w io.Writer) { - // Note that the typed iterator will report absent fields. - // The representation iterator, if maplike, will not. - doTemplate(` - func (x {{ .Type | mungeTypeNodeIdent }}) MapIterator() ipld.MapIterator { - return &{{ .Type | mungeTypeNodeItrIdent }}{&x, 0} - } - - type {{ .Type | mungeTypeNodeItrIdent }} struct { - node *{{ .Type | mungeTypeNodeIdent }} - idx int - } - - func (itr *{{ .Type | mungeTypeNodeItrIdent }}) 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 $field.IsOptional }} - if itr.node.d.{{ $field.Name | titlize }}.Maybe == schema.Maybe_Absent { - v = ipld.Undef - break - } - {{- end}} - {{- if $field.IsNullable }} - if itr.node.d.{{ $field.Name | titlize }}.Maybe == schema.Maybe_Null { - v = ipld.Null - break - } - {{- end}} - {{- if or $field.IsOptional $field.IsNullable }} - v = itr.node.d.{{ $field.Name | titlize}}.Value - {{- else}} - v = itr.node.d.{{ $field.Name | titlize}} - {{- end}} - {{- end}} - default: - panic("unreachable") - } - itr.idx++ - return - } - func (itr *{{ .Type | mungeTypeNodeItrIdent }}) Done() bool { - return itr.idx >= {{ len .Type.Fields }} - } - - `, w, gk) -} - -func (gk generateKindStruct) EmitNodeMethodLength(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) Length() int { - return {{ len .Type.Fields }} - } - `, w, gk) -} - -// --- type-semantics nodebuilder ---> - -func (gk generateKindStruct) EmitNodeMethodNodeBuilder(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeNodeIdent }}) NodeBuilder() ipld.NodeBuilder { - return {{ .Type | mungeTypeNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateKindStruct) GetNodeBuilderGen() nodebuilderGenerator { - return generateNbKindStruct{ - gk.Type, - genKindedNbRejections_Map{ - mungeTypeNodebuilderIdent(gk.Type), - string(gk.Type.Name()) + ".Builder", - }, - } -} - -type generateNbKindStruct struct { - Type schema.TypeStruct - genKindedNbRejections_Map -} - -func (gk generateNbKindStruct) EmitNodebuilderType(w io.Writer) { - doTemplate(` - type {{ .Type | mungeTypeNodebuilderIdent }} struct{} - - `, w, gk) -} - -func (gk generateNbKindStruct) EmitNodebuilderConstructor(w io.Writer) { - doTemplate(` - func {{ .Type | mungeNodebuilderConstructorIdent }}() ipld.NodeBuilder { - return {{ .Type | mungeTypeNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateNbKindStruct) EmitNodebuilderMethodCreateMap(w io.Writer) { - // Some interesting edge cases to note: - // - This builder, being all about semantics and not at all about serialization, - // is order-insensitive. - // - We don't specially handle being given 'undef' as a value. - // It just falls into the "need a schema.TypedNode" error bucket. - // - We only accept *codegenerated values* -- a schema.TypedNode created - // in the same schema universe *isn't accepted*. - // REVIEW: We could try to accept those, but it might have perf/sloc costs, - // and it's hard to imagine a user story that gets here. - // - The has-been-set-if-required validation is fun; it only requires state - // for non-optional fields, and that often gets a little hard to follow - // because it gets wedged in with other logic tables around optionality. - // REVIEW: 'x, ok := v.({{ $field.Type.Name }})' might need some stars in it... sometimes. - // FIXME : since the typed iterator yields undefineds, this should probably also accept them...? - // TODO : review the panic of `ErrNoSuchField` in `BuilderForValue` -- - // see the comments in the NodeBuilder interface for the open questions on this topic. - doTemplate(` - func (nb {{ .Type | mungeTypeNodebuilderIdent }}) CreateMap() (ipld.MapBuilder, error) { - mb := &{{ .Type | mungeTypeNodeMapBuilderIdent }}{v:&{{ .Type | mungeTypeNodeIdent }}{}} - {{- range $field := .Type.Fields }} - {{- if $field.IsOptional }} - mb.v.d.{{ $field.Name | titlize }}.Maybe = schema.Maybe_Absent - {{- end}} - {{- end}} - return mb, nil - } - - type {{ .Type | mungeTypeNodeMapBuilderIdent }} struct{ - v *{{ .Type | mungeTypeNodeIdent }} - {{- range $field := .Type.Fields }} - {{- if not $field.IsOptional }} - {{ $field.Name }}__isset bool - {{- end}} - {{- end}} - } - - func (mb *{{ .Type | mungeTypeNodeMapBuilderIdent }}) Insert(k, v ipld.Node) error { - ks, err := k.AsString() - if err != nil { - return ipld.ErrInvalidKey{"not a string: " + err.Error()} - } - switch ks { - {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} - {{- range $field := .Type.Fields }} - case "{{ $field.Name }}": - {{- if $field.IsNullable }} - if v.IsNull() { - mb.v.d.{{ $field.Name | titlize}}.Maybe = schema.Maybe_Null - {{- if not $field.IsOptional }} - mb.{{ $field.Name }}__isset = true - {{- end}} - return nil - } - {{- else}} - if v.IsNull() { - panic("type mismatch on struct field assignment: cannot assign null to non-nullable field") // FIXME need an error type for this - } - {{- end}} - tv, ok := v.(schema.TypedNode) - if !ok { - panic("need schema.TypedNode for insertion into struct") // FIXME need an error type for this - } - x, ok := v.({{ $field.Type | mungeTypeNodeIdent }}) - if !ok { - panic("field '{{$field.Name}}' in type {{$type.Name}} is type {{$field.Type.Name}}; cannot assign "+tv.Type().Name()) // FIXME need an error type for this - } - - {{- if or $field.IsOptional $field.IsNullable }} - mb.v.d.{{ $field.Name | titlize}}.Value = x - {{- else}} - mb.v.d.{{ $field.Name | titlize}} = x - {{- end}} - {{- if $field.IsOptional }} - mb.v.d.{{ $field.Name | titlize}}.Maybe = schema.Maybe_Value - {{- else}} - mb.{{ $field.Name }}__isset = true - {{- end}} - {{- end}} - default: - return schema.ErrNoSuchField{Type: nil /*TODO:typelit*/, FieldName: ks} - } - return nil - } - func (mb *{{ .Type | mungeTypeNodeMapBuilderIdent }}) Delete(k ipld.Node) error { - panic("TODO later") - } - func (mb *{{ .Type | mungeTypeNodeMapBuilderIdent }}) Build() (ipld.Node, error) { - {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} - {{- range $field := .Type.Fields }} - {{- if not $field.IsOptional }} - if !mb.{{ $field.Name }}__isset { - panic("missing required field '{{$field.Name}}' in building struct {{ $type.Name }}") // FIXME need an error type for this - } - {{- end}} - {{- end}} - v := *mb.v - mb = nil - return v, nil - } - func (mb *{{ .Type | mungeTypeNodeMapBuilderIdent }}) BuilderForKeys() ipld.NodeBuilder { - return _String__NodeBuilder{} - } - func (mb *{{ .Type | mungeTypeNodeMapBuilderIdent }}) BuilderForValue(ks string) ipld.NodeBuilder { - switch ks { - {{- range $field := .Type.Fields }} - case "{{ $field.Name }}": - return {{ $field.Type | mungeNodebuilderConstructorIdent }}() - {{- end}} - default: - panic(schema.ErrNoSuchField{Type: nil /*TODO:typelit*/, FieldName: ks}) - } - return nil - } - - `, w, gk) -} - -func (gk generateNbKindStruct) EmitNodebuilderMethodAmendMap(w io.Writer) { - doTemplate(` - func (nb {{ .Type | mungeTypeNodebuilderIdent }}) AmendMap() (ipld.MapBuilder, error) { - panic("TODO later") - } - `, w, gk) -} - -// --- entrypoints to representation ---> - -func (gk generateKindStruct) EmitTypedNodeMethodRepresentation(w io.Writer) { - doTemplate(` - func (n {{ .Type | mungeTypeNodeIdent }}) Representation() ipld.Node { - return {{ .Type | mungeTypeReprNodeIdent }}{&n} - } - `, w, gk) -} - -func (gk generateKindStruct) GetRepresentationNodeGen() nodeGenerator { - switch gk.Type.RepresentationStrategy().(type) { - case schema.StructRepresentation_Map: - return getStructRepresentationMapNodeGen(gk.Type) - default: - panic("missing case in switch for repr strategy for structs") - } -} diff --git a/schema/gen/go/genKindStructReprMap.go b/schema/gen/go/genKindStructReprMap.go deleted file mode 100644 index 2bfde776..00000000 --- a/schema/gen/go/genKindStructReprMap.go +++ /dev/null @@ -1,325 +0,0 @@ -package gengo - -import ( - "io" - - "github.com/ipld/go-ipld-prime/schema" -) - -func getStructRepresentationMapNodeGen(t schema.TypeStruct) nodeGenerator { - return generateStructReprMapNode{ - t, - generateKindedRejections_Map{ - mungeTypeReprNodeIdent(t), - string(t.Name()) + ".Representation", - }, - } -} - -type generateStructReprMapNode struct { - Type schema.TypeStruct - generateKindedRejections_Map -} - -func (gk generateStructReprMapNode) EmitNodeType(w io.Writer) { - doTemplate(` - var _ ipld.Node = {{ .Type | mungeTypeReprNodeIdent }}{} - - type {{ .Type | mungeTypeReprNodeIdent }} struct{ - n *{{ .Type | mungeTypeNodeIdent }} - } - - `, w, gk) -} - -func (gk generateStructReprMapNode) EmitNodeMethodReprKind(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeReprNodeIdent }}) ReprKind() ipld.ReprKind { - return ipld.ReprKind_Map - } - `, w, gk) -} - -func (gk generateStructReprMapNode) EmitNodeMethodLookupString(w io.Writer) { - // almost idential to the type-level one, just with different strings in the switch. - // TODO : support for implicits is missing. - doTemplate(` - func (rn {{ .Type | mungeTypeReprNodeIdent }}) LookupString(key string) (ipld.Node, error) { - switch key { - {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} - {{- range $field := .Type.Fields }} - case "{{ $field | $type.RepresentationStrategy.GetFieldKey }}": - {{- if $field.IsOptional }} - if rn.n.d.{{ $field.Name | titlize}}.Maybe == schema.Maybe_Absent { - return ipld.Undef, ipld.ErrNotExists{ipld.PathSegmentOfString(key)} - } - {{- end}} - {{- if $field.IsNullable }} - if rn.n.d.{{ $field.Name | titlize}}.Maybe == schema.Maybe_Null { - return ipld.Null, nil - } - {{- end}} - {{- if or $field.IsOptional $field.IsNullable }} - return rn.n.d.{{ $field.Name | titlize}}.Value, nil - {{- else}} - return rn.n.d.{{ $field.Name | titlize}}, nil - {{- end}} - {{- end}} - default: - return nil, schema.ErrNoSuchField{Type: nil /*TODO*/, FieldName: key} - } - } - `, w, gk) -} - -func (gk generateStructReprMapNode) EmitNodeMethodLookup(w io.Writer) { - doTemplate(` - func (rn {{ .Type | mungeTypeReprNodeIdent }}) Lookup(key ipld.Node) (ipld.Node, error) { - ks, err := key.AsString() - if err != nil { - return nil, ipld.ErrInvalidKey{"got " + key.ReprKind().String() + ", need string"} - } - return rn.LookupString(ks) - } - `, w, gk) -} - -func (gk generateStructReprMapNode) EmitNodeMethodMapIterator(w io.Writer) { - // Amusing note, the iterator ends up with a loop in its body, even though - // it only yields one entry pair at a time -- this is needed so we can - // use 'continue' statements to skip past optionals which are undefined. - // TODO : support for implicits is missing. - doTemplate(` - func (rn {{ .Type | mungeTypeReprNodeIdent }}) MapIterator() ipld.MapIterator { - return &{{ .Type | mungeTypeReprNodeItrIdent }}{rn.n, 0} - } - - type {{ .Type | mungeTypeReprNodeItrIdent }} struct { - node *{{ .Type | mungeTypeNodeIdent }} - idx int - } - - func (itr *{{ .Type | mungeTypeReprNodeItrIdent }}) Next() (k ipld.Node, v ipld.Node, _ error) { - if itr.idx >= {{ len .Type.Fields }} { - return nil, nil, ipld.ErrIteratorOverread{} - } - for { - switch itr.idx { - {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} - {{- range $i, $field := .Type.Fields }} - case {{ $i }}: - k = String{"{{ $field | $type.RepresentationStrategy.GetFieldKey }}"} - {{- if $field.IsOptional }} - if itr.node.d.{{ $field.Name | titlize}}.Maybe == schema.Maybe_Absent { - itr.idx++ - continue - } - {{- end}} - {{- if $field.IsNullable }} - if itr.node.d.{{ $field.Name | titlize}}.Maybe == schema.Maybe_Null { - v = ipld.Null - break - } - {{- end}} - {{- if or $field.IsOptional $field.IsNullable }} - v = itr.node.d.{{ $field.Name | titlize}}.Value - {{- else}} - v = itr.node.d.{{ $field.Name | titlize}} - {{- end}} - {{- end}} - default: - panic("unreachable") - } - } - itr.idx++ - return - } - func (itr *{{ .Type | mungeTypeReprNodeItrIdent }}) Done() bool { - return itr.idx >= {{ len .Type.Fields }} - } - - `, w, gk) - -} - -func (gk generateStructReprMapNode) EmitNodeMethodLength(w io.Writer) { - // This is fun: it has to count down for any unset optional fields. - // TODO : support for implicits is missing. - doTemplate(` - func (rn {{ .Type | mungeTypeReprNodeIdent }}) Length() int { - l := {{ len .Type.Fields }} - {{- range $field := .Type.Fields }} - {{- if $field.IsOptional }} - if rn.n.d.{{ $field.Name | titlize}}.Maybe == schema.Maybe_Absent { - l-- - } - {{- end}} - {{- end}} - return l - } - `, w, gk) -} - -func (gk generateStructReprMapNode) EmitNodeMethodNodeBuilder(w io.Writer) { - doTemplate(` - func ({{ .Type | mungeTypeReprNodeIdent }}) NodeBuilder() ipld.NodeBuilder { - return {{ .Type | mungeTypeReprNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateStructReprMapNode) GetNodeBuilderGen() nodebuilderGenerator { - return generateStructReprMapNb{ - gk.Type, - genKindedNbRejections_Map{ - mungeTypeReprNodebuilderIdent(gk.Type), - string(gk.Type.Name()) + ".Representation.Builder", - }, - } -} - -type generateStructReprMapNb struct { - Type schema.TypeStruct - genKindedNbRejections_Map -} - -func (gk generateStructReprMapNb) EmitNodebuilderType(w io.Writer) { - // Note there's no need to put the reprKind in the name of the type - // we generate here: there's only one representation per type. - // (We *could* munge the reprkind in for debug symbol reading, - // but at present it hasn't seemed warranted.) - doTemplate(` - type {{ .Type | mungeTypeReprNodebuilderIdent }} struct{} - - `, w, gk) -} - -func (gk generateStructReprMapNb) EmitNodebuilderConstructor(w io.Writer) { - doTemplate(` - func {{ .Type | mungeReprNodebuilderConstructorIdent }}() ipld.NodeBuilder { - return {{ .Type | mungeTypeReprNodebuilderIdent }}{} - } - `, w, gk) -} - -func (gk generateStructReprMapNb) EmitNodebuilderMethodCreateMap(w io.Writer) { - // Much of these looks the same as the type-level builders. Key differences: - // - We interact with the rename directives here. - // - The "__isset" bools are generated for *all* fields -- we need these - // to check if a key is repeated, so we can reject that. - // Worth mentioning: we could also choose *not* to check this, instead - // insisting it's a codec layer concern. This needs revisiting; - // at present I'm choosing "defense in depth", because trying to - // reason out the perf and usability implications in advance has - // yielded a huge matrix of concerns and no single clear gradient. - // TODO : support for implicits is missing. - // TODO : review the panic of `ErrNoSuchField` in `BuilderForValue` -- - // see the comments in the NodeBuilder interface for the open questions on this topic. - doTemplate(` - func (nb {{ .Type | mungeTypeReprNodebuilderIdent }}) CreateMap() (ipld.MapBuilder, error) { - mb := &{{ .Type | mungeTypeReprNodeMapBuilderIdent }}{v:&{{ .Type | mungeTypeNodeIdent }}{}} - {{- range $field := .Type.Fields }} - {{- if $field.IsOptional }} - mb.v.d.{{ $field.Name | titlize }}.Maybe = schema.Maybe_Absent - {{- end}} - {{- end}} - return mb, nil - } - - type {{ .Type | mungeTypeReprNodeMapBuilderIdent }} struct{ - v *{{ .Type | mungeTypeNodeIdent }} - {{- range $field := .Type.Fields }} - {{ $field.Name }}__isset bool - {{- end}} - } - - func (mb *{{ .Type | mungeTypeReprNodeMapBuilderIdent }}) Insert(k, v ipld.Node) error { - ks, err := k.AsString() - if err != nil { - return ipld.ErrInvalidKey{"not a string: " + err.Error()} - } - switch ks { - {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} - {{- range $field := .Type.Fields }} - case "{{ $field | $type.RepresentationStrategy.GetFieldKey }}": - if mb.{{ $field.Name }}__isset { - panic("repeated assignment to field") // FIXME need an error type for this - } - {{- if $field.IsNullable }} - if v.IsNull() { - mb.v.d.{{ $field.Name | titlize}}.Maybe = schema.Maybe_Null - mb.{{ $field.Name }}__isset = true - return nil - } - {{- else}} - if v.IsNull() { - panic("type mismatch on struct field assignment: cannot assign null to non-nullable field") // FIXME need an error type for this - } - {{- end}} - tv, ok := v.(schema.TypedNode) - if !ok { - panic("need schema.TypedNode for insertion into struct") // FIXME need an error type for this - } - x, ok := v.({{ $field.Type | mungeTypeNodeIdent }}) - if !ok { - panic("field '{{$field.Name}}' (key: '{{ $field | $type.RepresentationStrategy.GetFieldKey }}') in type {{$type.Name}} is type {{$field.Type.Name}}; cannot assign "+tv.Type().Name()) // FIXME need an error type for this - } - - {{- if or $field.IsOptional $field.IsNullable }} - mb.v.d.{{ $field.Name | titlize}}.Value = x - {{- else}} - mb.v.d.{{ $field.Name | titlize}} = x - {{- end}} - {{- if $field.IsOptional }} - mb.v.d.{{ $field.Name | titlize}}.Maybe = schema.Maybe_Value - {{- end}} - mb.{{ $field.Name }}__isset = true - {{- end}} - default: - return schema.ErrNoSuchField{Type: nil /*TODO:typelit*/, FieldName: ks} - } - return nil - } - func (mb *{{ .Type | mungeTypeReprNodeMapBuilderIdent }}) Delete(k ipld.Node) error { - panic("TODO later") - } - func (mb *{{ .Type | mungeTypeReprNodeMapBuilderIdent }}) Build() (ipld.Node, error) { - {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} - {{- range $field := .Type.Fields }} - {{- if not $field.IsOptional }} - if !mb.{{ $field.Name }}__isset { - panic("missing required field '{{$field.Name}}' (key: '{{ $field | $type.RepresentationStrategy.GetFieldKey }}') in building struct {{ $type.Name }}") // FIXME need an error type for this - } - {{- end}} - {{- end}} - v := mb.v - mb = nil - return v, nil - } - func (mb *{{ .Type | mungeTypeReprNodeMapBuilderIdent }}) BuilderForKeys() ipld.NodeBuilder { - return _String__NodeBuilder{} - } - func (mb *{{ .Type | mungeTypeReprNodeMapBuilderIdent }}) BuilderForValue(ks string) ipld.NodeBuilder { - switch ks { - {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} - {{- range $field := .Type.Fields }} - case "{{ $field | $type.RepresentationStrategy.GetFieldKey }}": - return {{ $field.Type | mungeNodebuilderConstructorIdent }}() - {{- end}} - default: - panic(schema.ErrNoSuchField{Type: nil /*TODO:typelit*/, FieldName: ks}) - } - return nil - } - - `, w, gk) - -} - -func (gk generateStructReprMapNb) EmitNodebuilderMethodAmendMap(w io.Writer) { - doTemplate(` - func (nb {{ .Type | mungeTypeReprNodebuilderIdent }}) AmendMap() (ipld.MapBuilder, error) { - panic("TODO later") - } - `, w, gk) -} diff --git a/schema/gen/go/genLink.go b/schema/gen/go/genLink.go new file mode 100644 index 00000000..665cef5d --- /dev/null +++ b/schema/gen/go/genLink.go @@ -0,0 +1,119 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +type linkGenerator struct { + AdjCfg *AdjunctCfg + mixins.LinkTraits + PkgName string + Type schema.TypeLink +} + +func (linkGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +// --- native content and specializations ---> + +func (g linkGenerator) EmitNativeType(w io.Writer) { + emitNativeType_scalar(w, g.AdjCfg, g) +} +func (g linkGenerator) EmitNativeAccessors(w io.Writer) { + emitNativeAccessors_scalar(w, g.AdjCfg, g) +} +func (g linkGenerator) EmitNativeBuilder(w io.Writer) { + emitNativeBuilder_scalar(w, g.AdjCfg, g) +} + +func (g linkGenerator) EmitNativeMaybe(w io.Writer) { + emitNativeMaybe(w, g.AdjCfg, g) +} + +// --- type info ---> + +func (g linkGenerator) EmitTypeConst(w io.Writer) { + doTemplate(` + // TODO EmitTypeConst + `, w, g.AdjCfg, g) +} + +// --- TypedNode linkerface satisfaction ---> + +func (g linkGenerator) EmitTypedNodeMethodType(w io.Writer) { + doTemplate(` + func ({{ .Type | TypeSymbol }}) Type() schema.Type { + return nil /*TODO:typelit*/ + } + `, w, g.AdjCfg, g) +} + +func (g linkGenerator) EmitTypedNodeMethodRepresentation(w io.Writer) { + emitTypicalTypedNodeMethodRepresentation(w, g.AdjCfg, g) +} + +// --- Node linkerface satisfaction ---> + +func (g linkGenerator) EmitNodeType(w io.Writer) { + // No additional types needed. Methods all attach to the native type. +} +func (g linkGenerator) EmitNodeTypeAssertions(w io.Writer) { + emitNodeTypeAssertions_typical(w, g.AdjCfg, g) +} +func (g linkGenerator) EmitNodeMethodAsLink(w io.Writer) { + emitNodeMethodAsKind_scalar(w, g.AdjCfg, g) +} +func (g linkGenerator) EmitNodeMethodStyle(w io.Writer) { + emitNodeMethodStyle_typical(w, g.AdjCfg, g) +} +func (g linkGenerator) EmitNodeStyleType(w io.Writer) { + emitNodeStyleType_typical(w, g.AdjCfg, g) +} + +// --- NodeBuilder and NodeAssembler ---> + +func (g linkGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return linkBuilderGenerator{ + g.AdjCfg, + mixins.LinkAssemblerTraits{ + g.PkgName, + g.TypeName, + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__", + }, + g.PkgName, + g.Type, + } +} + +type linkBuilderGenerator struct { + AdjCfg *AdjunctCfg + mixins.LinkAssemblerTraits + PkgName string + Type schema.TypeLink +} + +func (linkBuilderGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +func (g linkBuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g linkBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(w, g.AdjCfg, g) +} +func (g linkBuilderGenerator) EmitNodeAssemblerType(w io.Writer) { + emitNodeAssemblerType_scalar(w, g.AdjCfg, g) +} +func (g linkBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_scalar(w, g.AdjCfg, g) +} +func (g linkBuilderGenerator) EmitNodeAssemblerMethodAssignLink(w io.Writer) { + emitNodeAssemblerMethodAssignKind_scalar(w, g.AdjCfg, g) +} +func (g linkBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) { + emitNodeAssemblerMethodAssignNode_scalar(w, g.AdjCfg, g) +} +func (g linkBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + // Nothing needed here for link kinds. +} diff --git a/schema/gen/go/genLinkReprLink.go b/schema/gen/go/genLinkReprLink.go new file mode 100644 index 00000000..e68488cb --- /dev/null +++ b/schema/gen/go/genLinkReprLink.go @@ -0,0 +1,108 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +var _ TypeGenerator = &linkReprLinkGenerator{} + +func NewLinkReprLinkGenerator(pkgName string, typ schema.TypeLink, adjCfg *AdjunctCfg) TypeGenerator { + return linkReprLinkGenerator{ + linkGenerator{ + adjCfg, + mixins.LinkTraits{ + pkgName, + string(typ.Name()), + adjCfg.TypeSymbol(typ), + }, + pkgName, + typ, + }, + } +} + +type linkReprLinkGenerator struct { + linkGenerator +} + +func (g linkReprLinkGenerator) GetRepresentationNodeGen() NodeGenerator { + return linkReprLinkReprGenerator{ + g.AdjCfg, + g.Type, + } +} + +type linkReprLinkReprGenerator struct { + AdjCfg *AdjunctCfg + Type schema.TypeLink +} + +func (g linkReprLinkReprGenerator) 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 linkReprLinkReprGenerator) EmitNodeTypeAssertions(w io.Writer) { + doTemplate(` + var _ ipld.Node = &_{{ .Type | TypeSymbol }}__Repr{} + `, w, g.AdjCfg, g) +} +func (linkReprLinkReprGenerator) EmitNodeMethodReprKind(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodLookupString(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodLookup(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodLookupIndex(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodLookupSegment(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodMapIterator(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodListIterator(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodLength(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodIsUndefined(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodIsNull(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodAsBool(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodAsInt(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodAsFloat(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodAsString(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodAsBytes(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodAsLink(io.Writer) {} +func (linkReprLinkReprGenerator) EmitNodeMethodStyle(io.Writer) {} +func (g linkReprLinkReprGenerator) 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 linkReprLinkReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return linkReprLinkReprBuilderGenerator{g.AdjCfg, g.Type} +} + +type linkReprLinkReprBuilderGenerator struct { + AdjCfg *AdjunctCfg + Type schema.TypeLink +} + +func (linkReprLinkReprBuilderGenerator) EmitNodeBuilderType(io.Writer) {} +func (linkReprLinkReprBuilderGenerator) EmitNodeBuilderMethods(io.Writer) {} +func (g linkReprLinkReprBuilderGenerator) EmitNodeAssemblerType(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 }}__ReprAssembler = _{{ .Type | TypeSymbol }}__Assembler + `, w, g.AdjCfg, g) +} +func (linkReprLinkReprBuilderGenerator) EmitNodeAssemblerMethodBeginMap(io.Writer) {} +func (linkReprLinkReprBuilderGenerator) EmitNodeAssemblerMethodBeginList(io.Writer) {} +func (linkReprLinkReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(io.Writer) {} +func (linkReprLinkReprBuilderGenerator) EmitNodeAssemblerMethodAssignBool(io.Writer) {} +func (linkReprLinkReprBuilderGenerator) EmitNodeAssemblerMethodAssignInt(io.Writer) {} +func (linkReprLinkReprBuilderGenerator) EmitNodeAssemblerMethodAssignFloat(io.Writer) {} +func (linkReprLinkReprBuilderGenerator) EmitNodeAssemblerMethodAssignString(io.Writer) {} +func (linkReprLinkReprBuilderGenerator) EmitNodeAssemblerMethodAssignBytes(io.Writer) {} +func (linkReprLinkReprBuilderGenerator) EmitNodeAssemblerMethodAssignLink(io.Writer) {} +func (linkReprLinkReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(io.Writer) {} +func (linkReprLinkReprBuilderGenerator) EmitNodeAssemblerMethodStyle(io.Writer) {} +func (linkReprLinkReprBuilderGenerator) EmitNodeAssemblerOtherBits(io.Writer) {} diff --git a/schema/gen/go/genList.go b/schema/gen/go/genList.go new file mode 100644 index 00000000..c76a55e9 --- /dev/null +++ b/schema/gen/go/genList.go @@ -0,0 +1,227 @@ +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 +} + +func (listGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +// --- 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) { + // FUTURE: come back to this -- surely something nice can be done here. +} + +func (g listGenerator) EmitNativeBuilder(w io.Writer) { + // FUTURE: come back to this -- 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) { + emitTypicalTypedNodeMethodRepresentation(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) { + emitNodeTypeAssertions_typical(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) { + emitNodeMethodStyle_typical(w, g.AdjCfg, g) +} + +func (g listGenerator) EmitNodeStyleType(w io.Writer) { + emitNodeStyleType_typical(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 (listBuilderGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +func (g listBuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g listBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(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) { + emitNodeAssemblerMethodBeginList_listoid(w, g.AdjCfg, g) +} +func (g listBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_recursive(w, g.AdjCfg, g) +} +func (g listBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) { + emitNodeAssemblerMethodAssignNode_listoid(w, g.AdjCfg, g) +} +func (g listBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + emitNodeAssemblerHelper_listoid_tidyHelper(w, g.AdjCfg, g) + emitNodeAssemblerHelper_listoid_listAssemblerMethods(w, g.AdjCfg, g) +} diff --git a/schema/gen/go/genListReprList.go b/schema/gen/go/genListReprList.go new file mode 100644 index 00000000..95e7f698 --- /dev/null +++ b/schema/gen/go/genListReprList.go @@ -0,0 +1,206 @@ +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 (listReprListReprGenerator) IsRepr() bool { return true } // hint used in some generalized templates. + +func (g listReprListReprGenerator) EmitNodeType(w io.Writer) { + // Even though this is a "natural" representation... we need a new type here, + // because lists are recursive, and so all our functions that access + // children need to remember to return the representation node of those child values. + // It's still structurally the same, though (and we'll be able to cast in the methodset pattern). + // Error-thunking methods also have a different string in their error, so those are unique even if they don't seem particularly interesting. + 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 (g listReprListReprGenerator) EmitNodeMethodLookup(w io.Writer) { + // Null is also already a branch in the method we're calling; hopefully the compiler inlines and sees this and DTRT. + // REVIEW: these unchecked casts are definitely safe at compile time, but I'm not sure if the compiler considers that provable, + // so we should investigate if there's any runtime checks injected here that waste time. If so: write this with more gsloc to avoid :( + doTemplate(` + func (nr *_{{ .Type | TypeSymbol }}__Repr) Lookup(k ipld.Node) (ipld.Node, error) { + v, err := ({{ .Type | TypeSymbol }})(nr).Lookup(k) + if err != nil || v == ipld.Null { + return v, err + } + return v.({{ .Type.ValueType | TypeSymbol}}).Representation(), nil + } + `, w, g.AdjCfg, g) + +} + +func (g listReprListReprGenerator) EmitNodeMethodLookupIndex(w io.Writer) { + doTemplate(` + func (nr *_{{ .Type | TypeSymbol }}__Repr) LookupIndex(idx int) (ipld.Node, error) { + v, err := ({{ .Type | TypeSymbol }})(nr).LookupIndex(idx) + if err != nil || v == ipld.Null { + return v, err + } + return v.({{ .Type.ValueType | TypeSymbol}}).Representation(), nil + } + `, w, g.AdjCfg, g) +} + +func (g listReprListReprGenerator) EmitNodeMethodListIterator(w io.Writer) { + // FUTURE: trying to get this to share the preallocated memory if we get iterators wedged into their node slab will be ... fun. + doTemplate(` + func (nr *_{{ .Type | TypeSymbol }}__Repr) ListIterator() ipld.ListIterator { + return &_{{ .Type | TypeSymbol }}__ReprListItr{({{ .Type | TypeSymbol }})(nr), 0} + } + + type _{{ .Type | TypeSymbol }}__ReprListItr _{{ .Type | TypeSymbol }}__ListItr + + func (itr *_{{ .Type | TypeSymbol }}__ReprListItr) Next() (idx int, v ipld.Node, err error) { + idx, v, err = (*_{{ .Type | TypeSymbol }}__ListItr)(itr).Next() + if err != nil || v == ipld.Null { + return + } + return idx, v.({{ .Type.ValueType | TypeSymbol}}).Representation(), nil + } + func (itr *_{{ .Type | TypeSymbol }}__ReprListItr) Done() bool { + return (*_{{ .Type | TypeSymbol }}__ListItr)(itr).Done() + } + + `, w, g.AdjCfg, g) +} + +func (g listReprListReprGenerator) EmitNodeMethodLength(w io.Writer) { + doTemplate(` + func (rn *_{{ .Type | TypeSymbol }}__Repr) Length() int { + return len(rn.x) + } + `, w, g.AdjCfg, g) +} + +func (g listReprListReprGenerator) EmitNodeMethodStyle(w io.Writer) { + emitNodeMethodStyle_typical(w, g.AdjCfg, g) +} + +func (g listReprListReprGenerator) EmitNodeStyleType(w io.Writer) { + emitNodeStyleType_typical(w, g.AdjCfg, g) +} + +// --- NodeBuilder and NodeAssembler ---> + +func (g listReprListReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return listReprListReprBuilderGenerator{ + g.AdjCfg, + mixins.ListAssemblerTraits{ + g.PkgName, + g.TypeName, + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr", + }, + g.PkgName, + g.Type, + } +} + +type listReprListReprBuilderGenerator struct { + AdjCfg *AdjunctCfg + mixins.ListAssemblerTraits + PkgName string + Type schema.TypeList +} + +func (listReprListReprBuilderGenerator) IsRepr() bool { return true } // hint used in some generalized templates. + +func (g listReprListReprBuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g listReprListReprBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(w, g.AdjCfg, g) +} +func (g listReprListReprBuilderGenerator) 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. + // + // Note that this textually similar to the type-level assembler, but because it embeds the repr assembler for the child types, + // it might be *significantly* different in size and memory layout in that trailing part of the struct. + doTemplate(` + type _{{ .Type | TypeSymbol }}__ReprAssembler struct { + w *_{{ .Type | TypeSymbol }} + m *schema.Maybe + state laState + + {{ if not .Type.ValueIsNullable }}cm schema.Maybe{{end}} + va _{{ .Type.ValueType | TypeSymbol }}__ReprAssembler + } + + func (na *_{{ .Type | TypeSymbol }}__ReprAssembler) reset() { + na.state = laState_initial + na.va.reset() + } + `, w, g.AdjCfg, g) +} +func (g listReprListReprBuilderGenerator) EmitNodeAssemblerMethodBeginList(w io.Writer) { + emitNodeAssemblerMethodBeginList_listoid(w, g.AdjCfg, g) +} +func (g listReprListReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_recursive(w, g.AdjCfg, g) +} +func (g listReprListReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) { + emitNodeAssemblerMethodAssignNode_listoid(w, g.AdjCfg, g) +} +func (g listReprListReprBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + emitNodeAssemblerHelper_listoid_tidyHelper(w, g.AdjCfg, g) + emitNodeAssemblerHelper_listoid_listAssemblerMethods(w, g.AdjCfg, g) +} diff --git a/schema/gen/go/genMap.go b/schema/gen/go/genMap.go new file mode 100644 index 00000000..ef202463 --- /dev/null +++ b/schema/gen/go/genMap.go @@ -0,0 +1,307 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +type mapGenerator struct { + AdjCfg *AdjunctCfg + mixins.MapTraits + PkgName string + Type schema.TypeMap +} + +func (mapGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +// --- native content and specializations ---> + +func (g mapGenerator) EmitNativeType(w io.Writer) { + // Maps do double bookkeeping. + // - 'm' is used for quick lookup. + // - 't' is used for both for order maintainence, and for allocation amortization for both keys and values. + // Note that the key in 'm' is *not* a pointer. + // The value in 'm' is a pointer into 't' (except when it's a maybe; maybes are already pointers). + doTemplate(` + type _{{ .Type | TypeSymbol }} struct { + m map[_{{ .Type.KeyType | TypeSymbol }}]{{if .Type.ValueIsNullable }}Maybe{{else}}*_{{end}}{{ .Type.ValueType | TypeSymbol }} + t []_{{ .Type | TypeSymbol }}__entry + } + type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }} + `, w, g.AdjCfg, g) + // - address of 'k' is used when we return keys as nodes, such as in iterators. + // Having these in the 't' slice above amortizes moving all of them to heap at once, + // which makes iterators that have to return them as an interface much (much) lower cost -- no 'runtime.conv*' pain. + // - address of 'v' is used in map values, to return, and of course also in iterators. + doTemplate(` + type _{{ .Type | TypeSymbol }}__entry struct { + k _{{ .Type.KeyType | TypeSymbol }} + v _{{ .Type.ValueType | TypeSymbol }}{{if .Type.ValueIsNullable }}__Maybe{{end}} + } + `, w, g.AdjCfg, g) +} + +func (g mapGenerator) EmitNativeAccessors(w io.Writer) { + // Generate a speciated Lookup as well as LookupMaybe method. + // The LookupMaybe method is needed if the map value is nullable and you're going to distinguish nulls + // (and may also be convenient if you would rather handle Maybe_Absent than an error for not-found). + // The Lookup method works fine if the map value isn't nullable + // (and should be preferred in that case, because boxing something into a maybe when it wasn't already stored that way costs an alloc(!), + // and may additionally incur a memcpy if the maybe for the value type doesn't use pointers internally). + // REVIEW: is there a way we can make this less twisty? it is VERY unfortunate if the user has to know what sort of map it is to know which method to prefer. + // Maybe the Lookup method on maps that have nullable values should just always have a MaybeT return type? + // But then this means the Lookup method doesn't "need" an error as part of its return signiture, which just shuffles differences around. + doTemplate(` + func (n *_{{ .Type | TypeSymbol }}) LookupMaybe(k {{ .Type.KeyType | TypeSymbol }}) Maybe{{ .Type.ValueType | TypeSymbol }} { + v, ok := n.m[*k] + if !ok { + return &_{{ .Type | TypeSymbol }}__valueAbsent + } + {{- if .Type.ValueIsNullable }} + return v + {{- else}} + return &_{{ .Type.ValueType | TypeSymbol }}__Maybe{ + m: schema.Maybe_Value, + v: {{ if not (MaybeUsesPtr .Type.ValueType) }}*{{end}}v, + } + {{- end}} + } + + var _{{ .Type | TypeSymbol }}__valueAbsent = _{{ .Type.ValueType | TypeSymbol }}__Maybe{m:schema.Maybe_Absent} + + // TODO generate also a plain Lookup method that doesn't box and alloc if this type contains non-nullable values! + `, w, g.AdjCfg, g) + // FUTURE: also a speciated iterator? +} + +func (g mapGenerator) EmitNativeBuilder(w io.Writer) { + // Not yet clear what exactly might be most worth emitting here. +} + +func (g mapGenerator) EmitNativeMaybe(w io.Writer) { + emitNativeMaybe(w, g.AdjCfg, g) +} + +// --- type info ---> + +func (g mapGenerator) EmitTypeConst(w io.Writer) { + doTemplate(` + // TODO EmitTypeConst + `, w, g.AdjCfg, g) +} + +// --- TypedNode interface satisfaction ---> + +func (g mapGenerator) EmitTypedNodeMethodType(w io.Writer) { + doTemplate(` + func ({{ .Type | TypeSymbol }}) Type() schema.Type { + return nil /*TODO:typelit*/ + } + `, w, g.AdjCfg, g) +} + +func (g mapGenerator) EmitTypedNodeMethodRepresentation(w io.Writer) { + emitTypicalTypedNodeMethodRepresentation(w, g.AdjCfg, g) +} + +// --- Node interface satisfaction ---> + +func (g mapGenerator) EmitNodeType(w io.Writer) { + // No additional types needed. Methods all attach to the native type. +} + +func (g mapGenerator) EmitNodeTypeAssertions(w io.Writer) { + emitNodeTypeAssertions_typical(w, g.AdjCfg, g) +} + +func (g mapGenerator) EmitNodeMethodLookupString(w io.Writer) { + // What should be coercible in which directions (and how surprising that is) is an interesting question. + // Most of the answer comes from considering what needs to be possible when working with PathSegment: + // we *must* be able to accept a string in a PathSegment and be able to use it to navigate a map -- even if the map has complex keys. + // For that to work out, it means if the key type doesn't have a string type kind, we must be willing to reach into its representation and use the fromString there. + // If the key type *does* have a string kind at the type level, we'll use that; no need to consider going through the representation. + doTemplate(` + func (n {{ .Type | TypeSymbol }}) LookupString(k string) (ipld.Node, error) { + var k2 _{{ .Type.KeyType | TypeSymbol }} + {{- if eq .Type.KeyType.Kind.String "String" }} + if err := (_{{ .Type.KeyType | TypeSymbol }}__Style{}).fromString(&k2, k); err != nil { + return nil, err // TODO wrap in some kind of ErrInvalidKey + } + {{- else}} + if err := (_{{ .Type.KeyType | TypeSymbol }}__ReprStyle{}).fromString(&k2, k); err != nil { + return nil, err // TODO wrap in some kind of ErrInvalidKey + } + {{- end}} + v, exists := n.m[k2] + if !exists { + return nil, ipld.ErrNotExists{ipld.PathSegmentOfString(k)} + } + {{- 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 mapGenerator) EmitNodeMethodLookup(w io.Writer) { + // LookupNode will procede by cast if it can; or simply error if that doesn't work. + // There's no attempt to turn the node (or its repr) into a string and then reify that into a key; + // if you used a Node here, you should've meant it. + // REVIEW: by comparison structs will coerce anything stringish silently...! 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) { + k2, ok := k.({{ .Type.KeyType | TypeSymbol }}) + if !ok { + panic("todo invalid key type error") + // 'ipld.ErrInvalidKey{TypeName:"{{ .PkgName }}.{{ .Type.Name }}", Key:&_String{k}}' doesn't quite cut it: need room to explain the type, and it's not guaranteed k can be turned into a string at all + } + v, exists := n.m[*k2] + if !exists { + return nil, ipld.ErrNotExists{ipld.PathSegmentOfString(k2.String())} + } + {{- 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 mapGenerator) EmitNodeMethodMapIterator(w io.Writer) { + doTemplate(` + func (n {{ .Type | TypeSymbol }}) MapIterator() ipld.MapIterator { + return &_{{ .Type | TypeSymbol }}__MapItr{n, 0} + } + + type _{{ .Type | TypeSymbol }}__MapItr struct { + n {{ .Type | TypeSymbol }} + idx int + } + + func (itr *_{{ .Type | TypeSymbol }}__MapItr) Next() (k ipld.Node, v ipld.Node, _ error) { + if itr.idx >= len(itr.n.t) { + return nil, nil, ipld.ErrIteratorOverread{} + } + x := &itr.n.t[itr.idx] + k = &x.k + {{- if .Type.ValueIsNullable }} + switch x.v.m { + case schema.Maybe_Null: + v = ipld.Null + case schema.Maybe_Value: + v = {{ if not (MaybeUsesPtr .Type.ValueType) }}&{{end}}x.v.v + } + {{- else}} + v = &x.v + {{- end}} + itr.idx++ + return + } + func (itr *_{{ .Type | TypeSymbol }}__MapItr) Done() bool { + return itr.idx >= len(itr.n.t) + } + + `, w, g.AdjCfg, g) +} + +func (g mapGenerator) EmitNodeMethodLength(w io.Writer) { + doTemplate(` + func (n {{ .Type | TypeSymbol }}) Length() int { + return len(n.t) + } + `, w, g.AdjCfg, g) +} + +func (g mapGenerator) EmitNodeMethodStyle(w io.Writer) { + emitNodeMethodStyle_typical(w, g.AdjCfg, g) +} + +func (g mapGenerator) EmitNodeStyleType(w io.Writer) { + emitNodeStyleType_typical(w, g.AdjCfg, g) +} + +// --- NodeBuilder and NodeAssembler ---> + +func (g mapGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return mapBuilderGenerator{ + g.AdjCfg, + mixins.MapAssemblerTraits{ + g.PkgName, + g.TypeName, + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__", + }, + g.PkgName, + g.Type, + } +} + +type mapBuilderGenerator struct { + AdjCfg *AdjunctCfg + mixins.MapAssemblerTraits + PkgName string + Type schema.TypeMap +} + +func (mapBuilderGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +func (g mapBuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g mapBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(w, g.AdjCfg, g) +} +func (g mapBuilderGenerator) 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 map state (the broad transitions between null, start-map, and finish are handled by 'm' for consistency.) + // - there's no equivalent of the 'f' (**f**ocused next) field in struct assemblers -- that's implicitly the last row of the 'w.t'. + // + // - 'cm' is **c**hild **m**aybe and is used for the completion message from children. + // It's used for values if values aren't allowed to be nullable and thus don't have their own per-value maybe slot we can use. + // It's always used for key assembly, since keys are never allowed to be nullable and thus etc. + // - 'ka' and 'va' are the key assembler and value assembler respectively. + // Perhaps surprisingly, we can get away with using the assemblers for each type just straight up, no wrappers necessary; + // All of the required magic is handled through maybe pointers and some tidy methods used during state transitions. + doTemplate(` + type _{{ .Type | TypeSymbol }}__Assembler struct { + w *_{{ .Type | TypeSymbol }} + m *schema.Maybe + state maState + + cm schema.Maybe + ka _{{ .Type.KeyType | TypeSymbol }}__Assembler + va _{{ .Type.ValueType | TypeSymbol }}__Assembler + } + + func (na *_{{ .Type | TypeSymbol }}__Assembler) reset() { + na.state = maState_initial + na.ka.reset() + na.va.reset() + } + `, w, g.AdjCfg, g) +} +func (g mapBuilderGenerator) EmitNodeAssemblerMethodBeginMap(w io.Writer) { + emitNodeAssemblerMethodBeginMap_mapoid(w, g.AdjCfg, g) +} +func (g mapBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_recursive(w, g.AdjCfg, g) +} +func (g mapBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) { + emitNodeAssemblerMethodAssignNode_mapoid(w, g.AdjCfg, g) +} +func (g mapBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + emitNodeAssemblerHelper_mapoid_keyTidyHelper(w, g.AdjCfg, g) + emitNodeAssemblerHelper_mapoid_valueTidyHelper(w, g.AdjCfg, g) + emitNodeAssemblerHelper_mapoid_mapAssemblerMethods(w, g.AdjCfg, g) +} diff --git a/schema/gen/go/genMapReprMap.go b/schema/gen/go/genMapReprMap.go new file mode 100644 index 00000000..ecf2988d --- /dev/null +++ b/schema/gen/go/genMapReprMap.go @@ -0,0 +1,206 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +var _ TypeGenerator = &mapReprMapGenerator{} + +func NewMapReprMapGenerator(pkgName string, typ schema.TypeMap, adjCfg *AdjunctCfg) TypeGenerator { + return mapReprMapGenerator{ + mapGenerator{ + adjCfg, + mixins.MapTraits{ + pkgName, + string(typ.Name()), + adjCfg.TypeSymbol(typ), + }, + pkgName, + typ, + }, + } +} + +type mapReprMapGenerator struct { + mapGenerator +} + +func (g mapReprMapGenerator) GetRepresentationNodeGen() NodeGenerator { + return mapReprMapReprGenerator{ + g.AdjCfg, + mixins.MapTraits{ + g.PkgName, + string(g.Type.Name()) + ".Repr", + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr", + }, + g.PkgName, + g.Type, + } +} + +type mapReprMapReprGenerator struct { + AdjCfg *AdjunctCfg + mixins.MapTraits + PkgName string + Type schema.TypeMap +} + +func (mapReprMapReprGenerator) IsRepr() bool { return true } // hint used in some generalized templates. + +func (g mapReprMapReprGenerator) EmitNodeType(w io.Writer) { + // Even though this is a "natural" representation... we need a new type here, + // because maps are recursive, and so all our functions that access + // children need to remember to return the representation node of those child values. + // It's still structurally the same, though (and we'll be able to cast in the methodset pattern). + // Error-thunking methods also have a different string in their error, so those are unique even if they don't seem particularly interesting. + doTemplate(` + type _{{ .Type | TypeSymbol }}__Repr _{{ .Type | TypeSymbol }} + `, w, g.AdjCfg, g) +} +func (g mapReprMapReprGenerator) EmitNodeTypeAssertions(w io.Writer) { + doTemplate(` + var _ ipld.Node = &_{{ .Type | TypeSymbol }}__Repr{} + `, w, g.AdjCfg, g) +} + +func (g mapReprMapReprGenerator) EmitNodeMethodLookupString(w io.Writer) { + doTemplate(` + func (nr *_{{ .Type | TypeSymbol }}__Repr) LookupString(k string) (ipld.Node, error) { + v, err := ({{ .Type | TypeSymbol }})(nr).LookupString(k) + if err != nil || v == ipld.Null { + return v, err + } + return v.({{ .Type.ValueType | TypeSymbol}}).Representation(), nil + } + `, w, g.AdjCfg, g) +} +func (g mapReprMapReprGenerator) EmitNodeMethodLookup(w io.Writer) { + // Null is also already a branch in the method we're calling; hopefully the compiler inlines and sees this and DTRT. + // REVIEW: these unchecked casts are definitely safe at compile time, but I'm not sure if the compiler considers that provable, + // so we should investigate if there's any runtime checks injected here that waste time. If so: write this with more gsloc to avoid :( + doTemplate(` + func (nr *_{{ .Type | TypeSymbol }}__Repr) Lookup(k ipld.Node) (ipld.Node, error) { + v, err := ({{ .Type | TypeSymbol }})(nr).Lookup(k) + if err != nil || v == ipld.Null { + return v, err + } + return v.({{ .Type.ValueType | TypeSymbol}}).Representation(), nil + } + `, w, g.AdjCfg, g) +} +func (g mapReprMapReprGenerator) EmitNodeMethodMapIterator(w io.Writer) { + // FUTURE: trying to get this to share the preallocated memory if we get iterators wedged into their node slab will be ... fun. + doTemplate(` + func (nr *_{{ .Type | TypeSymbol }}__Repr) MapIterator() ipld.MapIterator { + return &_{{ .Type | TypeSymbol }}__ReprMapItr{({{ .Type | TypeSymbol }})(nr), 0} + } + + type _{{ .Type | TypeSymbol }}__ReprMapItr _{{ .Type | TypeSymbol }}__MapItr + + func (itr *_{{ .Type | TypeSymbol }}__ReprMapItr) Next() (k ipld.Node, v ipld.Node, err error) { + k, v, err = (*_{{ .Type | TypeSymbol }}__MapItr)(itr).Next() + if err != nil || v == ipld.Null { + return + } + return k, v.({{ .Type.ValueType | TypeSymbol}}).Representation(), nil + } + func (itr *_{{ .Type | TypeSymbol }}__ReprMapItr) Done() bool { + return (*_{{ .Type | TypeSymbol }}__MapItr)(itr).Done() + } + + `, w, g.AdjCfg, g) +} +func (g mapReprMapReprGenerator) EmitNodeMethodLength(w io.Writer) { + doTemplate(` + func (rn *_{{ .Type | TypeSymbol }}__Repr) Length() int { + return len(rn.t) + } + `, w, g.AdjCfg, g) +} +func (g mapReprMapReprGenerator) EmitNodeMethodStyle(w io.Writer) { + emitNodeMethodStyle_typical(w, g.AdjCfg, g) +} +func (g mapReprMapReprGenerator) EmitNodeStyleType(w io.Writer) { + emitNodeStyleType_typical(w, g.AdjCfg, g) +} + +// --- NodeBuilder and NodeAssembler ---> + +func (g mapReprMapReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return mapReprMapReprBuilderGenerator{ + g.AdjCfg, + mixins.MapAssemblerTraits{ + g.PkgName, + g.TypeName, + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr", + }, + g.PkgName, + g.Type, + } +} + +type mapReprMapReprBuilderGenerator struct { + AdjCfg *AdjunctCfg + mixins.MapAssemblerTraits + PkgName string + Type schema.TypeMap +} + +func (mapReprMapReprBuilderGenerator) IsRepr() bool { return true } // hint used in some generalized templates. + +func (g mapReprMapReprBuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g mapReprMapReprBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(w, g.AdjCfg, g) +} +func (g mapReprMapReprBuilderGenerator) 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 map state (the broad transitions between null, start-map, and finish are handled by 'm' for consistency.) + // - there's no equivalent of the 'f' (**f**ocused next) field in struct assemblers -- that's implicitly the last row of the 'w.t'. + // + // - 'cm' is **c**hild **m**aybe and is used for the completion message from children. + // It's used for values if values aren't allowed to be nullable and thus don't have their own per-value maybe slot we can use. + // It's always used for key assembly, since keys are never allowed to be nullable and thus etc. + // - 'ka' and 'va' are the key assembler and value assembler respectively. + // Perhaps surprisingly, we can get away with using the assemblers for each type just straight up, no wrappers necessary; + // All of the required magic is handled through maybe pointers and some tidy methods used during state transitions. + // + // Note that this textually similar to the type-level assembler, but because it embeds the repr assembler for the child types, + // it might be *significantly* different in size and memory layout in that trailing part of the struct. + doTemplate(` + type _{{ .Type | TypeSymbol }}__ReprAssembler struct { + w *_{{ .Type | TypeSymbol }} + m *schema.Maybe + state maState + + cm schema.Maybe + ka _{{ .Type.KeyType | TypeSymbol }}__ReprAssembler + va _{{ .Type.ValueType | TypeSymbol }}__ReprAssembler + } + + func (na *_{{ .Type | TypeSymbol }}__ReprAssembler) reset() { + na.state = maState_initial + na.ka.reset() + na.va.reset() + } + `, w, g.AdjCfg, g) +} +func (g mapReprMapReprBuilderGenerator) EmitNodeAssemblerMethodBeginMap(w io.Writer) { + emitNodeAssemblerMethodBeginMap_mapoid(w, g.AdjCfg, g) +} +func (g mapReprMapReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_recursive(w, g.AdjCfg, g) +} +func (g mapReprMapReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) { + emitNodeAssemblerMethodAssignNode_mapoid(w, g.AdjCfg, g) +} +func (g mapReprMapReprBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + emitNodeAssemblerHelper_mapoid_keyTidyHelper(w, g.AdjCfg, g) + emitNodeAssemblerHelper_mapoid_valueTidyHelper(w, g.AdjCfg, g) + emitNodeAssemblerHelper_mapoid_mapAssemblerMethods(w, g.AdjCfg, g) +} diff --git a/schema/gen/go/genMinima.go b/schema/gen/go/genMinima.go deleted file mode 100644 index 941da3b6..00000000 --- a/schema/gen/go/genMinima.go +++ /dev/null @@ -1,36 +0,0 @@ -package gengo - -import ( - "fmt" - "io" -) - -// EmitMinima emits common code shared by all types -- -// only needs to be output once per module -func EmitMinima(packageName string, f io.Writer) { - // Write header and imports. - fmt.Fprintf(f, "package %s\n\n", packageName) - f.Write([]byte(`import ( - ipld "github.com/ipld/go-ipld-prime" -) -`)) - - // 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. -} diff --git a/schema/gen/go/genString.go b/schema/gen/go/genString.go new file mode 100644 index 00000000..f9cae928 --- /dev/null +++ b/schema/gen/go/genString.go @@ -0,0 +1,135 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +type stringGenerator struct { + AdjCfg *AdjunctCfg + mixins.StringTraits + PkgName string + Type schema.TypeString +} + +func (stringGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +// --- native content and specializations ---> + +func (g stringGenerator) EmitNativeType(w io.Writer) { + emitNativeType_scalar(w, g.AdjCfg, g) +} +func (g stringGenerator) EmitNativeAccessors(w io.Writer) { + emitNativeAccessors_scalar(w, g.AdjCfg, g) +} +func (g stringGenerator) EmitNativeBuilder(w io.Writer) { + // Generate a single-step construction function -- this is easy to do for a scalar, + // and all representations of scalar kind can be expected to have a method like this. + // The function is attached to the nodestyle for convenient namespacing; + // it needs no new memory, so it would be inappropriate to attach to the builder or assembler. + // The function is directly used internally by anything else that might involve recursive destructuring on the same scalar kind + // (for example, structs using stringjoin strategies that have one of this type as a field, etc). + // FUTURE: should engage validation flow. + doTemplate(` + func (_{{ .Type | TypeSymbol }}__Style) fromString(w *_{{ .Type | TypeSymbol }}, v string) error { + *w = _{{ .Type | TypeSymbol }}{v} + return nil + } + `, w, g.AdjCfg, g) + // And generate a publicly exported version of that single-step constructor, too. + // (Just don't expose the details about allocation, because you can't meaningfully use that from outside the package.) + emitNativeBuilder_scalar(w, g.AdjCfg, g) +} + +func (g stringGenerator) EmitNativeMaybe(w io.Writer) { + emitNativeMaybe(w, g.AdjCfg, g) +} + +// --- type info ---> + +func (g stringGenerator) EmitTypeConst(w io.Writer) { + doTemplate(` + // TODO EmitTypeConst + `, w, g.AdjCfg, g) +} + +// --- TypedNode interface satisfaction ---> + +func (g stringGenerator) EmitTypedNodeMethodType(w io.Writer) { + doTemplate(` + func ({{ .Type | TypeSymbol }}) Type() schema.Type { + return nil /*TODO:typelit*/ + } + `, w, g.AdjCfg, g) +} + +func (g stringGenerator) EmitTypedNodeMethodRepresentation(w io.Writer) { + emitTypicalTypedNodeMethodRepresentation(w, g.AdjCfg, g) +} + +// --- Node interface satisfaction ---> + +func (g stringGenerator) EmitNodeType(w io.Writer) { + // No additional types needed. Methods all attach to the native type. +} + +func (g stringGenerator) EmitNodeTypeAssertions(w io.Writer) { + emitNodeTypeAssertions_typical(w, g.AdjCfg, g) +} +func (g stringGenerator) EmitNodeMethodAsString(w io.Writer) { + emitNodeMethodAsKind_scalar(w, g.AdjCfg, g) +} +func (g stringGenerator) EmitNodeMethodStyle(w io.Writer) { + emitNodeMethodStyle_typical(w, g.AdjCfg, g) +} +func (g stringGenerator) EmitNodeStyleType(w io.Writer) { + emitNodeStyleType_typical(w, g.AdjCfg, g) +} + +// --- NodeBuilder and NodeAssembler ---> + +func (g stringGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return stringBuilderGenerator{ + g.AdjCfg, + mixins.StringAssemblerTraits{ + g.PkgName, + g.TypeName, + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__", + }, + g.PkgName, + g.Type, + } +} + +type stringBuilderGenerator struct { + AdjCfg *AdjunctCfg + mixins.StringAssemblerTraits + PkgName string + Type schema.TypeString +} + +func (stringBuilderGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +func (g stringBuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g stringBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(w, g.AdjCfg, g) +} +func (g stringBuilderGenerator) EmitNodeAssemblerType(w io.Writer) { + emitNodeAssemblerType_scalar(w, g.AdjCfg, g) +} +func (g stringBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_scalar(w, g.AdjCfg, g) +} +func (g stringBuilderGenerator) EmitNodeAssemblerMethodAssignString(w io.Writer) { + emitNodeAssemblerMethodAssignKind_scalar(w, g.AdjCfg, g) +} +func (g stringBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) { + emitNodeAssemblerMethodAssignNode_scalar(w, g.AdjCfg, g) +} +func (g stringBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + // Nothing needed here for string kinds. +} diff --git a/schema/gen/go/genStringReprString.go b/schema/gen/go/genStringReprString.go new file mode 100644 index 00000000..f813be6f --- /dev/null +++ b/schema/gen/go/genStringReprString.go @@ -0,0 +1,108 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +var _ TypeGenerator = &stringReprStringGenerator{} + +func NewStringReprStringGenerator(pkgName string, typ schema.TypeString, adjCfg *AdjunctCfg) TypeGenerator { + return stringReprStringGenerator{ + stringGenerator{ + adjCfg, + mixins.StringTraits{ + pkgName, + string(typ.Name()), + adjCfg.TypeSymbol(typ), + }, + pkgName, + typ, + }, + } +} + +type stringReprStringGenerator struct { + stringGenerator +} + +func (g stringReprStringGenerator) GetRepresentationNodeGen() NodeGenerator { + return stringReprStringReprGenerator{ + g.AdjCfg, + g.Type, + } +} + +type stringReprStringReprGenerator struct { + AdjCfg *AdjunctCfg + Type schema.TypeString +} + +func (g stringReprStringReprGenerator) 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 stringReprStringReprGenerator) EmitNodeTypeAssertions(w io.Writer) { + doTemplate(` + var _ ipld.Node = &_{{ .Type | TypeSymbol }}__Repr{} + `, w, g.AdjCfg, g) +} +func (stringReprStringReprGenerator) EmitNodeMethodReprKind(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodLookupString(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodLookup(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodLookupIndex(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodLookupSegment(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodMapIterator(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodListIterator(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodLength(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodIsUndefined(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodIsNull(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodAsBool(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodAsInt(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodAsFloat(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodAsString(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodAsBytes(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodAsLink(io.Writer) {} +func (stringReprStringReprGenerator) EmitNodeMethodStyle(io.Writer) {} +func (g stringReprStringReprGenerator) 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 stringReprStringReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return stringReprStringReprBuilderGenerator{g.AdjCfg, g.Type} +} + +type stringReprStringReprBuilderGenerator struct { + AdjCfg *AdjunctCfg + Type schema.TypeString +} + +func (stringReprStringReprBuilderGenerator) EmitNodeBuilderType(io.Writer) {} +func (g stringReprStringReprBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) {} +func (g stringReprStringReprBuilderGenerator) EmitNodeAssemblerType(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 }}__ReprAssembler = _{{ .Type | TypeSymbol }}__Assembler + `, w, g.AdjCfg, g) +} +func (stringReprStringReprBuilderGenerator) EmitNodeAssemblerMethodBeginMap(io.Writer) {} +func (stringReprStringReprBuilderGenerator) EmitNodeAssemblerMethodBeginList(io.Writer) {} +func (stringReprStringReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(io.Writer) {} +func (stringReprStringReprBuilderGenerator) EmitNodeAssemblerMethodAssignBool(io.Writer) {} +func (stringReprStringReprBuilderGenerator) EmitNodeAssemblerMethodAssignInt(io.Writer) {} +func (stringReprStringReprBuilderGenerator) EmitNodeAssemblerMethodAssignFloat(io.Writer) {} +func (stringReprStringReprBuilderGenerator) EmitNodeAssemblerMethodAssignString(io.Writer) {} +func (stringReprStringReprBuilderGenerator) EmitNodeAssemblerMethodAssignBytes(io.Writer) {} +func (stringReprStringReprBuilderGenerator) EmitNodeAssemblerMethodAssignLink(io.Writer) {} +func (stringReprStringReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(io.Writer) {} +func (stringReprStringReprBuilderGenerator) EmitNodeAssemblerMethodStyle(io.Writer) {} +func (stringReprStringReprBuilderGenerator) EmitNodeAssemblerOtherBits(io.Writer) {} diff --git a/schema/gen/go/genStruct.go b/schema/gen/go/genStruct.go new file mode 100644 index 00000000..eaef9fd9 --- /dev/null +++ b/schema/gen/go/genStruct.go @@ -0,0 +1,592 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +type structGenerator struct { + AdjCfg *AdjunctCfg + mixins.MapTraits + PkgName string + Type schema.TypeStruct +} + +func (structGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +// --- native content and specializations ---> + +func (g structGenerator) EmitNativeType(w io.Writer) { + doTemplate(` + type _{{ .Type | TypeSymbol }} struct { + {{- range $field := .Type.Fields}} + {{ $field | FieldSymbolLower }} _{{ $field.Type | TypeSymbol }}{{if $field.IsMaybe }}__Maybe{{end}} + {{- end}} + } + type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }} + `, w, g.AdjCfg, g) +} + +func (g structGenerator) EmitNativeAccessors(w io.Writer) { + doTemplate(` + {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} + {{- range $field := .Type.Fields }} + func (n _{{ $type | TypeSymbol }}) Field{{ $field | FieldSymbolUpper }}() {{ if $field.IsMaybe }}Maybe{{end}}{{ $field.Type | TypeSymbol }} { + return &n.{{ $field | FieldSymbolLower }} + } + {{- end}} + `, w, g.AdjCfg, g) +} + +func (g structGenerator) EmitNativeBuilder(w io.Writer) { + // Unclear what, if anything, goes here. +} + +func (g structGenerator) EmitNativeMaybe(w io.Writer) { + emitNativeMaybe(w, g.AdjCfg, g) +} + +// --- type info ---> + +func (g structGenerator) EmitTypeConst(w io.Writer) { + doTemplate(` + // TODO EmitTypeConst + `, w, g.AdjCfg, g) +} + +// --- TypedNode interface satisfaction ---> + +func (g structGenerator) EmitTypedNodeMethodType(w io.Writer) { + doTemplate(` + func ({{ .Type | TypeSymbol }}) Type() schema.Type { + return nil /*TODO:typelit*/ + } + `, w, g.AdjCfg, g) +} + +func (g structGenerator) EmitTypedNodeMethodRepresentation(w io.Writer) { + emitTypicalTypedNodeMethodRepresentation(w, g.AdjCfg, g) +} + +// --- Node interface satisfaction ---> + +func (g structGenerator) EmitNodeType(w io.Writer) { + // No additional types needed. Methods all attach to the native type. + // We do, however, want some constants for our fields; + // they'll make iterators able to work faster. So let's emit those. + doTemplate(` + var ( + {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} + {{- range $field := .Type.Fields }} + fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} = _String{"{{ $field.Name }}"} + {{- end }} + ) + `, w, g.AdjCfg, g) +} + +func (g structGenerator) EmitNodeTypeAssertions(w io.Writer) { + emitNodeTypeAssertions_typical(w, g.AdjCfg, g) +} + +func (g structGenerator) EmitNodeMethodLookupString(w io.Writer) { + doTemplate(` + func (n {{ .Type | TypeSymbol }}) LookupString(key string) (ipld.Node, error) { + switch key { + {{- range $field := .Type.Fields }} + case "{{ $field.Name }}": + {{- if $field.IsOptional }} + if n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Absent { + return ipld.Undef, nil + } + {{- end}} + {{- if $field.IsNullable }} + if n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Null { + return ipld.Null, nil + } + {{- end}} + {{- if $field.IsMaybe }} + return {{if not (MaybeUsesPtr $field.Type) }}&{{end}}n.{{ $field | FieldSymbolLower }}.v, nil + {{- else}} + return &n.{{ $field | FieldSymbolLower }}, nil + {{- end}} + {{- end}} + default: + return nil, schema.ErrNoSuchField{Type: nil /*TODO*/, FieldName: key} + } + } + `, w, g.AdjCfg, g) +} + +func (g structGenerator) EmitNodeMethodLookup(w io.Writer) { + doTemplate(` + func (n {{ .Type | TypeSymbol }}) Lookup(key ipld.Node) (ipld.Node, error) { + ks, err := key.AsString() + if err != nil { + return nil, err + } + return n.LookupString(ks) + } + `, w, g.AdjCfg, g) +} + +func (g structGenerator) EmitNodeMethodMapIterator(w io.Writer) { + // Note that the typed iterator will report absent fields. + // The representation iterator (if has one) however will skip those. + doTemplate(` + func (n {{ .Type | TypeSymbol }}) MapIterator() ipld.MapIterator { + return &_{{ .Type | TypeSymbol }}__MapItr{n, 0} + } + + type _{{ .Type | TypeSymbol }}__MapItr struct { + n {{ .Type | TypeSymbol }} + idx int + } + + func (itr *_{{ .Type | TypeSymbol }}__MapItr) Next() (k ipld.Node, v ipld.Node, _ error) { + if itr.idx >= {{ len .Type.Fields }} { + return nil, nil, ipld.ErrIteratorOverread{} + } + switch itr.idx { + {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} + {{- range $i, $field := .Type.Fields }} + case {{ $i }}: + k = &fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} + {{- if $field.IsOptional }} + if itr.n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Absent { + v = ipld.Undef + break + } + {{- end}} + {{- if $field.IsNullable }} + if itr.n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Null { + v = ipld.Null + break + } + {{- end}} + {{- if $field.IsMaybe }} + v = {{if not (MaybeUsesPtr $field.Type) }}&{{end}}itr.n.{{ $field | FieldSymbolLower}}.v + {{- else}} + v = &itr.n.{{ $field | FieldSymbolLower}} + {{- end}} + {{- end}} + default: + panic("unreachable") + } + itr.idx++ + return + } + func (itr *_{{ .Type | TypeSymbol }}__MapItr) Done() bool { + return itr.idx >= {{ len .Type.Fields }} + } + + `, w, g.AdjCfg, g) +} + +func (g structGenerator) EmitNodeMethodLength(w io.Writer) { + doTemplate(` + func ({{ .Type | TypeSymbol }}) Length() int { + return {{ len .Type.Fields }} + } + `, w, g.AdjCfg, g) +} + +func (g structGenerator) EmitNodeMethodStyle(w io.Writer) { + emitNodeMethodStyle_typical(w, g.AdjCfg, g) +} + +func (g structGenerator) EmitNodeStyleType(w io.Writer) { + emitNodeStyleType_typical(w, g.AdjCfg, g) +} + +// --- NodeBuilder and NodeAssembler ---> + +func (g structGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return structBuilderGenerator{ + g.AdjCfg, + mixins.MapAssemblerTraits{ + g.PkgName, + g.TypeName, + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__", + }, + g.PkgName, + g.Type, + } +} + +type structBuilderGenerator struct { + AdjCfg *AdjunctCfg + mixins.MapAssemblerTraits + PkgName string + Type schema.TypeStruct +} + +func (structBuilderGenerator) IsRepr() bool { return false } // hint used in some generalized templates. + +func (g structBuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g structBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(w, g.AdjCfg, g) +} +func (g structBuilderGenerator) 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 map state (the broad transitions between null, start-map, and finish are handled by 'm' for consistency.) + // - 's' is a bitfield for what's been **s**et. + // - 'f' is the **f**ocused field that will be assembled next. + // + // - 'cm' is **c**hild **m**aybe and is used for the completion message from children that aren't allowed to be nullable (for those that are, their own maybe.m is used). + // ('cm' could be elided for structs where all fields are maybes. trivial but not yet implemented.) + // - the 'ca_*' fields embed **c**hild **a**ssemblers -- these are embedded so we can yield pointers to them without causing new allocations. + doTemplate(` + type _{{ .Type | TypeSymbol }}__Assembler struct { + w *_{{ .Type | TypeSymbol }} + m *schema.Maybe + state maState + s int + f int + + cm schema.Maybe + {{range $field := .Type.Fields -}} + ca_{{ $field | FieldSymbolLower }} _{{ $field.Type | TypeSymbol }}__Assembler + {{end -}} + } + + func (na *_{{ .Type | TypeSymbol }}__Assembler) reset() { + na.state = maState_initial + na.s = 0 + {{- range $field := .Type.Fields }} + na.ca_{{ $field | FieldSymbolLower }}.reset() + {{- end}} + } + + var ( + {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} + {{- range $i, $field := .Type.Fields }} + fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} = 1 << {{ $i }} + {{- end}} + fieldBits__{{ $type | TypeSymbol }}_sufficient = 0 {{- range $i, $field := .Type.Fields }}{{if not $field.IsOptional }} + 1 << {{ $i }}{{end}}{{end}} + ) + `, w, g.AdjCfg, g) +} +func (g structBuilderGenerator) EmitNodeAssemblerMethodBeginMap(w io.Writer) { + // We currently disregard sizeHint. It's not relevant to us. + // We could check it strictly and emit errors; presently, we don't. + // 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) BeginMap(int) (ipld.MapAssembler, 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 .Type | MaybeUsesPtr }} + if na.w == nil { + na.w = &_{{ .Type | TypeSymbol }}{} + } + {{- end}} + return na, nil + } + `, w, g.AdjCfg, g) +} +func (g structBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_recursive(w, g.AdjCfg, g) +} +func (g structBuilderGenerator) 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_Map { + return ipld.ErrWrongKind{TypeName: "{{ .PkgName }}.{{ .Type.Name }}", MethodName: "AssignNode", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: v.ReprKind()} + } + itr := v.MapIterator() + for !itr.Done() { + k, v, err := itr.Next() + if err != nil { + return err + } + if err := na.AssembleKey().AssignNode(k); err != nil { + return err + } + if err := na.AssembleValue().AssignNode(v); err != nil { + return err + } + } + return na.Finish() + } + `, w, g.AdjCfg, g) +} +func (g structBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + g.emitMapAssemblerChildTidyHelper(w) + g.emitMapAssemblerMethods(w) + g.emitKeyAssembler(w) +} +func (g structBuilderGenerator) emitMapAssemblerChildTidyHelper(w io.Writer) { + // This function attempts to clean up the state machine to acknolwedge child assembly finish. + // If the child was finished and we just collected it, return true and update state to maState_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 maState_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.) + // Most of the logic here is about nullables and not optionals, + // because if you're an optional that's absent, you never got to value assembly. + // There's still one branch for optionals, though, because they have a different residence for 'm' just as nullables do. + // Child assemblers are expected to control their own state machines; + // for values that have maybes, we never change their maybe state again, so the usual logic should hold; + // for values that don't have maybes (and thus share 'cm')... + // We don't bother to nil their 'm' pointer; the worst that can happen is an over-held assembler for that field + // can make a bizarre and broken transition for a subsequent field, which will result in very ugly errors, but isn't unsafe per se. + // We do nil their 'w' pointer, though: we don't want a set to that able to leak in later if we're on the way to Finish! + doTemplate(` + func (ma *_{{ .Type | TypeSymbol }}__Assembler) valueFinishTidy() bool { + switch ma.f { + {{- range $i, $field := .Type.Fields }} + case {{ $i }}: + {{- if $field.IsNullable }} + switch ma.w.{{ $field | FieldSymbolLower }}.m { + case schema.Maybe_Null: + ma.state = maState_initial + return true + case schema.Maybe_Value: + {{- if (MaybeUsesPtr $field.Type) }} + ma.w.{{ $field | FieldSymbolLower }}.v = ma.ca_{{ $field | FieldSymbolLower }}.w + {{- end}} + ma.state = maState_initial + return true + default: + return false + } + {{- else if $field.IsOptional }} + switch ma.w.{{ $field | FieldSymbolLower }}.m { + case schema.Maybe_Value: + {{- if (MaybeUsesPtr $field.Type) }} + ma.w.{{ $field | FieldSymbolLower }}.v = ma.ca_{{ $field | FieldSymbolLower }}.w + {{- end}} + ma.state = maState_initial + return true + default: + return false + } + {{- else}} + switch ma.cm { + case schema.Maybe_Value: + ma.ca_{{ $field | FieldSymbolLower }}.w = nil + ma.cm = schema.Maybe_Absent + ma.state = maState_initial + return true + default: + return false + } + {{- end}} + {{- end}} + default: + panic("unreachable") + } + } + `, w, g.AdjCfg, g) +} +func (g structBuilderGenerator) emitMapAssemblerMethods(w io.Writer) { + // FUTURE: some of the setup of the child assemblers could probably be DRY'd up. + doTemplate(` + func (ma *_{{ .Type | TypeSymbol }}__Assembler) AssembleEntry(k string) (ipld.NodeAssembler, error) { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleEntry cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleEntry cannot be called on an assembler that's already finished") + } + switch k { + {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} + {{- range $i, $field := .Type.Fields }} + case "{{ $field.Name }}": + if ma.s & fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} != 0 { + return nil, ipld.ErrRepeatedMapKey{&fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}} + } + ma.s += fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} + ma.state = maState_midValue + {{- if $field.IsMaybe }} + ma.ca_{{ $field | FieldSymbolLower }}.w = {{if not (MaybeUsesPtr $field.Type) }}&{{end}}ma.w.{{ $field | FieldSymbolLower }}.v + ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.w.{{ $field | FieldSymbolLower }}.m + {{if $field.IsNullable }}ma.w.{{ $field | FieldSymbolLower }}.m = allowNull{{end}} + {{- else}} + ma.ca_{{ $field | FieldSymbolLower }}.w = &ma.w.{{ $field | FieldSymbolLower }} + ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.cm + {{- end}} + return &ma.ca_{{ $field | FieldSymbolLower }}, nil + {{- end}} + default: + return nil, ipld.ErrInvalidKey{TypeName:"{{ .PkgName }}.{{ .Type.Name }}", Key:&_String{k}} + } + } + func (ma *_{{ .Type | TypeSymbol }}__Assembler) AssembleKey() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleKey cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleKey cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleKey cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleKey cannot be called on an assembler that's already finished") + } + ma.state = maState_midKey + return (*_{{ .Type | TypeSymbol }}__KeyAssembler)(ma) + } + func (ma *_{{ .Type | TypeSymbol }}__Assembler) AssembleValue() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + panic("invalid state: AssembleValue cannot be called when no key is primed") + case maState_midKey: + panic("invalid state: AssembleValue cannot be called when in the middle of assembling a key") + case maState_expectValue: + // carry on + case maState_midValue: + panic("invalid state: AssembleValue cannot be called when in the middle of assembling another value") + case maState_finished: + panic("invalid state: AssembleValue cannot be called on an assembler that's already finished") + } + ma.state = maState_midValue + switch ma.f { + {{- range $i, $field := .Type.Fields }} + case {{ $i }}: + {{- if $field.IsMaybe }} + ma.ca_{{ $field | FieldSymbolLower }}.w = {{if not (MaybeUsesPtr $field.Type) }}&{{end}}ma.w.{{ $field | FieldSymbolLower }}.v + ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.w.{{ $field | FieldSymbolLower }}.m + {{if $field.IsNullable }}ma.w.{{ $field | FieldSymbolLower }}.m = allowNull{{end}} + {{- else}} + ma.ca_{{ $field | FieldSymbolLower }}.w = &ma.w.{{ $field | FieldSymbolLower }} + ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.cm + {{- end}} + return &ma.ca_{{ $field | FieldSymbolLower }} + {{- end}} + default: + panic("unreachable") + } + } + func (ma *_{{ .Type | TypeSymbol }}__Assembler) Finish() error { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: Finish cannot be called when in the middle of assembling a key") + case maState_expectValue: + panic("invalid state: Finish cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: Finish cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: Finish cannot be called on an assembler that's already finished") + } + //FIXME check if all required fields are set + ma.state = maState_finished + *ma.m = schema.Maybe_Value + return nil + } + func (ma *_{{ .Type | TypeSymbol }}__Assembler) KeyStyle() ipld.NodeStyle { + return _String__Style{} + } + func (ma *_{{ .Type | TypeSymbol }}__Assembler) ValueStyle(k string) ipld.NodeStyle { + panic("todo structbuilder mapassembler valuestyle") + } + `, w, g.AdjCfg, g) +} +func (g structBuilderGenerator) emitKeyAssembler(w io.Writer) { + doTemplate(` + type _{{ .Type | TypeSymbol }}__KeyAssembler _{{ .Type | TypeSymbol }}__Assembler + `, w, g.AdjCfg, g) + stubs := mixins.StringAssemblerTraits{ + g.PkgName, + g.TypeName + ".KeyAssembler", + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Key", + } + // This key assembler can disregard any idea of complex keys because it's a struct! + // Struct field names must be strings (and quite simple ones at that). + stubs.EmitNodeAssemblerMethodBeginMap(w) + stubs.EmitNodeAssemblerMethodBeginList(w) + stubs.EmitNodeAssemblerMethodAssignNull(w) + stubs.EmitNodeAssemblerMethodAssignBool(w) + stubs.EmitNodeAssemblerMethodAssignInt(w) + stubs.EmitNodeAssemblerMethodAssignFloat(w) + doTemplate(` + func (ka *_{{ .Type | TypeSymbol }}__KeyAssembler) AssignString(k string) error { + if ka.state != maState_midKey { + panic("misuse: KeyAssembler held beyond its valid lifetime") + } + switch k { + {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} + {{- range $i, $field := .Type.Fields }} + case "{{ $field.Name }}": + if ka.s & fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} != 0 { + return ipld.ErrRepeatedMapKey{&fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}} + } + ka.s += fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} + ka.state = maState_expectValue + ka.f = {{ $i }} + {{- end}} + default: + return ipld.ErrInvalidKey{TypeName:"{{ .PkgName }}.{{ .Type.Name }}", Key:&_String{k}} + } + return nil + } + `, w, g.AdjCfg, g) + stubs.EmitNodeAssemblerMethodAssignBytes(w) + stubs.EmitNodeAssemblerMethodAssignLink(w) + doTemplate(` + func (ka *_{{ .Type | TypeSymbol }}__KeyAssembler) AssignNode(v ipld.Node) error { + if v2, err := v.AsString(); err != nil { + return err + } else { + return ka.AssignString(v2) + } + } + func (_{{ .Type | TypeSymbol }}__KeyAssembler) Style() ipld.NodeStyle { + return _String__Style{} + } + `, w, g.AdjCfg, g) +} diff --git a/schema/gen/go/genStructReprMap.go b/schema/gen/go/genStructReprMap.go new file mode 100644 index 00000000..1033bb00 --- /dev/null +++ b/schema/gen/go/genStructReprMap.go @@ -0,0 +1,632 @@ +package gengo + +import ( + "io" + "strconv" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +var _ TypeGenerator = &structReprMapGenerator{} + +func NewStructReprMapGenerator(pkgName string, typ schema.TypeStruct, adjCfg *AdjunctCfg) TypeGenerator { + return structReprMapGenerator{ + structGenerator{ + adjCfg, + mixins.MapTraits{ + pkgName, + string(typ.Name()), + adjCfg.TypeSymbol(typ), + }, + pkgName, + typ, + }, + } +} + +type structReprMapGenerator struct { + structGenerator +} + +func (g structReprMapGenerator) GetRepresentationNodeGen() NodeGenerator { + return structReprMapReprGenerator{ + g.AdjCfg, + mixins.MapTraits{ + g.PkgName, + string(g.Type.Name()) + ".Repr", + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr", + }, + g.PkgName, + g.Type, + } +} + +type structReprMapReprGenerator struct { + AdjCfg *AdjunctCfg + mixins.MapTraits + PkgName string + Type schema.TypeStruct +} + +func (structReprMapReprGenerator) IsRepr() bool { return true } // hint used in some generalized templates. + +func (g structReprMapReprGenerator) EmitNodeType(w io.Writer) { + // The type is structurally the same, but will have a different set of methods. + doTemplate(` + type _{{ .Type | TypeSymbol }}__Repr _{{ .Type | TypeSymbol }} + `, w, g.AdjCfg, g) + + // We do also want some constants for our fields; + // they'll make iterators able to work faster. + // These might be the same strings as the type-level field names + // (in fact, they are, unless renames are used)... but that's fine. + // We get simpler code by just doing this unconditionally. + doTemplate(` + var ( + {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} + {{- range $field := .Type.Fields }} + fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}_serial = _String{"{{ $field | $type.RepresentationStrategy.GetFieldKey }}"} + {{- end }} + ) + `, w, g.AdjCfg, g) +} + +func (g structReprMapReprGenerator) EmitNodeTypeAssertions(w io.Writer) { + doTemplate(` + var _ ipld.Node = &_{{ .Type | TypeSymbol }}__Repr{} + `, w, g.AdjCfg, g) +} + +func (g structReprMapReprGenerator) EmitNodeMethodLookupString(w io.Writer) { + // Similar to the type-level method, except any undef fields also return ErrNotExists. + doTemplate(` + func (n *_{{ .Type | TypeSymbol }}__Repr) LookupString(key string) (ipld.Node, error) { + switch key { + {{- range $field := .Type.Fields }} + case "{{ $field | $field.Parent.RepresentationStrategy.GetFieldKey }}": + {{- if $field.IsOptional }} + if n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Absent { + return ipld.Undef, ipld.ErrNotExists{ipld.PathSegmentOfString(key)} + } + {{- end}} + {{- if $field.IsNullable }} + if n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Null { + return ipld.Null, nil + } + {{- end}} + {{- if $field.IsMaybe }} + return n.{{ $field | FieldSymbolLower }}.v.Representation(), nil + {{- else}} + return n.{{ $field | FieldSymbolLower }}.Representation(), nil + {{- end}} + {{- end}} + default: + return nil, schema.ErrNoSuchField{Type: nil /*TODO*/, FieldName: key} + } + } + `, w, g.AdjCfg, g) +} + +func (g structReprMapReprGenerator) EmitNodeMethodLookup(w io.Writer) { + doTemplate(` + func (n *_{{ .Type | TypeSymbol }}__Repr) Lookup(key ipld.Node) (ipld.Node, error) { + ks, err := key.AsString() + if err != nil { + return nil, err + } + return n.LookupString(ks) + } + `, w, g.AdjCfg, g) +} + +func (g structReprMapReprGenerator) EmitNodeMethodMapIterator(w io.Writer) { + // The 'idx' int is what field we'll yield next. + // Note that this iterator doesn't mention fields that are absent. + // This makes things a bit trickier -- especially the 'Done' predicate, + // since it may have to do lookahead if there's any optionals at the end of the structure! + // It also means 'idx' can jump ahead by more than one per Next call in order to skip over absent fields. + // TODO : support for implicits is still future work. + + // First: Determine if there are any optionals at all. + // If there are none, some control flow symbols need to not be emitted. + fields := g.Type.Fields() + haveOptionals := false + for _, field := range fields { + if field.IsOptional() { + haveOptionals = true + break + } + } + + // Second: Count how many trailing fields are optional. + // The 'Done' predicate gets more complex when in the trailing optionals. + fieldCount := len(fields) + beginTrailingOptionalField := fieldCount + for i := fieldCount - 1; i >= 0; i-- { + if !fields[i].IsOptional() { + break + } + beginTrailingOptionalField = i + } + haveTrailingOptionals := beginTrailingOptionalField < fieldCount + + // Now: finally we can get on with the actual templating. + doTemplate(` + func (n *_{{ .Type | TypeSymbol }}__Repr) MapIterator() ipld.MapIterator { + {{- if .HaveTrailingOptionals }} + end := {{ len .Type.Fields }}`+ + func() string { // this next part was too silly in templates due to lack of reverse ranging. + v := "\n" + for i := fieldCount - 1; i >= beginTrailingOptionalField; i-- { + v += "\t\t\tif n." + g.AdjCfg.FieldSymbolLower(fields[i]) + ".m == schema.Maybe_Absent {\n" + v += "\t\t\t\tend = " + strconv.Itoa(i) + "\n" + v += "\t\t\t} else {\n" + v += "\t\t\t\tgoto done\n" + v += "\t\t\t}\n" + } + return v + }()+`done: + return &_{{ .Type | TypeSymbol }}__ReprMapItr{n, 0, end} + {{- else}} + return &_{{ .Type | TypeSymbol }}__ReprMapItr{n, 0} + {{- end}} + } + + type _{{ .Type | TypeSymbol }}__ReprMapItr struct { + n *_{{ .Type | TypeSymbol }}__Repr + idx int + {{if .HaveTrailingOptionals }}end int{{end}} + } + + func (itr *_{{ .Type | TypeSymbol }}__ReprMapItr) Next() (k ipld.Node, v ipld.Node, _ error) { + {{ if .HaveOptionals }}advance:{{end -}} + if itr.idx >= {{ len .Type.Fields }} { + return nil, nil, ipld.ErrIteratorOverread{} + } + switch itr.idx { + {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} + {{- range $i, $field := .Type.Fields }} + case {{ $i }}: + k = &fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}_serial + {{- if $field.IsOptional }} + if itr.n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Absent { + itr.idx++ + goto advance + } + {{- end}} + {{- if $field.IsNullable }} + if itr.n.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Null { + v = ipld.Null + break + } + {{- end}} + {{- if $field.IsMaybe }} + v = itr.n.{{ $field | FieldSymbolLower}}.v.Representation() + {{- else}} + v = itr.n.{{ $field | FieldSymbolLower}}.Representation() + {{- end}} + {{- end}} + default: + panic("unreachable") + } + itr.idx++ + return + } + {{- if .HaveTrailingOptionals }} + func (itr *_{{ .Type | TypeSymbol }}__ReprMapItr) Done() bool { + return itr.idx >= itr.end + } + {{- else}} + func (itr *_{{ .Type | TypeSymbol }}__ReprMapItr) Done() bool { + return itr.idx >= {{ len .Type.Fields }} + } + {{- end}} + `, w, g.AdjCfg, struct { + Type schema.TypeStruct + HaveOptionals bool + HaveTrailingOptionals bool + BeginTrailingOptionalField int + }{ + g.Type, + haveOptionals, + haveTrailingOptionals, + beginTrailingOptionalField, + }) +} + +func (g structReprMapReprGenerator) EmitNodeMethodLength(w io.Writer) { + // This is fun: it has to count down for any unset optional fields. + // TODO : support for implicits is still future work. + doTemplate(` + func (rn *_{{ .Type | TypeSymbol }}__Repr) Length() int { + l := {{ len .Type.Fields }} + {{- range $field := .Type.Fields }} + {{- if $field.IsOptional }} + if rn.{{ $field | FieldSymbolLower }}.m == schema.Maybe_Absent { + l-- + } + {{- end}} + {{- end}} + return l + } + `, w, g.AdjCfg, g) +} + +func (g structReprMapReprGenerator) EmitNodeMethodStyle(w io.Writer) { + emitNodeMethodStyle_typical(w, g.AdjCfg, g) +} + +func (g structReprMapReprGenerator) EmitNodeStyleType(w io.Writer) { + emitNodeStyleType_typical(w, g.AdjCfg, g) +} + +// --- NodeBuilder and NodeAssembler ---> + +func (g structReprMapReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return structReprMapReprBuilderGenerator{ + g.AdjCfg, + mixins.MapAssemblerTraits{ + g.PkgName, + g.TypeName, + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr", + }, + g.PkgName, + g.Type, + } +} + +type structReprMapReprBuilderGenerator struct { + AdjCfg *AdjunctCfg + mixins.MapAssemblerTraits + PkgName string + Type schema.TypeStruct +} + +func (structReprMapReprBuilderGenerator) IsRepr() bool { return true } // hint used in some generalized templates. + +func (g structReprMapReprBuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g structReprMapReprBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(w, g.AdjCfg, g) +} +func (g structReprMapReprBuilderGenerator) 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 map state (the broad transitions between null, start-map, and finish are handled by 'm' for consistency.) + // - 's' is a bitfield for what's been **s**et. + // - 'f' is the **f**ocused field that will be assembled next. + // + // - 'cm' is **c**hild **m**aybe and is used for the completion message from children that aren't allowed to be nullable (for those that are, their own maybe.m is used). + // - the 'ca_*' fields embed **c**hild **a**ssemblers -- these are embedded so we can yield pointers to them without causing new allocations. + doTemplate(` + type _{{ .Type | TypeSymbol }}__ReprAssembler struct { + w *_{{ .Type | TypeSymbol }} + m *schema.Maybe + state maState + s int + f int + + cm schema.Maybe + {{range $field := .Type.Fields -}} + ca_{{ $field | FieldSymbolLower }} _{{ $field.Type | TypeSymbol }}__ReprAssembler + {{end -}} + } + + func (na *_{{ .Type | TypeSymbol }}__ReprAssembler) reset() { + na.state = maState_initial + na.s = 0 + {{- range $field := .Type.Fields }} + na.ca_{{ $field | FieldSymbolLower }}.reset() + {{- end}} + } + `, w, g.AdjCfg, g) +} +func (g structReprMapReprBuilderGenerator) EmitNodeAssemblerMethodBeginMap(w io.Writer) { + // We currently disregard sizeHint. It's not relevant to us. + // We could check it strictly and emit errors; presently, we don't. + // 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 }}__ReprAssembler) BeginMap(int) (ipld.MapAssembler, 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 .Type | MaybeUsesPtr }} + if na.w == nil { + na.w = &_{{ .Type | TypeSymbol }}{} + } + {{- end}} + return na, nil + } + `, w, g.AdjCfg, g) +} +func (g structReprMapReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_recursive(w, g.AdjCfg, g) +} +func (g structReprMapReprBuilderGenerator) 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 }}__ReprAssembler) 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_Map { + return ipld.ErrWrongKind{TypeName: "{{ .PkgName }}.{{ .Type.Name }}.Repr", MethodName: "AssignNode", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: v.ReprKind()} + } + itr := v.MapIterator() + for !itr.Done() { + k, v, err := itr.Next() + if err != nil { + return err + } + if err := na.AssembleKey().AssignNode(k); err != nil { + return err + } + if err := na.AssembleValue().AssignNode(v); err != nil { + return err + } + } + return na.Finish() + } + `, w, g.AdjCfg, g) +} +func (g structReprMapReprBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + g.emitMapAssemblerChildTidyHelper(w) + g.emitMapAssemblerMethods(w) + g.emitKeyAssembler(w) +} +func (g structReprMapReprBuilderGenerator) emitMapAssemblerChildTidyHelper(w io.Writer) { + // This is exactly the same as the matching method on the type-level assembler; + // everything that differs happens to be hidden behind the 'f' indirection, which is numeric. + doTemplate(` + func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) valueFinishTidy() bool { + switch ma.f { + {{- range $i, $field := .Type.Fields }} + case {{ $i }}: + {{- if $field.IsNullable }} + switch ma.w.{{ $field | FieldSymbolLower }}.m { + case schema.Maybe_Null: + ma.state = maState_initial + return true + case schema.Maybe_Value: + {{- if (MaybeUsesPtr $field.Type) }} + ma.w.{{ $field | FieldSymbolLower }}.v = ma.ca_{{ $field | FieldSymbolLower }}.w + {{- end}} + ma.state = maState_initial + return true + default: + return false + } + {{- else if $field.IsOptional }} + switch ma.w.{{ $field | FieldSymbolLower }}.m { + case schema.Maybe_Value: + {{- if (MaybeUsesPtr $field.Type) }} + ma.w.{{ $field | FieldSymbolLower }}.v = ma.ca_{{ $field | FieldSymbolLower }}.w + {{- end}} + ma.state = maState_initial + return true + default: + return false + } + {{- else}} + switch ma.cm { + case schema.Maybe_Value: + {{- /* while defense in depth here might avoid some 'wat' outcomes, it's not strictly necessary for safety */ -}} + {{- /* ma.ca_{{ $field | FieldSymbolLower }}.w = nil */ -}} + {{- /* ma.ca_{{ $field | FieldSymbolLower }}.m = nil */ -}} + ma.cm = schema.Maybe_Absent + ma.state = maState_initial + return true + default: + return false + } + {{- end}} + {{- end}} + default: + panic("unreachable") + } + } + `, w, g.AdjCfg, g) +} +func (g structReprMapReprBuilderGenerator) emitMapAssemblerMethods(w io.Writer) { + // FUTURE: some of the setup of the child assemblers could probably be DRY'd up. + doTemplate(` + func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) AssembleEntry(k string) (ipld.NodeAssembler, error) { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleEntry cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleEntry cannot be called on an assembler that's already finished") + } + switch k { + {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} + {{- range $i, $field := .Type.Fields }} + case "{{ $field | $type.RepresentationStrategy.GetFieldKey }}": + if ma.s & fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} != 0 { + return nil, ipld.ErrRepeatedMapKey{&fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}_serial} + } + ma.s += fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} + ma.state = maState_midValue + {{- if $field.IsMaybe }} + ma.ca_{{ $field | FieldSymbolLower }}.w = {{if not (MaybeUsesPtr $field.Type) }}&{{end}}ma.w.{{ $field | FieldSymbolLower }}.v + ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.w.{{ $field | FieldSymbolLower }}.m + {{if $field.IsNullable }}ma.w.{{ $field | FieldSymbolLower }}.m = allowNull{{end}} + {{- else}} + ma.ca_{{ $field | FieldSymbolLower }}.w = &ma.w.{{ $field | FieldSymbolLower }} + ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.cm + {{- end}} + return &ma.ca_{{ $field | FieldSymbolLower }}, nil + {{- end}} + default: + return nil, ipld.ErrInvalidKey{TypeName:"{{ .PkgName }}.{{ .Type.Name }}.Repr", Key:&_String{k}} + } + } + func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) AssembleKey() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleKey cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleKey cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleKey cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleKey cannot be called on an assembler that's already finished") + } + ma.state = maState_midKey + return (*_{{ .Type | TypeSymbol }}__ReprKeyAssembler)(ma) + } + func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) AssembleValue() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + panic("invalid state: AssembleValue cannot be called when no key is primed") + case maState_midKey: + panic("invalid state: AssembleValue cannot be called when in the middle of assembling a key") + case maState_expectValue: + // carry on + case maState_midValue: + panic("invalid state: AssembleValue cannot be called when in the middle of assembling another value") + case maState_finished: + panic("invalid state: AssembleValue cannot be called on an assembler that's already finished") + } + ma.state = maState_midValue + switch ma.f { + {{- range $i, $field := .Type.Fields }} + case {{ $i }}: + {{- if $field.IsMaybe }} + ma.ca_{{ $field | FieldSymbolLower }}.w = {{if not (MaybeUsesPtr $field.Type) }}&{{end}}ma.w.{{ $field | FieldSymbolLower }}.v + ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.w.{{ $field | FieldSymbolLower }}.m + {{if $field.IsNullable }}ma.w.{{ $field | FieldSymbolLower }}.m = allowNull{{end}} + {{- else}} + ma.ca_{{ $field | FieldSymbolLower }}.w = &ma.w.{{ $field | FieldSymbolLower }} + ma.ca_{{ $field | FieldSymbolLower }}.m = &ma.cm + {{- end}} + return &ma.ca_{{ $field | FieldSymbolLower }} + {{- end}} + default: + panic("unreachable") + } + } + func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) Finish() error { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: Finish cannot be called when in the middle of assembling a key") + case maState_expectValue: + panic("invalid state: Finish cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: Finish cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: Finish cannot be called on an assembler that's already finished") + } + //FIXME check if all required fields are set + ma.state = maState_finished + *ma.m = schema.Maybe_Value + return nil + } + func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) KeyStyle() ipld.NodeStyle { + return _String__Style{} + } + func (ma *_{{ .Type | TypeSymbol }}__ReprAssembler) ValueStyle(k string) ipld.NodeStyle { + panic("todo structbuilder mapassembler repr valuestyle") + } + `, w, g.AdjCfg, g) +} +func (g structReprMapReprBuilderGenerator) emitKeyAssembler(w io.Writer) { + doTemplate(` + type _{{ .Type | TypeSymbol }}__ReprKeyAssembler _{{ .Type | TypeSymbol }}__ReprAssembler + `, w, g.AdjCfg, g) + stubs := mixins.StringAssemblerTraits{ + g.PkgName, + g.TypeName + ".KeyAssembler", // ".Repr" is already in `g.TypeName`, so don't stutter the "Repr" part. + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__ReprKey", + } + // This key assembler can disregard any idea of complex keys because it's at the representation level! + // Map keys must always be plain strings at the representation level. + stubs.EmitNodeAssemblerMethodBeginMap(w) + stubs.EmitNodeAssemblerMethodBeginList(w) + stubs.EmitNodeAssemblerMethodAssignNull(w) + stubs.EmitNodeAssemblerMethodAssignBool(w) + stubs.EmitNodeAssemblerMethodAssignInt(w) + stubs.EmitNodeAssemblerMethodAssignFloat(w) + doTemplate(` + func (ka *_{{ .Type | TypeSymbol }}__ReprKeyAssembler) AssignString(k string) error { + if ka.state != maState_midKey { + panic("misuse: KeyAssembler held beyond its valid lifetime") + } + switch k { + {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} + {{- range $i, $field := .Type.Fields }} + case "{{ $field | $type.RepresentationStrategy.GetFieldKey }}": + if ka.s & fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} != 0 { + return ipld.ErrRepeatedMapKey{&fieldName__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }}_serial} + } + ka.s += fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} + ka.state = maState_expectValue + ka.f = {{ $i }} + {{- end}} + default: + return ipld.ErrInvalidKey{TypeName:"{{ .PkgName }}.{{ .Type.Name }}.Repr", Key:&_String{k}} + } + return nil + } + `, w, g.AdjCfg, g) + stubs.EmitNodeAssemblerMethodAssignBytes(w) + stubs.EmitNodeAssemblerMethodAssignLink(w) + doTemplate(` + func (ka *_{{ .Type | TypeSymbol }}__ReprKeyAssembler) AssignNode(v ipld.Node) error { + if v2, err := v.AsString(); err != nil { + return err + } else { + return ka.AssignString(v2) + } + } + func (_{{ .Type | TypeSymbol }}__ReprKeyAssembler) Style() ipld.NodeStyle { + return _String__Style{} + } + `, w, g.AdjCfg, g) +} diff --git a/schema/gen/go/genStructReprStringjoin.go b/schema/gen/go/genStructReprStringjoin.go new file mode 100644 index 00000000..5e45e84f --- /dev/null +++ b/schema/gen/go/genStructReprStringjoin.go @@ -0,0 +1,253 @@ +package gengo + +import ( + "io" + + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" +) + +var _ TypeGenerator = &structReprStringjoinGenerator{} + +func NewStructReprStringjoinGenerator(pkgName string, typ schema.TypeStruct, adjCfg *AdjunctCfg) TypeGenerator { + return structReprStringjoinGenerator{ + structGenerator{ + adjCfg, + mixins.MapTraits{ + pkgName, + string(typ.Name()), + adjCfg.TypeSymbol(typ), + }, + pkgName, + typ, + }, + } +} + +type structReprStringjoinGenerator struct { + structGenerator +} + +func (g structReprStringjoinGenerator) GetRepresentationNodeGen() NodeGenerator { + return structReprStringjoinReprGenerator{ + g.AdjCfg, + mixins.StringTraits{ + g.PkgName, + string(g.Type.Name()) + ".Repr", + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr", + }, + g.PkgName, + g.Type, + } +} + +type structReprStringjoinReprGenerator struct { + AdjCfg *AdjunctCfg + mixins.StringTraits + PkgName string + Type schema.TypeStruct +} + +func (g structReprStringjoinReprGenerator) EmitNodeType(w io.Writer) { + // The type is structurally the same, but will have a different set of methods. + doTemplate(` + type _{{ .Type | TypeSymbol }}__Repr _{{ .Type | TypeSymbol }} + `, w, g.AdjCfg, g) +} + +func (g structReprStringjoinReprGenerator) EmitNodeTypeAssertions(w io.Writer) { + doTemplate(` + var _ ipld.Node = &_{{ .Type | TypeSymbol }}__Repr{} + `, w, g.AdjCfg, g) +} + +func (g structReprStringjoinReprGenerator) EmitNodeMethodAsString(w io.Writer) { + // Prerequisites: + // - every field must be a string, or have string representation. + // - this should've been checked when compiling the type system info. + // - we're willing to imply a base-10 atoi/itoa for ints (but it's not currently supported). + // - there are NO sanity checks that your value doesn't contain the delimiter + // - you need to do this in validation hooks or some other way + // - optional or nullable fields are not supported with this representation strategy. + // - this should've been checked when compiling the type system info. + // - if support for this is added in the future, you can bet all optionals + // will be required to be *either* in a row at the start, or in a row at the end. + // (a 'direction' property might also be needed, so behavior is defined if every field is optional.) + // + // A speciated String method is also generated here. + // (Organization questionable: if this was at type level, it'd be in the 'EmitNativeAccessors' block, + // but we don't have that in the NodeGenerator interface so we don't have it here. Maybe that's a mistake.) + // + // A String method is *also* generated on the type-level node. + // This might be worth consistency review... + // It's a practical necessity in areas like stringifying for key error messages if used in map keys, for example. + doTemplate(` + func (n *_{{ .Type | TypeSymbol }}__Repr) AsString() (string, error) { + return n.String(), nil + } + func (n *_{{ .Type | TypeSymbol }}__Repr) String() string { + return {{ "" }} + {{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}} + {{- range $i, $field := .Type.Fields }} + {{- if $i }} + "{{ $type.RepresentationStrategy.GetDelim }}" + {{end -}} + (*_{{ $field.Type | TypeSymbol }}__Repr)(&n.{{ $field | FieldSymbolLower }}).String() + {{- end}} + } + func (n {{ .Type | TypeSymbol }}) String() string { + return (*_{{ .Type | TypeSymbol }}__Repr)(n).String() + } + `, w, g.AdjCfg, g) +} + +func (g structReprStringjoinReprGenerator) EmitNodeMethodStyle(w io.Writer) { + // REVIEW: this appears to be standard even across kinds; can we extract it? + doTemplate(` + func (_{{ .Type | TypeSymbol }}__Repr) Style() ipld.NodeStyle { + return _{{ .Type | TypeSymbol }}__ReprStyle{} + } + `, w, g.AdjCfg, g) +} + +func (g structReprStringjoinReprGenerator) EmitNodeStyleType(w io.Writer) { + // REVIEW: this appears to be standard even across kinds; can we extract it? + doTemplate(` + type _{{ .Type | TypeSymbol }}__ReprStyle struct{} + + func (_{{ .Type | TypeSymbol }}__ReprStyle) NewBuilder() ipld.NodeBuilder { + var nb _{{ .Type | TypeSymbol }}__ReprBuilder + nb.Reset() + return &nb + } + `, w, g.AdjCfg, g) +} + +// --- NodeBuilder and NodeAssembler ---> + +func (g structReprStringjoinReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { + return structReprStringjoinReprBuilderGenerator{ + g.AdjCfg, + mixins.StringAssemblerTraits{ + g.PkgName, + g.TypeName, + "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr", + }, + g.PkgName, + g.Type, + } +} + +type structReprStringjoinReprBuilderGenerator struct { + AdjCfg *AdjunctCfg + mixins.StringAssemblerTraits + PkgName string + Type schema.TypeStruct +} + +func (structReprStringjoinReprBuilderGenerator) IsRepr() bool { return true } // hint used in some generalized templates. + +func (g structReprStringjoinReprBuilderGenerator) EmitNodeBuilderType(w io.Writer) { + emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) +} +func (g structReprStringjoinReprBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { + emitNodeBuilderMethods_typical(w, g.AdjCfg, g) + + // Generate a single-step construction function -- this is easy to do for a scalar, + // and all representations of scalar kind can be expected to have a method like this. + // The function is attached to the nodestyle for convenient namespacing; + // it needs no new memory, so it would be inappropriate to attach to the builder or assembler. + // The function is directly used internally by anything else that might involve recursive destructuring on the same scalar kind + // (for example, structs using stringjoin strategies that have one of this type as a field, etc). + // Since we're a representation of scalar kind, and can recurse, + // we ourselves presume this plain construction method must also exist for all our members. + // REVIEW: We could make an immut-safe verion of this and export it on the NodeStyle too, as `FromString(string)`. + // FUTURE: should engage validation flow. + doTemplate(` + func (_{{ .Type | TypeSymbol }}__ReprStyle) fromString(w *_{{ .Type | TypeSymbol }}, v string) error { + ss, err := mixins.SplitExact(v, "{{ .Type.RepresentationStrategy.GetDelim }}", {{ len .Type.Fields }}) + if err != nil { + return ipld.ErrUnmatchable{TypeName:"{{ .PkgName }}.{{ .Type.Name }}.Repr", Reason: err} + } + {{- $dot := . -}} {{- /* ranging modifies dot, unhelpfully */ -}} + {{- range $i, $field := .Type.Fields }} + if err := (_{{ $field.Type | TypeSymbol }}__ReprStyle{}).fromString(&w.{{ $field | FieldSymbolLower }}, ss[{{ $i }}]); err != nil { + return ipld.ErrUnmatchable{TypeName:"{{ $dot.PkgName }}.{{ $dot.Type.Name }}.Repr", Reason: err} + } + {{- end}} + return nil + } + `, w, g.AdjCfg, g) +} +func (g structReprStringjoinReprBuilderGenerator) EmitNodeAssemblerType(w io.Writer) { + doTemplate(` + type _{{ .Type | TypeSymbol }}__ReprAssembler struct { + w *_{{ .Type | TypeSymbol }} + m *schema.Maybe + } + + func (na *_{{ .Type | TypeSymbol }}__ReprAssembler) reset() {} + `, w, g.AdjCfg, g) +} +func (g structReprStringjoinReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + emitNodeAssemblerMethodAssignNull_scalar(w, g.AdjCfg, g) +} +func (g structReprStringjoinReprBuilderGenerator) EmitNodeAssemblerMethodAssignString(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 }}__ReprAssembler) AssignString(v string) error { + switch *na.m { + case schema.Maybe_Value, schema.Maybe_Null: + panic("invalid state: cannot assign into assembler that's already finished") + } + {{- if .Type | MaybeUsesPtr }} + if na.w == nil { + na.w = &_{{ .Type | TypeSymbol }}{} + } + {{- end}} + if err := (_{{ .Type | TypeSymbol }}__ReprStyle{}).fromString(na.w, v); err != nil { + return err + } + *na.m = schema.Maybe_Value + return nil + } + `, w, g.AdjCfg, g) +} + +func (g structReprStringjoinReprBuilderGenerator) 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. + doTemplate(` + func (na *_{{ .Type | TypeSymbol }}__ReprAssembler) 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") + } + {{- 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 v2, err := v.AsString(); err != nil { + return err + } else { + return na.AssignString(v2) + } + } + `, w, g.AdjCfg, g) +} +func (g structReprStringjoinReprBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { + // None for this. +} diff --git a/schema/gen/go/gen_test.go b/schema/gen/go/gen_test.go deleted file mode 100644 index cad6173f..00000000 --- a/schema/gen/go/gen_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package gengo - -import ( - "os" - "testing" - - "github.com/ipld/go-ipld-prime/schema" -) - -func TestNuevo(t *testing.T) { - os.Mkdir("_test", 0755) - openOrPanic := func(filename string) *os.File { - y, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) - if err != nil { - panic(err) - } - return y - } - - f := openOrPanic("_test/minima.go") - EmitMinima("whee", f) - - tString := schema.SpawnString("String") - 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) - - 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, true), - }, - schema.StructRepresentation_Map{}, - ) - - tKindsStroct := schema.SpawnStruct("KindsStroct", - []schema.StructField{ - schema.SpawnStructField("inty", tInt, false, false), - schema.SpawnStructField("bytey", tBytes, false, false), - schema.SpawnStructField("linky", tLink, false, false), - schema.SpawnStructField("intListy", tIntList, false, false), - schema.SpawnStructField("nullableIntListy", tNullableIntList, false, false), - }, - schema.StructRepresentation_Map{}, - ) - f = openOrPanic("_test/tString.go") - EmitFileHeader("whee", f) - EmitEntireType(NewGeneratorForKindString(tString), f) - - f = openOrPanic("_test/tInt.go") - EmitFileHeader("whee", f) - EmitEntireType(NewGeneratorForKindInt(tInt), f) - - f = openOrPanic("_test/tBytes.go") - EmitFileHeader("whee", f) - EmitEntireType(NewGeneratorForKindBytes(tBytes), f) - - f = openOrPanic("_test/tLink.go") - 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) - - f = openOrPanic("_test/tNullableIntList.go") - EmitFileHeader("whee", f) - EmitEntireType(NewGeneratorForKindList(tNullableIntList), f) - - f = openOrPanic("_test/Stract.go") - EmitFileHeader("whee", f) - EmitEntireType(NewGeneratorForKindStruct(tStract), f) - - f = openOrPanic("_test/Stract2.go") - EmitFileHeader("whee", f) - EmitEntireType(NewGeneratorForKindStruct(tStract2), f) - - f = openOrPanic("_test/Stract3.go") - EmitFileHeader("whee", f) - EmitEntireType(NewGeneratorForKindStruct(tStract3), f) - - f = openOrPanic("_test/Stroct.go") - EmitFileHeader("whee", f) - EmitEntireType(NewGeneratorForKindStruct(tStroct), f) - - f = openOrPanic("_test/KindsStroct.go") - EmitFileHeader("whee", f) - EmitEntireType(NewGeneratorForKindStruct(tKindsStroct), f) -} diff --git a/schema/gen/go/generate.go b/schema/gen/go/generate.go new file mode 100644 index 00000000..a579e203 --- /dev/null +++ b/schema/gen/go/generate.go @@ -0,0 +1,66 @@ +package gengo + +import ( + "io" + "os" + "path/filepath" + + "github.com/ipld/go-ipld-prime/schema" +) + +func Generate(pth string, pkgName string, ts schema.TypeSystem, adjCfg *AdjunctCfg) { + // Emit fixed bits. + withFile(filepath.Join(pth, "minima.go"), func(f io.Writer) { + EmitInternalEnums(pkgName, f) + }) + + // Emit a file for each type. + for _, typ := range ts.GetTypes() { + withFile(filepath.Join(pth, "t"+typ.Name().String()+".go"), func(f io.Writer) { + EmitFileHeader(pkgName, f) + switch t2 := typ.(type) { + case schema.TypeBool: + EmitEntireType(NewBoolReprBoolGenerator(pkgName, t2, adjCfg), f) + case schema.TypeInt: + EmitEntireType(NewIntReprIntGenerator(pkgName, t2, adjCfg), f) + case schema.TypeFloat: + EmitEntireType(NewFloatReprFloatGenerator(pkgName, t2, adjCfg), f) + case schema.TypeString: + EmitEntireType(NewStringReprStringGenerator(pkgName, t2, adjCfg), f) + case schema.TypeBytes: + EmitEntireType(NewBytesReprBytesGenerator(pkgName, t2, adjCfg), f) + case schema.TypeLink: + EmitEntireType(NewLinkReprLinkGenerator(pkgName, t2, adjCfg), f) + case schema.TypeStruct: + switch t2.RepresentationStrategy().(type) { + case schema.StructRepresentation_Map: + EmitEntireType(NewStructReprMapGenerator(pkgName, t2, adjCfg), f) + case schema.StructRepresentation_Stringjoin: + EmitEntireType(NewStructReprStringjoinGenerator(pkgName, t2, adjCfg), f) + default: + panic("unrecognized struct representation strategy") + } + 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 :)") + } + }) + } + + // Emit the unified type table. + withFile(filepath.Join(pth, "typeTable.go"), func(f io.Writer) { + EmitTypeTable(pkgName, ts, adjCfg, f) + }) +} + +func withFile(filename string, fn func(io.Writer)) { + f, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) + if err != nil { + panic(err) + } + defer f.Close() + fn(f) +} diff --git a/schema/gen/go/generators.go b/schema/gen/go/generators.go new file mode 100644 index 00000000..8388db73 --- /dev/null +++ b/schema/gen/go/generators.go @@ -0,0 +1,180 @@ +package gengo + +import ( + "fmt" + "io" + + "github.com/ipld/go-ipld-prime/schema" +) + +// TypeGenerator gathers all the info for generating all code related to one +// type in the schema. +type TypeGenerator interface { + // -- the natively-typed apis --> + + EmitNativeType(io.Writer) + EmitNativeAccessors(io.Writer) // depends on the kind -- field accessors for struct, typed iterators for map, etc. + EmitNativeBuilder(io.Writer) // typically emits some kind of struct that has a Build method. + EmitNativeMaybe(io.Writer) // a pointer-free 'maybe' mechanism is generated for all types. + + // -- the schema.TypedNode.Type method and vars --> + + EmitTypeConst(io.Writer) // these emit dummies for now + EmitTypedNodeMethodType(io.Writer) // these emit dummies for now + + // -- all node methods --> + // (and note that the nodeBuilder for this one should be the "semantic" one, + // e.g. it *always* acts like a map for structs, even if the repr is different.) + + NodeGenerator + + // -- and the representation and its node and nodebuilder --> + // (these vary!) + + EmitTypedNodeMethodRepresentation(io.Writer) + GetRepresentationNodeGen() NodeGenerator // includes transitively the matched NodeBuilderGenerator +} + +type NodeGenerator interface { + EmitNodeType(io.Writer) // usually already covered by EmitNativeType for the primary node, but has a nonzero body for the repr node + EmitNodeTypeAssertions(io.Writer) // optional to include this content + EmitNodeMethodReprKind(io.Writer) + EmitNodeMethodLookupString(io.Writer) + EmitNodeMethodLookup(io.Writer) + EmitNodeMethodLookupIndex(io.Writer) + EmitNodeMethodLookupSegment(io.Writer) + EmitNodeMethodMapIterator(io.Writer) // also iterator itself + EmitNodeMethodListIterator(io.Writer) // also iterator itself + EmitNodeMethodLength(io.Writer) + EmitNodeMethodIsUndefined(io.Writer) + EmitNodeMethodIsNull(io.Writer) + EmitNodeMethodAsBool(io.Writer) + EmitNodeMethodAsInt(io.Writer) + EmitNodeMethodAsFloat(io.Writer) + EmitNodeMethodAsString(io.Writer) + EmitNodeMethodAsBytes(io.Writer) + EmitNodeMethodAsLink(io.Writer) + EmitNodeMethodStyle(io.Writer) + EmitNodeStyleType(io.Writer) + GetNodeBuilderGenerator() NodeBuilderGenerator // assembler features also included inside +} + +type NodeBuilderGenerator interface { + EmitNodeBuilderType(io.Writer) + EmitNodeBuilderMethods(io.Writer) // not many, so just slung them together. + EmitNodeAssemblerType(io.Writer) // you can call this and not EmitNodeBuilderType in some situations. + EmitNodeAssemblerMethodBeginMap(io.Writer) + EmitNodeAssemblerMethodBeginList(io.Writer) + EmitNodeAssemblerMethodAssignNull(io.Writer) + EmitNodeAssemblerMethodAssignBool(io.Writer) + EmitNodeAssemblerMethodAssignInt(io.Writer) + EmitNodeAssemblerMethodAssignFloat(io.Writer) + EmitNodeAssemblerMethodAssignString(io.Writer) + EmitNodeAssemblerMethodAssignBytes(io.Writer) + EmitNodeAssemblerMethodAssignLink(io.Writer) + EmitNodeAssemblerMethodAssignNode(io.Writer) + EmitNodeAssemblerMethodStyle(io.Writer) + EmitNodeAssemblerOtherBits(io.Writer) // key and value child assemblers are done here. +} + +// EmitFileHeader emits a baseline package header that will +// allow a file with a generated type to compile. +// (Fortunately, there are no variations in this.) +func EmitFileHeader(packageName string, w io.Writer) { + fmt.Fprintf(w, "package %s\n\n", packageName) + fmt.Fprintf(w, doNotEditComment+"\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/node/mixins\"\n") + fmt.Fprintf(w, "\t\"github.com/ipld/go-ipld-prime/schema\"\n") + fmt.Fprintf(w, ")\n\n") +} + +// EmitEntireType calls all methods of TypeGenerator and streams +// all results into a single writer. +func EmitEntireType(tg TypeGenerator, w io.Writer) { + tg.EmitNativeType(w) + tg.EmitNativeAccessors(w) + tg.EmitNativeBuilder(w) + tg.EmitNativeMaybe(w) + EmitNode(tg, w) + tg.EmitTypedNodeMethodType(w) + tg.EmitTypedNodeMethodRepresentation(w) + + rng := tg.GetRepresentationNodeGen() + if rng == nil { // FIXME: hack to save me from stubbing tons right now, remove when done + return + } + EmitNode(rng, w) +} + +func EmitNode(ng NodeGenerator, w io.Writer) { + ng.EmitNodeType(w) + ng.EmitNodeTypeAssertions(w) + ng.EmitNodeMethodReprKind(w) + ng.EmitNodeMethodLookupString(w) + ng.EmitNodeMethodLookup(w) + ng.EmitNodeMethodLookupIndex(w) + ng.EmitNodeMethodLookupSegment(w) + ng.EmitNodeMethodMapIterator(w) + ng.EmitNodeMethodListIterator(w) + ng.EmitNodeMethodLength(w) + ng.EmitNodeMethodIsUndefined(w) + ng.EmitNodeMethodIsNull(w) + ng.EmitNodeMethodAsBool(w) + ng.EmitNodeMethodAsInt(w) + ng.EmitNodeMethodAsFloat(w) + ng.EmitNodeMethodAsString(w) + ng.EmitNodeMethodAsBytes(w) + ng.EmitNodeMethodAsLink(w) + ng.EmitNodeMethodStyle(w) + + ng.EmitNodeStyleType(w) + + nbg := ng.GetNodeBuilderGenerator() + if nbg == nil { // FIXME: hack to save me from stubbing tons right now, remove when done + return + } + nbg.EmitNodeBuilderType(w) + nbg.EmitNodeBuilderMethods(w) + nbg.EmitNodeAssemblerType(w) + nbg.EmitNodeAssemblerMethodBeginMap(w) + nbg.EmitNodeAssemblerMethodBeginList(w) + nbg.EmitNodeAssemblerMethodAssignNull(w) + nbg.EmitNodeAssemblerMethodAssignBool(w) + nbg.EmitNodeAssemblerMethodAssignInt(w) + nbg.EmitNodeAssemblerMethodAssignFloat(w) + nbg.EmitNodeAssemblerMethodAssignString(w) + nbg.EmitNodeAssemblerMethodAssignBytes(w) + nbg.EmitNodeAssemblerMethodAssignLink(w) + nbg.EmitNodeAssemblerMethodAssignNode(w) + nbg.EmitNodeAssemblerMethodStyle(w) + nbg.EmitNodeAssemblerOtherBits(w) +} + +func EmitTypeTable(pkgName string, ts schema.TypeSystem, adjCfg *AdjunctCfg, w io.Writer) { + // REVIEW: if "T__Repr" is how we want to expose this. We could also put 'Repr' accessors on the type/style objects. + // FUTURE: types and styles are supposed to be the same. Some of this text pretends they already are, but work is needed on this. + doTemplate(` + package `+pkgName+` + + // Type is a struct embeding a NodeStyle/Type for every Node implementation in this package. + // One of its major uses is to start the construction of a value. + // You can use it like this: + // + // `+pkgName+`.Type.YourTypeName.NewBuilder().BeginMap() //... + // + // and: + // + // `+pkgName+`.Type.OtherTypeName.NewBuilder().AssignString("x") // ... + // + var Type typeSlab + + type typeSlab struct { + {{- range . }} + {{ .Name }} _{{ . | TypeSymbol }}__Style + {{ .Name }}__Repr _{{ . | TypeSymbol }}__ReprStyle + {{- end}} + } + `, w, adjCfg, ts.GetTypes()) +} diff --git a/schema/gen/go/genpartsCommon.go b/schema/gen/go/genpartsCommon.go new file mode 100644 index 00000000..00ad046f --- /dev/null +++ b/schema/gen/go/genpartsCommon.go @@ -0,0 +1,285 @@ +package gengo + +import ( + "io" +) + +/* + This file is full of "typical" templates. + They may not be used by *every* type and representation, + but if they're extracted here, they're at least used by *many*. +*/ + +// The codegen do-not-edit warning comment. +// Follows the pattern in https://golang.org/s/generatedcode / https://github.com/golang/go/issues/13560#issuecomment-288457920 . +// Should appear somewhere near the top of every file (though precise order doesn't matter). +const doNotEditComment = `// Code generated by go-ipld-prime gengo. DO NOT EDIT.` + +// emitNativeMaybe turns out to be completely agnostic to pretty much everything; +// it doesn't vary by kind at all, and has never yet ended up needing specialization. +func emitNativeMaybe(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + doTemplate(` + type _{{ .Type | TypeSymbol }}__Maybe struct { + m schema.Maybe + v {{if not (MaybeUsesPtr .Type) }}_{{end}}{{ .Type | TypeSymbol }} + } + type Maybe{{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}__Maybe + + func (m Maybe{{ .Type | TypeSymbol }}) IsNull() bool { + return m.m == schema.Maybe_Null + } + func (m Maybe{{ .Type | TypeSymbol }}) IsUndefined() bool { + return m.m == schema.Maybe_Absent + } + func (m Maybe{{ .Type | TypeSymbol }}) Exists() bool { + return m.m == schema.Maybe_Value + } + func (m Maybe{{ .Type | TypeSymbol }}) AsNode() ipld.Node { + switch m.m { + case schema.Maybe_Absent: + return ipld.Undef + case schema.Maybe_Null: + return ipld.Null + case schema.Maybe_Value: + return {{if not (MaybeUsesPtr .Type) }}&{{end}}m.v + default: + panic("unreachable") + } + } + func (m Maybe{{ .Type | TypeSymbol }}) Must() {{ .Type | TypeSymbol }} { + if !m.Exists() { + panic("unbox of a maybe rejected") + } + return {{if not (MaybeUsesPtr .Type) }}&{{end}}m.v + } + `, w, adjCfg, data) +} + +func emitNativeType_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // Using a struct with a single member is the same size in memory as a typedef, + // while also having the advantage of meaning we can block direct casting, + // which is desirable because the compiler then ensures our validate methods can't be evaded. + doTemplate(` + type _{{ .Type | TypeSymbol }} struct{ x {{ .ReprKind | KindPrim }} } + type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }} + `, w, adjCfg, data) +} + +func emitNativeAccessors_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // The node interface's `AsFoo` method is almost sufficient... but + // this method unboxes without needing to return an error that's statically impossible, + // which makes it easier to use in chaining. + doTemplate(` + func (n {{ .Type | TypeSymbol }}) {{ .ReprKind.String | title }}() {{ .ReprKind | KindPrim }} { + return n.x + } + `, w, adjCfg, data) +} + +func emitNativeBuilder_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // Generate a single-step construction function -- this is easy to do for a scalar, + // and all representations of scalar kind can be expected to have a method like this. + // The function is attached to the nodestyle for convenient namespacing; + // it needs no new memory, so it would be inappropriate to attach to the builder or assembler. + // FUTURE: should engage validation flow. + doTemplate(` + func (_{{ .Type | TypeSymbol }}__Style) From{{ .ReprKind.String | title }}(v {{ .ReprKind | KindPrim }}) ({{ .Type | TypeSymbol }}, error) { + n := _{{ .Type | TypeSymbol }}{v} + return &n, nil + } + `, w, adjCfg, data) +} + +func emitNodeTypeAssertions_typical(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + doTemplate(` + var _ ipld.Node = ({{ .Type | TypeSymbol }})(&_{{ .Type | TypeSymbol }}{}) + var _ schema.TypedNode = ({{ .Type | TypeSymbol }})(&_{{ .Type | TypeSymbol }}{}) + `, w, adjCfg, data) +} + +func emitNodeMethodAsKind_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + doTemplate(` + func (n {{ .Type | TypeSymbol }}) As{{ .ReprKind.String | title }}() ({{ .ReprKind | KindPrim }}, error) { + return n.x, nil + } + `, w, adjCfg, data) +} + +func emitNodeMethodStyle_typical(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + doTemplate(` + func ({{ if .IsRepr }}_{{end}}{{ .Type | TypeSymbol }}{{ if .IsRepr }}__Repr{{end}}) Style() ipld.NodeStyle { + return _{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Style{} + } + `, w, adjCfg, data) +} + +// nodeStyle doesn't really vary textually at all between types and kinds +// because it's just builders and standard resets. +func emitNodeStyleType_typical(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + doTemplate(` + type _{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Style struct{} + + func (_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Style) NewBuilder() ipld.NodeBuilder { + var nb _{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Builder + nb.Reset() + return &nb + } + `, w, adjCfg, data) +} + +// emitTypicalTypedNodeMethodRepresentation does... what it says on the tin. +// +// For most types, the way to get the representation node pointer doesn't +// textually depend on either the node implementation details nor what the representation strategy is, +// or really much at all for that matter. +// It only depends on that they have the same structure, so this cast works. +// +// Most (all?) types can use this. However, it's here rather in the mixins, for two reasons: +// one, it still seems possible to imagine we'll have a type someday for which this pattern won't hold; +// and two, mixins are also used in the repr generators, and it wouldn't be all sane for this method to end up also on reprs. +func emitTypicalTypedNodeMethodRepresentation(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + doTemplate(` + func (n {{ .Type | TypeSymbol }}) Representation() ipld.Node { + return (*_{{ .Type | TypeSymbol }}__Repr)(n) + } + `, w, adjCfg, data) +} + +// Turns out basically all builders are just an embed of the corresponding assembler. +func emitEmitNodeBuilderType_typical(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + doTemplate(` + type _{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Builder struct { + _{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler + } + `, w, adjCfg, data) +} + +// Builder build and reset methods are common even when some parts of the assembler vary. +// We count on the zero value of any addntl non-common fields of the assembler being correct. +func emitNodeBuilderMethods_typical(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + doTemplate(` + func (nb *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Builder) Build() ipld.Node { + if *nb.m != schema.Maybe_Value { + panic("invalid state: cannot call Build on an assembler that's not finished") + } + return nb.w + } + func (nb *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Builder) Reset() { + var w _{{ .Type | TypeSymbol }} + var m schema.Maybe + *nb = _{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Builder{_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler{w: &w, m: &m}} + } + `, w, adjCfg, data) +} + +// emitNodeAssemblerType_scalar emits a NodeAssembler that's typical for a scalar. +// Types that are recursive tend to have more state and custom stuff, so won't use this +// (although the 'm' and 'w' variable names may still be presumed universally). +func emitNodeAssemblerType_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + doTemplate(` + type _{{ .Type | TypeSymbol }}__Assembler struct { + w *_{{ .Type | TypeSymbol }} + m *schema.Maybe + } + + func (na *_{{ .Type | TypeSymbol }}__Assembler) reset() {} + `, w, adjCfg, data) +} + +func emitNodeAssemblerMethodAssignNull_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + doTemplate(` + func (na *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) AssignNull() error { + switch *na.m { + case allowNull: + *na.m = schema.Maybe_Null + return nil + case schema.Maybe_Absent: + return mixins.{{ .ReprKind.String | title }}Assembler{"{{ .PkgName }}.{{ .TypeName }}{{ if .IsRepr }}.Repr{{end}}"}.AssignNull() + case schema.Maybe_Value, schema.Maybe_Null: + panic("invalid state: cannot assign into assembler that's already finished") + } + panic("unreachable") + } + `, w, adjCfg, data) +} + +// almost the same as the variant for scalars, but also has to check for midvalue state. +func emitNodeAssemblerMethodAssignNull_recursive(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + doTemplate(` + func (na *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) AssignNull() error { + switch *na.m { + case allowNull: + *na.m = schema.Maybe_Null + return nil + case schema.Maybe_Absent: + return mixins.{{ .ReprKind.String | title }}Assembler{"{{ .PkgName }}.{{ .TypeName }}{{ if .IsRepr }}.Repr{{end}}"}.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, adjCfg, data) +} + +// works for the AssignFoo methods for scalar kinds that are just boxing a thing. +// There's no equivalent of this at all for recursives -- they're too diverse. +func emitNodeAssemblerMethodAssignKind_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // 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) Assign{{ .ReprKind.String | title }}(v {{ .ReprKind | KindPrim }}) error { + switch *na.m { + case schema.Maybe_Value, schema.Maybe_Null: + panic("invalid state: cannot assign into assembler that's already finished") + } + {{- if .Type | MaybeUsesPtr }} + if na.w == nil { + na.w = &_{{ .Type | TypeSymbol }}{} + } + {{- end}} + na.w.x = v + *na.m = schema.Maybe_Value + return nil + } + `, w, adjCfg, data) +} + +// leans heavily on the fact all the AsFoo and AssignFoo methods follow a very consistent textual pattern. +// FUTURE: may be able to get this to work for recursives, too -- but maps and lists each have very unique bottom thirds of this function. +func emitNodeAssemblerMethodAssignNode_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // 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. + 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") + } + {{- 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 v2, err := v.As{{ .ReprKind.String | title }}(); err != nil { + return err + } else { + return na.Assign{{ .ReprKind.String | title }}(v2) + } + } + `, w, adjCfg, data) +} diff --git a/schema/gen/go/genpartsList.go b/schema/gen/go/genpartsList.go new file mode 100644 index 00000000..fe32623b --- /dev/null +++ b/schema/gen/go/genpartsList.go @@ -0,0 +1,206 @@ +package gengo + +import ( + "io" +) + +// FIXME docs: these methods all say "-oid" but I think that was overoptimistic and not actually that applicable, really. +// AssignNode? Okay, that one's fine. +// The rest? They're all *very* emphatic about knowing either: +// - that na.w.x is a slice; or, +// - that there's only one 'va' (one type; and that it's reused). +// The reuse level for those two traits is pretty minimal. + +func emitNodeAssemblerMethodBeginList_listoid(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // 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. + // + // There's surprisingly little variation for IsRepr on this one: + // - the child types we *store* are the same either way, so that doesn't vary; + // - the only thing that we return that's different is... ourself. + // + // DRY: even further, to an extracted function in the final output? Maybe. + // This could be plausible, iff... the top half of the struct (na.m, na.w) was independently addressable. (na.va has a varying concrete type and blocks extractions.) + // Would also want to examine if that makes desirable trades in gsloc/asmsize/speed/debuggability. + // Only seems to apply to case of list-repr-list, so unclear if worth the effort. + doTemplate(` + func (na *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}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, adjCfg, data) +} + +func emitNodeAssemblerMethodAssignNode_listoid(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // 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. + // + // This works easily for both type-level and representational nodes because + // any divergences that have to do with the child value are nicely hidden behind `AssembleValue`. + doTemplate(` + func (na *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}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 }}{{ if .IsRepr }}.Repr{{end}}", 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, adjCfg, data) +} + +func emitNodeAssemblerHelper_listoid_tidyHelper(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // 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! + // + // DRY(nope): Can this be extracted to be a shared function between repr and type level nodes? + // It is textually identical, so... yeah, that'd be nice. But... + // Nope. It touches `la.va` directly. + // Attempting to extract that or hide it behind an interface would create virtual function calls in a very tight spot, and we don't want the execution time cost. + doTemplate(` + func (la *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}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}} + la.va.w = nil + 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, adjCfg, data) +} + +func emitNodeAssemblerHelper_listoid_listAssemblerMethods(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // DRY: Might want to split this up a bit further so it can be used by more kinds. + // Some parts of this could be reused by struct-repr-tuple, potentially, but would require being able to insert some more checks relating to length. + // This would also require excluding *all* 'va' references; those are radicaly different for structs, in that there's not even one (singular) of them. + // + // DRY(nope): Can this be extracted to a shared function in the output? + // Same story as the tidy helper -- it touches `la.va` concretely in several places, and that blocks extraction. + doTemplate(` + func (la *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}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, adjCfg, data) + doTemplate(` + func (la *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}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, adjCfg, data) + doTemplate(` + func (la *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) ValueStyle(_ int) ipld.NodeStyle { + return _{{ .Type.ValueType | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Style{} + } + `, w, adjCfg, data) +} diff --git a/schema/gen/go/genpartsMap.go b/schema/gen/go/genpartsMap.go new file mode 100644 index 00000000..0e9e8616 --- /dev/null +++ b/schema/gen/go/genpartsMap.go @@ -0,0 +1,322 @@ +package gengo + +import ( + "io" +) + +// FIXME docs: these methods all say "-oid" but I think that was overoptimistic and not actually that applicable, really. +// AssignNode? Okay, that one's fine. +// The rest? They're all *very* emphatic about knowing either: +// - that na.w.t and na.w.m are fields; or, +// - that there's only one 'ka' and 'va' (one type each; and that it's reused). +// The reuse level for those two traits is pretty minimal. + +func emitNodeAssemblerMethodBeginMap_mapoid(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // 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 }}__{{ if .IsRepr }}Repr{{end}}Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, 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}} + na.w.m = make(map[_{{ .Type.KeyType | TypeSymbol }}]{{if .Type.ValueIsNullable }}Maybe{{else}}*_{{end}}{{ .Type.ValueType | TypeSymbol }}, sizeHint) + na.w.t = make([]_{{ .Type | TypeSymbol }}__entry, 0, sizeHint) + return na, nil + } + `, w, adjCfg, data) +} + +func emitNodeAssemblerMethodAssignNode_mapoid(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // 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. + // + // This works easily for both type-level and representational nodes because + // any divergences that have to do with the child value are nicely hidden behind `AssembleKey` and `AssembleValue`. + doTemplate(` + func (na *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}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_Map { + return ipld.ErrWrongKind{TypeName: "{{ .PkgName }}.{{ .Type.Name }}{{ if .IsRepr }}.Repr{{end}}", MethodName: "AssignNode", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: v.ReprKind()} + } + itr := v.MapIterator() + for !itr.Done() { + k, v, err := itr.Next() + if err != nil { + return err + } + if err := na.AssembleKey().AssignNode(k); err != nil { + return err + } + if err := na.AssembleValue().AssignNode(v); err != nil { + return err + } + } + return na.Finish() + } + `, w, adjCfg, data) +} + +func emitNodeAssemblerHelper_mapoid_keyTidyHelper(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // This function attempts to clean up the state machine to acknolwedge key assembly finish. + // If the child was finished and we just collected it, return true and update state to maState_expectValue. + // Collecting the child includes updating the 'ma.w.m' to point into the relevant row of 'ma.w.t', since that couldn't be done earlier, + // AND initializing the 'ma.va' (since we're already holding relevant offsets into 'ma.w.t'). + // 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 maState_midKey. + // (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.) + // Completion info always comes via 'cm', and 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! + // + // DRY(nope): Can this be extracted to be a shared function between repr and type level nodes? + // It is textually identical, so... yeah, that'd be nice. But... + // Nope. It touches `ma.ka` and `ma.va` directly. + // Attempting to extract or hide those behind an interface would create virtual function calls in a very tight spot, and we don't want the execution time cost. + doTemplate(` + func (ma *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) keyFinishTidy() bool { + switch ma.cm { + case schema.Maybe_Value: + ma.ka.w = nil + tz := &ma.w.t[len(ma.w.t)-1] + ma.cm = schema.Maybe_Absent + ma.state = maState_expectValue + ma.w.m[tz.k] = &tz.v + {{- if .Type.ValueIsNullable }} + {{- if not (MaybeUsesPtr .Type.ValueType) }} + ma.va.w = &tz.v.v + {{- end}} + ma.va.m = &tz.v.m + tz.v.m = allowNull + {{- else}} + ma.va.w = &tz.v + ma.va.m = &ma.cm + {{- end}} + ma.ka.reset() + return true + default: + return false + } + } + `, w, adjCfg, data) +} + +func emitNodeAssemblerHelper_mapoid_valueTidyHelper(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // 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 maState_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 maState_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! + // + // DRY(nope): Can this be extracted to be a shared function between repr and type level nodes? + // Exact same story as the key tidy helper -- touches child assemblers concretely, and that blocks extraction. + doTemplate(` + func (ma *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) valueFinishTidy() bool { + {{- if .Type.ValueIsNullable }} + tz := &ma.w.t[len(ma.w.t)-1] + switch tz.v.m { + case schema.Maybe_Null: + ma.state = maState_initial + ma.va.reset() + return true + case schema.Maybe_Value: + {{- if (MaybeUsesPtr .Type.ValueType) }} + tz.v.v = ma.va.w + {{- end}} + ma.va.w = nil + ma.state = maState_initial + ma.va.reset() + return true + {{- else}} + switch ma.cm { + case schema.Maybe_Value: + ma.va.w = nil + ma.cm = schema.Maybe_Absent + ma.state = maState_initial + ma.va.reset() + return true + {{- end}} + default: + return false + } + } + `, w, adjCfg, data) +} + +func emitNodeAssemblerHelper_mapoid_mapAssemblerMethods(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { + // FUTURE: some of the setup of the child assemblers could probably be DRY'd up. + // + // REVIEW: there's a copy-by-value of k2 that's avoidable. But it simplifies the error path. Worth working on? + // + // REVIEW: processing the key via the reprStyle of the key even when we're at the type level if it's type kind isn't string is currently supported, but should it be? or is that more confusing than valuable? + // Very possible that it shouldn't be supported: the full-on keyAssembler route won't accept this, so consistency with that might be best. + // On the other hand, lookups by string *do* support this kind of processing (and it must, or PathSegment utility becomes unacceptably damaged), so either way, something feels surprising. + // + // DRY(nope): Can this be extracted to a shared function in the output? + // Same story as the tidy helpers -- it touches `va` and `ka` concretely in several places, and that blocks extraction. + // + // DRY: a lot of the state transition fences again are common for all mapoids, and could probably even be a function over '*state'... + // except for the fact they need to call the valueFinishTidy function, which is another one of those points that blocks extraction because we strongly don't want virtual functions calls there. + // Maybe the templates can be textually dedup'd more, though, at least. + doTemplate(` + func (ma *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) AssembleEntry(k string) (ipld.NodeAssembler, error) { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleEntry cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleEntry cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleEntry cannot be called on an assembler that's already finished") + } + + var k2 _{{ .Type.KeyType | TypeSymbol }} + {{- if or (not (eq .Type.KeyType.Kind.String "String")) .IsRepr }} + if err := (_{{ .Type.KeyType | TypeSymbol }}__ReprStyle{}).fromString(&k2, k); err != nil { + return nil, err // TODO wrap in some kind of ErrInvalidKey + } + {{- else}} + if err := (_{{ .Type.KeyType | TypeSymbol }}__Style{}).fromString(&k2, k); err != nil { + return nil, err // TODO wrap in some kind of ErrInvalidKey + } + {{- end}} + if _, exists := ma.w.m[k2]; exists { + return nil, ipld.ErrRepeatedMapKey{&k2} + } + ma.w.t = append(ma.w.t, _{{ .Type | TypeSymbol }}__entry{k: k2}) + tz := &ma.w.t[len(ma.w.t)-1] + ma.state = maState_midValue + + ma.w.m[k2] = &tz.v + {{- if .Type.ValueIsNullable }} + {{- if not (MaybeUsesPtr .Type.ValueType) }} + ma.va.w = &tz.v.v + {{- end}} + ma.va.m = &tz.v.m + tz.v.m = allowNull + {{- else}} + ma.va.w = &tz.v + ma.va.m = &ma.cm + {{- end}} + return &ma.va, nil + } + `, w, adjCfg, data) + doTemplate(` + func (ma *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) AssembleKey() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: AssembleKey cannot be called when in the middle of assembling another key") + case maState_expectValue: + panic("invalid state: AssembleKey cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: AssembleKey cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: AssembleKey cannot be called on an assembler that's already finished") + } + ma.w.t = append(ma.w.t, _{{ .Type | TypeSymbol }}__entry{}) + ma.state = maState_midKey + ma.ka.m = &ma.cm + ma.ka.w = &ma.w.t[len(ma.w.t)-1].k + return &ma.ka + } + `, w, adjCfg, data) + doTemplate(` + func (ma *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) AssembleValue() ipld.NodeAssembler { + switch ma.state { + case maState_initial: + panic("invalid state: AssembleValue cannot be called when no key is primed") + case maState_midKey: + if !ma.keyFinishTidy() { + panic("invalid state: AssembleValue cannot be called when in the middle of assembling a key") + } // if tidy success: carry on + case maState_expectValue: + // carry on + case maState_midValue: + panic("invalid state: AssembleValue cannot be called when in the middle of assembling another value") + case maState_finished: + panic("invalid state: AssembleValue cannot be called on an assembler that's already finished") + } + ma.state = maState_midValue + return &ma.va + } + `, w, adjCfg, data) + doTemplate(` + func (ma *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) Finish() error { + switch ma.state { + case maState_initial: + // carry on + case maState_midKey: + panic("invalid state: Finish cannot be called when in the middle of assembling a key") + case maState_expectValue: + panic("invalid state: Finish cannot be called when expecting start of value assembly") + case maState_midValue: + if !ma.valueFinishTidy() { + panic("invalid state: Finish cannot be called when in the middle of assembling a value") + } // if tidy success: carry on + case maState_finished: + panic("invalid state: Finish cannot be called on an assembler that's already finished") + } + ma.state = maState_finished + *ma.m = schema.Maybe_Value + return nil + } + `, w, adjCfg, data) + doTemplate(` + func (ma *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) KeyStyle() ipld.NodeStyle { + return _{{ .Type.KeyType | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Style{} + } + func (ma *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) ValueStyle(_ string) ipld.NodeStyle { + return _{{ .Type.ValueType | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Style{} + } + `, w, adjCfg, data) +} diff --git a/schema/gen/go/genpartsMinima.go b/schema/gen/go/genpartsMinima.go new file mode 100644 index 00000000..239687a5 --- /dev/null +++ b/schema/gen/go/genpartsMinima.go @@ -0,0 +1,59 @@ +package gengo + +import ( + "fmt" + "io" + + wish "github.com/warpfork/go-wish" +) + +// EmitInternalEnums creates a file with enum types used internally. +// For example, the state machine values used in map and list builders. +// These always need to exist exactly once in each package created by codegen. +func EmitInternalEnums(packageName string, w io.Writer) { + fmt.Fprint(w, wish.Dedent(` + package `+packageName+` + + `+doNotEditComment+` + + import ( + "github.com/ipld/go-ipld-prime/schema" + ) + + `)) + + // The 'Maybe' enum does double-duty in this package as a state machine for assembler completion. + // + // The 'Maybe_Absent' value gains the additional semantic of "clear to assign (but not null)" + // (which works because if you're *in* a value assembler, "absent" as a final result is already off the table). + // Additionally, we get a few extra states that we cram into the same area of bits: + // - `midvalue` is used by assemblers of recursives to block AssignNull after BeginX. + // - `allowNull` is used by parent assemblers when initializing a child assembler to tell the child a transition to Maybe_Null is allowed in this context. + fmt.Fprint(w, wish.Dedent(` + const ( + midvalue = schema.Maybe(4) + allowNull = schema.Maybe(5) + ) + + `)) + + fmt.Fprint(w, wish.Dedent(` + type maState uint8 + + const ( + maState_initial maState = iota + maState_midKey + maState_expectValue + maState_midValue + maState_finished + ) + + type laState uint8 + + const ( + laState_initial laState = iota + laState_midValue + laState_finished + ) + `)) +} diff --git a/schema/gen/go/info_test.go b/schema/gen/go/info_test.go deleted file mode 100644 index 66879891..00000000 --- a/schema/gen/go/info_test.go +++ /dev/null @@ -1,51 +0,0 @@ -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. -} diff --git a/schema/gen/go/mixins/boolGenMixin.go b/schema/gen/go/mixins/boolGenMixin.go new file mode 100644 index 00000000..14b234e8 --- /dev/null +++ b/schema/gen/go/mixins/boolGenMixin.go @@ -0,0 +1,103 @@ +package mixins + +import ( + "io" + + ipld "github.com/ipld/go-ipld-prime" +) + +type BoolTraits struct { + PkgName string + TypeName string // see doc in kindTraitsGenerator + TypeSymbol string // see doc in kindTraitsGenerator +} + +func (BoolTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Bool +} +func (g BoolTraits) EmitNodeMethodReprKind(w io.Writer) { + doTemplate(` + func ({{ .TypeSymbol }}) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Bool + } + `, w, g) +} +func (g BoolTraits) EmitNodeMethodLookupString(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodLookupString(w) +} +func (g BoolTraits) EmitNodeMethodLookup(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodLookup(w) +} +func (g BoolTraits) EmitNodeMethodLookupIndex(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodLookupIndex(w) +} +func (g BoolTraits) EmitNodeMethodLookupSegment(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodLookupSegment(w) +} +func (g BoolTraits) EmitNodeMethodMapIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodMapIterator(w) +} +func (g BoolTraits) EmitNodeMethodListIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodListIterator(w) +} +func (g BoolTraits) EmitNodeMethodLength(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodLength(w) +} +func (g BoolTraits) EmitNodeMethodIsUndefined(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodIsUndefined(w) +} +func (g BoolTraits) EmitNodeMethodIsNull(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodIsNull(w) +} +func (g BoolTraits) EmitNodeMethodAsInt(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodAsInt(w) +} +func (g BoolTraits) EmitNodeMethodAsFloat(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodAsFloat(w) +} +func (g BoolTraits) EmitNodeMethodAsString(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodAsString(w) +} +func (g BoolTraits) EmitNodeMethodAsBytes(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodAsBytes(w) +} +func (g BoolTraits) EmitNodeMethodAsLink(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bool}.emitNodeMethodAsLink(w) +} + +type BoolAssemblerTraits struct { + PkgName string + TypeName string // see doc in kindAssemblerTraitsGenerator + AppliedPrefix string // see doc in kindAssemblerTraitsGenerator +} + +func (BoolAssemblerTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Bool +} +func (g BoolAssemblerTraits) EmitNodeAssemblerMethodBeginMap(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bool}.emitNodeAssemblerMethodBeginMap(w) +} +func (g BoolAssemblerTraits) EmitNodeAssemblerMethodBeginList(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bool}.emitNodeAssemblerMethodBeginList(w) +} +func (g BoolAssemblerTraits) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bool}.emitNodeAssemblerMethodAssignNull(w) +} +func (g BoolAssemblerTraits) EmitNodeAssemblerMethodAssignInt(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bool}.emitNodeAssemblerMethodAssignInt(w) +} +func (g BoolAssemblerTraits) EmitNodeAssemblerMethodAssignFloat(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bool}.emitNodeAssemblerMethodAssignFloat(w) +} +func (g BoolAssemblerTraits) EmitNodeAssemblerMethodAssignString(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bool}.emitNodeAssemblerMethodAssignString(w) +} +func (g BoolAssemblerTraits) EmitNodeAssemblerMethodAssignBytes(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bool}.emitNodeAssemblerMethodAssignBytes(w) +} +func (g BoolAssemblerTraits) EmitNodeAssemblerMethodAssignLink(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bool}.emitNodeAssemblerMethodAssignLink(w) +} +func (g BoolAssemblerTraits) EmitNodeAssemblerMethodStyle(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bool}.emitNodeAssemblerMethodStyle(w) +} diff --git a/schema/gen/go/mixins/bytesGenMixin.go b/schema/gen/go/mixins/bytesGenMixin.go new file mode 100644 index 00000000..bf6e6ca1 --- /dev/null +++ b/schema/gen/go/mixins/bytesGenMixin.go @@ -0,0 +1,103 @@ +package mixins + +import ( + "io" + + ipld "github.com/ipld/go-ipld-prime" +) + +type BytesTraits struct { + PkgName string + TypeName string // see doc in kindTraitsGenerator + TypeSymbol string // see doc in kindTraitsGenerator +} + +func (BytesTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Bytes +} +func (g BytesTraits) EmitNodeMethodReprKind(w io.Writer) { + doTemplate(` + func ({{ .TypeSymbol }}) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Bytes + } + `, w, g) +} +func (g BytesTraits) EmitNodeMethodLookupString(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodLookupString(w) +} +func (g BytesTraits) EmitNodeMethodLookup(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodLookup(w) +} +func (g BytesTraits) EmitNodeMethodLookupIndex(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodLookupIndex(w) +} +func (g BytesTraits) EmitNodeMethodLookupSegment(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodLookupSegment(w) +} +func (g BytesTraits) EmitNodeMethodMapIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodMapIterator(w) +} +func (g BytesTraits) EmitNodeMethodListIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodListIterator(w) +} +func (g BytesTraits) EmitNodeMethodLength(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodLength(w) +} +func (g BytesTraits) EmitNodeMethodIsUndefined(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodIsUndefined(w) +} +func (g BytesTraits) EmitNodeMethodIsNull(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodIsNull(w) +} +func (g BytesTraits) EmitNodeMethodAsBool(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodAsBool(w) +} +func (g BytesTraits) EmitNodeMethodAsInt(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodAsInt(w) +} +func (g BytesTraits) EmitNodeMethodAsFloat(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodAsFloat(w) +} +func (g BytesTraits) EmitNodeMethodAsString(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodAsString(w) +} +func (g BytesTraits) EmitNodeMethodAsLink(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Bytes}.emitNodeMethodAsLink(w) +} + +type BytesAssemblerTraits struct { + PkgName string + TypeName string // see doc in kindAssemblerTraitsGenerator + AppliedPrefix string // see doc in kindAssemblerTraitsGenerator +} + +func (BytesAssemblerTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Bytes +} +func (g BytesAssemblerTraits) EmitNodeAssemblerMethodBeginMap(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bytes}.emitNodeAssemblerMethodBeginMap(w) +} +func (g BytesAssemblerTraits) EmitNodeAssemblerMethodBeginList(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bytes}.emitNodeAssemblerMethodBeginList(w) +} +func (g BytesAssemblerTraits) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bytes}.emitNodeAssemblerMethodAssignNull(w) +} +func (g BytesAssemblerTraits) EmitNodeAssemblerMethodAssignBool(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bytes}.emitNodeAssemblerMethodAssignBool(w) +} +func (g BytesAssemblerTraits) EmitNodeAssemblerMethodAssignInt(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bytes}.emitNodeAssemblerMethodAssignInt(w) +} +func (g BytesAssemblerTraits) EmitNodeAssemblerMethodAssignFloat(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bytes}.emitNodeAssemblerMethodAssignFloat(w) +} +func (g BytesAssemblerTraits) EmitNodeAssemblerMethodAssignString(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bytes}.emitNodeAssemblerMethodAssignString(w) +} +func (g BytesAssemblerTraits) EmitNodeAssemblerMethodAssignLink(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bytes}.emitNodeAssemblerMethodAssignLink(w) +} +func (g BytesAssemblerTraits) EmitNodeAssemblerMethodStyle(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Bytes}.emitNodeAssemblerMethodStyle(w) +} diff --git a/schema/gen/go/mixins/floatGenMixin.go b/schema/gen/go/mixins/floatGenMixin.go new file mode 100644 index 00000000..9fa904b6 --- /dev/null +++ b/schema/gen/go/mixins/floatGenMixin.go @@ -0,0 +1,103 @@ +package mixins + +import ( + "io" + + ipld "github.com/ipld/go-ipld-prime" +) + +type FloatTraits struct { + PkgName string + TypeName string // see doc in kindTraitsGenerator + TypeSymbol string // see doc in kindTraitsGenerator +} + +func (FloatTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Float +} +func (g FloatTraits) EmitNodeMethodReprKind(w io.Writer) { + doTemplate(` + func ({{ .TypeSymbol }}) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Float + } + `, w, g) +} +func (g FloatTraits) EmitNodeMethodLookupString(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodLookupString(w) +} +func (g FloatTraits) EmitNodeMethodLookup(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodLookup(w) +} +func (g FloatTraits) EmitNodeMethodLookupIndex(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodLookupIndex(w) +} +func (g FloatTraits) EmitNodeMethodLookupSegment(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodLookupSegment(w) +} +func (g FloatTraits) EmitNodeMethodMapIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodMapIterator(w) +} +func (g FloatTraits) EmitNodeMethodListIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodListIterator(w) +} +func (g FloatTraits) EmitNodeMethodLength(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodLength(w) +} +func (g FloatTraits) EmitNodeMethodIsUndefined(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodIsUndefined(w) +} +func (g FloatTraits) EmitNodeMethodIsNull(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodIsNull(w) +} +func (g FloatTraits) EmitNodeMethodAsBool(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodAsBool(w) +} +func (g FloatTraits) EmitNodeMethodAsInt(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodAsInt(w) +} +func (g FloatTraits) EmitNodeMethodAsString(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodAsString(w) +} +func (g FloatTraits) EmitNodeMethodAsBytes(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodAsBytes(w) +} +func (g FloatTraits) EmitNodeMethodAsLink(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Float}.emitNodeMethodAsLink(w) +} + +type FloatAssemblerTraits struct { + PkgName string + TypeName string // see doc in kindAssemblerTraitsGenerator + AppliedPrefix string // see doc in kindAssemblerTraitsGenerator +} + +func (FloatAssemblerTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Float +} +func (g FloatAssemblerTraits) EmitNodeAssemblerMethodBeginMap(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Float}.emitNodeAssemblerMethodBeginMap(w) +} +func (g FloatAssemblerTraits) EmitNodeAssemblerMethodBeginList(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Float}.emitNodeAssemblerMethodBeginList(w) +} +func (g FloatAssemblerTraits) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Float}.emitNodeAssemblerMethodAssignNull(w) +} +func (g FloatAssemblerTraits) EmitNodeAssemblerMethodAssignBool(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Float}.emitNodeAssemblerMethodAssignBool(w) +} +func (g FloatAssemblerTraits) EmitNodeAssemblerMethodAssignInt(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Float}.emitNodeAssemblerMethodAssignInt(w) +} +func (g FloatAssemblerTraits) EmitNodeAssemblerMethodAssignString(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Float}.emitNodeAssemblerMethodAssignString(w) +} +func (g FloatAssemblerTraits) EmitNodeAssemblerMethodAssignBytes(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Float}.emitNodeAssemblerMethodAssignBytes(w) +} +func (g FloatAssemblerTraits) EmitNodeAssemblerMethodAssignLink(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Float}.emitNodeAssemblerMethodAssignLink(w) +} +func (g FloatAssemblerTraits) EmitNodeAssemblerMethodStyle(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Float}.emitNodeAssemblerMethodStyle(w) +} diff --git a/schema/gen/go/mixins/intGenMixin.go b/schema/gen/go/mixins/intGenMixin.go new file mode 100644 index 00000000..f0ed3f1f --- /dev/null +++ b/schema/gen/go/mixins/intGenMixin.go @@ -0,0 +1,103 @@ +package mixins + +import ( + "io" + + ipld "github.com/ipld/go-ipld-prime" +) + +type IntTraits struct { + PkgName string + TypeName string // see doc in kindTraitsGenerator + TypeSymbol string // see doc in kindTraitsGenerator +} + +func (IntTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Int +} +func (g IntTraits) EmitNodeMethodReprKind(w io.Writer) { + doTemplate(` + func ({{ .TypeSymbol }}) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Int + } + `, w, g) +} +func (g IntTraits) EmitNodeMethodLookupString(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodLookupString(w) +} +func (g IntTraits) EmitNodeMethodLookup(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodLookup(w) +} +func (g IntTraits) EmitNodeMethodLookupIndex(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodLookupIndex(w) +} +func (g IntTraits) EmitNodeMethodLookupSegment(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodLookupSegment(w) +} +func (g IntTraits) EmitNodeMethodMapIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodMapIterator(w) +} +func (g IntTraits) EmitNodeMethodListIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodListIterator(w) +} +func (g IntTraits) EmitNodeMethodLength(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodLength(w) +} +func (g IntTraits) EmitNodeMethodIsUndefined(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodIsUndefined(w) +} +func (g IntTraits) EmitNodeMethodIsNull(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodIsNull(w) +} +func (g IntTraits) EmitNodeMethodAsBool(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodAsBool(w) +} +func (g IntTraits) EmitNodeMethodAsFloat(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodAsFloat(w) +} +func (g IntTraits) EmitNodeMethodAsString(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodAsString(w) +} +func (g IntTraits) EmitNodeMethodAsBytes(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodAsBytes(w) +} +func (g IntTraits) EmitNodeMethodAsLink(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Int}.emitNodeMethodAsLink(w) +} + +type IntAssemblerTraits struct { + PkgName string + TypeName string // see doc in kindAssemblerTraitsGenerator + AppliedPrefix string // see doc in kindAssemblerTraitsGenerator +} + +func (IntAssemblerTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Int +} +func (g IntAssemblerTraits) EmitNodeAssemblerMethodBeginMap(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Int}.emitNodeAssemblerMethodBeginMap(w) +} +func (g IntAssemblerTraits) EmitNodeAssemblerMethodBeginList(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Int}.emitNodeAssemblerMethodBeginList(w) +} +func (g IntAssemblerTraits) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Int}.emitNodeAssemblerMethodAssignNull(w) +} +func (g IntAssemblerTraits) EmitNodeAssemblerMethodAssignBool(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Int}.emitNodeAssemblerMethodAssignBool(w) +} +func (g IntAssemblerTraits) EmitNodeAssemblerMethodAssignFloat(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Int}.emitNodeAssemblerMethodAssignFloat(w) +} +func (g IntAssemblerTraits) EmitNodeAssemblerMethodAssignString(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Int}.emitNodeAssemblerMethodAssignString(w) +} +func (g IntAssemblerTraits) EmitNodeAssemblerMethodAssignBytes(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Int}.emitNodeAssemblerMethodAssignBytes(w) +} +func (g IntAssemblerTraits) EmitNodeAssemblerMethodAssignLink(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Int}.emitNodeAssemblerMethodAssignLink(w) +} +func (g IntAssemblerTraits) EmitNodeAssemblerMethodStyle(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Int}.emitNodeAssemblerMethodStyle(w) +} diff --git a/schema/gen/go/mixins/kindTraits.go b/schema/gen/go/mixins/kindTraits.go new file mode 100644 index 00000000..b43b2f3c --- /dev/null +++ b/schema/gen/go/mixins/kindTraits.go @@ -0,0 +1,319 @@ +package mixins + +import ( + "io" + + ipld "github.com/ipld/go-ipld-prime" +) + +// kindTraitsGenerator is a embedded in all the other mixins, +// and handles all the method generation which is a pure function of the kind. +// +// OVERRIDE THE METHODS THAT DO APPLY TO YOUR KIND; +// the default method bodies produced by this mixin are those that return errors, +// and that is not what you want for the methods that *are* interesting for your kind. +// The kindTraitsGenerator methods will panic if called for a kind that should've overriden them. +// +// If you're implementing something that can hold "any" kind, +// probably none of these methods apply to you at all. +// +// The other types in this package embed kindTraitsGenerator with a name, +// and only forward the methods to it that don't apply for their kind; +// this means when they're used as an anonymous embed, they grant +// all the appropriate dummy methods to their container, +// while leaving the ones that are still needed entirely absent, +// so the compiler helpfully tells you to finish rather than waiting until +// runtime to panic if a should-have-been-overriden method slips through. +type kindTraitsGenerator struct { + PkgName string + TypeName string // as will be printed in messages (e.g. can be goosed up a bit, like "Thing.Repr" instead of "_Thing__Repr"). + TypeSymbol string // the identifier in code (sometimes is munged internals like "_Thing__Repr" corresponding to no publicly admitted schema.Type.Name). + Kind ipld.ReprKind +} + +func (g kindTraitsGenerator) emitNodeMethodLookupString(w io.Writer) { + if ipld.ReprKindSet_JustMap.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) LookupString(string) (ipld.Node, error) { + return mixins.{{ .Kind.String | title }}{"{{ .PkgName }}.{{ .TypeName }}"}.LookupString("") + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodLookup(w io.Writer) { + if ipld.ReprKindSet_JustMap.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) Lookup(ipld.Node) (ipld.Node, error) { + return mixins.{{ .Kind.String | title }}{"{{ .PkgName }}.{{ .TypeName }}"}.Lookup(nil) + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodLookupIndex(w io.Writer) { + if ipld.ReprKindSet_JustList.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) LookupIndex(idx int) (ipld.Node, error) { + return mixins.{{ .Kind.String | title }}{"{{ .PkgName }}.{{ .TypeName }}"}.LookupIndex(0) + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodLookupSegment(w io.Writer) { + if ipld.ReprKindSet_Recursive.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { + return mixins.{{ .Kind.String | title }}{"{{ .PkgName }}.{{ .TypeName }}"}.LookupSegment(seg) + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodMapIterator(w io.Writer) { + if ipld.ReprKindSet_JustMap.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) MapIterator() ipld.MapIterator { + return nil + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodListIterator(w io.Writer) { + if ipld.ReprKindSet_JustList.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) ListIterator() ipld.ListIterator { + return nil + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodLength(w io.Writer) { + if ipld.ReprKindSet_Recursive.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) Length() int { + return -1 + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodIsUndefined(w io.Writer) { + doTemplate(` + func ({{ .TypeSymbol }}) IsUndefined() bool { + return false + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodIsNull(w io.Writer) { + doTemplate(` + func ({{ .TypeSymbol }}) IsNull() bool { + return false + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodAsBool(w io.Writer) { + if ipld.ReprKindSet_JustBool.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) AsBool() (bool, error) { + return mixins.{{ .Kind.String | title }}{"{{ .PkgName }}.{{ .TypeName }}"}.AsBool() + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodAsInt(w io.Writer) { + if ipld.ReprKindSet_JustInt.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) AsInt() (int, error) { + return mixins.{{ .Kind.String | title }}{"{{ .PkgName }}.{{ .TypeName }}"}.AsInt() + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodAsFloat(w io.Writer) { + if ipld.ReprKindSet_JustFloat.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) AsFloat() (float64, error) { + return mixins.{{ .Kind.String | title }}{"{{ .PkgName }}.{{ .TypeName }}"}.AsFloat() + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodAsString(w io.Writer) { + if ipld.ReprKindSet_JustString.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) AsString() (string, error) { + return mixins.{{ .Kind.String | title }}{"{{ .PkgName }}.{{ .TypeName }}"}.AsString() + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodAsBytes(w io.Writer) { + if ipld.ReprKindSet_JustBytes.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) AsBytes() ([]byte, error) { + return mixins.{{ .Kind.String | title }}{"{{ .PkgName }}.{{ .TypeName }}"}.AsBytes() + } + `, w, g) +} + +func (g kindTraitsGenerator) emitNodeMethodAsLink(w io.Writer) { + if ipld.ReprKindSet_JustLink.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .TypeSymbol }}) AsLink() (ipld.Link, error) { + return mixins.{{ .Kind.String | title }}{"{{ .PkgName }}.{{ .TypeName }}"}.AsLink() + } + `, w, g) +} + +// kindAssemblerTraitsGenerator is an awfully lot like kindTraitsGenerator, +// except applying to methods for builders and assemblers. +type kindAssemblerTraitsGenerator struct { + PkgName string + TypeName string // as will be printed in messages (e.g. can be goosed up a bit, like "Thing.Repr" instead of "_Thing__Repr"). + AppliedPrefix string // the prefix of what to attach methods to... this one is a little wild: should probably be either "_{{ .Type | TypeSymbol }}__" or "_{{ .Type | TypeSymbol }}__Repr", and we'll just add the words "Builder" and "Assembler". + Kind ipld.ReprKind +} + +// bailed on extracting a common emitNodeBuilderType: too many variations in content and pointer placement to be worth it. +// bailed on extracting a common emitNodeBuilderMethods: same. +// bailed on extracting a common emitNodeAssemblerType: same. +// +// If you try to do these, you'll probably need: +// - an explicit understanding of if generating representations or not +// - to still be ready for boatloads of exceptions if the representation isn't directly castable to and from the type-level node. + +func (g kindAssemblerTraitsGenerator) emitNodeAssemblerMethodBeginMap(w io.Writer) { + if ipld.ReprKindSet_JustMap.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .AppliedPrefix }}Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { + return mixins.{{ .Kind.String | title }}Assembler{"{{ .PkgName }}.{{ .TypeName }}"}.BeginMap(0) + } + `, w, g) +} + +func (g kindAssemblerTraitsGenerator) emitNodeAssemblerMethodBeginList(w io.Writer) { + if ipld.ReprKindSet_JustList.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .AppliedPrefix }}Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { + return mixins.{{ .Kind.String | title }}Assembler{"{{ .PkgName }}.{{ .TypeName }}"}.BeginList(0) + } + `, w, g) +} + +func (g kindAssemblerTraitsGenerator) emitNodeAssemblerMethodAssignNull(w io.Writer) { + if ipld.ReprKindSet_JustNull.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func (na *{{ .AppliedPrefix }}Assembler) AssignNull() error { + return mixins.{{ .Kind.String | title }}Assembler{"{{ .PkgName }}.{{ .TypeName }}"}.AssignNull() + } + `, w, g) +} + +func (g kindAssemblerTraitsGenerator) emitNodeAssemblerMethodAssignBool(w io.Writer) { + if ipld.ReprKindSet_JustBool.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .AppliedPrefix }}Assembler) AssignBool(bool) error { + return mixins.{{ .Kind.String | title }}Assembler{"{{ .PkgName }}.{{ .TypeName }}"}.AssignBool(false) + } + `, w, g) +} + +func (g kindAssemblerTraitsGenerator) emitNodeAssemblerMethodAssignInt(w io.Writer) { + if ipld.ReprKindSet_JustInt.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .AppliedPrefix }}Assembler) AssignInt(int) error { + return mixins.{{ .Kind.String | title }}Assembler{"{{ .PkgName }}.{{ .TypeName }}"}.AssignInt(0) + } + `, w, g) +} + +func (g kindAssemblerTraitsGenerator) emitNodeAssemblerMethodAssignFloat(w io.Writer) { + if ipld.ReprKindSet_JustFloat.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .AppliedPrefix }}Assembler) AssignFloat(float64) error { + return mixins.{{ .Kind.String | title }}Assembler{"{{ .PkgName }}.{{ .TypeName }}"}.AssignFloat(0) + } + `, w, g) +} + +func (g kindAssemblerTraitsGenerator) emitNodeAssemblerMethodAssignString(w io.Writer) { + if ipld.ReprKindSet_JustString.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .AppliedPrefix }}Assembler) AssignString(string) error { + return mixins.{{ .Kind.String | title }}Assembler{"{{ .PkgName }}.{{ .TypeName }}"}.AssignString("") + } + `, w, g) +} + +func (g kindAssemblerTraitsGenerator) emitNodeAssemblerMethodAssignBytes(w io.Writer) { + if ipld.ReprKindSet_JustBytes.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .AppliedPrefix }}Assembler) AssignBytes([]byte) error { + return mixins.{{ .Kind.String | title }}Assembler{"{{ .PkgName }}.{{ .TypeName }}"}.AssignBytes(nil) + } + `, w, g) +} + +func (g kindAssemblerTraitsGenerator) emitNodeAssemblerMethodAssignLink(w io.Writer) { + if ipld.ReprKindSet_JustLink.Contains(g.Kind) { + panic("gen internals error: you should've overriden this") + } + doTemplate(` + func ({{ .AppliedPrefix }}Assembler) AssignLink(ipld.Link) error { + return mixins.{{ .Kind.String | title }}Assembler{"{{ .PkgName }}.{{ .TypeName }}"}.AssignLink(nil) + } + `, w, g) +} + +// bailed on extracting a common emitNodeAssemblerMethodAssignNode: way too many variations. + +func (g kindAssemblerTraitsGenerator) emitNodeAssemblerMethodStyle(w io.Writer) { + doTemplate(` + func ({{ .AppliedPrefix }}Assembler) Style() ipld.NodeStyle { + return {{ .AppliedPrefix }}Style{} + } + `, w, g) +} + +// bailed on extracting a common emitNodeAssemblerOtherBits: it's just self-evident there's nothing common there. diff --git a/schema/gen/go/mixins/linkGenMixin.go b/schema/gen/go/mixins/linkGenMixin.go new file mode 100644 index 00000000..3a25d9e5 --- /dev/null +++ b/schema/gen/go/mixins/linkGenMixin.go @@ -0,0 +1,103 @@ +package mixins + +import ( + "io" + + ipld "github.com/ipld/go-ipld-prime" +) + +type LinkTraits struct { + PkgName string + TypeName string // see doc in kindTraitsGenerator + TypeSymbol string // see doc in kindTraitsGenerator +} + +func (LinkTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Link +} +func (g LinkTraits) EmitNodeMethodReprKind(w io.Writer) { + doTemplate(` + func ({{ .TypeSymbol }}) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Link + } + `, w, g) +} +func (g LinkTraits) EmitNodeMethodLookupString(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodLookupString(w) +} +func (g LinkTraits) EmitNodeMethodLookup(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodLookup(w) +} +func (g LinkTraits) EmitNodeMethodLookupIndex(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodLookupIndex(w) +} +func (g LinkTraits) EmitNodeMethodLookupSegment(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodLookupSegment(w) +} +func (g LinkTraits) EmitNodeMethodMapIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodMapIterator(w) +} +func (g LinkTraits) EmitNodeMethodListIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodListIterator(w) +} +func (g LinkTraits) EmitNodeMethodLength(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodLength(w) +} +func (g LinkTraits) EmitNodeMethodIsUndefined(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodIsUndefined(w) +} +func (g LinkTraits) EmitNodeMethodIsNull(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodIsNull(w) +} +func (g LinkTraits) EmitNodeMethodAsBool(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodAsBool(w) +} +func (g LinkTraits) EmitNodeMethodAsInt(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodAsInt(w) +} +func (g LinkTraits) EmitNodeMethodAsFloat(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodAsFloat(w) +} +func (g LinkTraits) EmitNodeMethodAsString(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodAsString(w) +} +func (g LinkTraits) EmitNodeMethodAsBytes(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Link}.emitNodeMethodAsBytes(w) +} + +type LinkAssemblerTraits struct { + PkgName string + TypeName string // see doc in kindAssemblerTraitsGenerator + AppliedPrefix string // see doc in kindAssemblerTraitsGenerator +} + +func (LinkAssemblerTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Link +} +func (g LinkAssemblerTraits) EmitNodeAssemblerMethodBeginMap(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Link}.emitNodeAssemblerMethodBeginMap(w) +} +func (g LinkAssemblerTraits) EmitNodeAssemblerMethodBeginList(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Link}.emitNodeAssemblerMethodBeginList(w) +} +func (g LinkAssemblerTraits) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Link}.emitNodeAssemblerMethodAssignNull(w) +} +func (g LinkAssemblerTraits) EmitNodeAssemblerMethodAssignBool(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Link}.emitNodeAssemblerMethodAssignBool(w) +} +func (g LinkAssemblerTraits) EmitNodeAssemblerMethodAssignInt(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Link}.emitNodeAssemblerMethodAssignInt(w) +} +func (g LinkAssemblerTraits) EmitNodeAssemblerMethodAssignFloat(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Link}.emitNodeAssemblerMethodAssignFloat(w) +} +func (g LinkAssemblerTraits) EmitNodeAssemblerMethodAssignString(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Link}.emitNodeAssemblerMethodAssignString(w) +} +func (g LinkAssemblerTraits) EmitNodeAssemblerMethodAssignBytes(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Link}.emitNodeAssemblerMethodAssignBytes(w) +} +func (g LinkAssemblerTraits) EmitNodeAssemblerMethodStyle(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Link}.emitNodeAssemblerMethodStyle(w) +} diff --git a/schema/gen/go/mixins/listGenMixin.go b/schema/gen/go/mixins/listGenMixin.go new file mode 100644 index 00000000..cb9e5d08 --- /dev/null +++ b/schema/gen/go/mixins/listGenMixin.go @@ -0,0 +1,102 @@ +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 (ListTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_List +} +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 (ListAssemblerTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_List +} +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) +} diff --git a/schema/gen/go/mixins/mapGenMixin.go b/schema/gen/go/mixins/mapGenMixin.go new file mode 100644 index 00000000..11dfd692 --- /dev/null +++ b/schema/gen/go/mixins/mapGenMixin.go @@ -0,0 +1,98 @@ +package mixins + +import ( + "io" + + ipld "github.com/ipld/go-ipld-prime" +) + +type MapTraits struct { + PkgName string + TypeName string // see doc in kindTraitsGenerator + TypeSymbol string // see doc in kindTraitsGenerator +} + +func (MapTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Map +} +func (g MapTraits) EmitNodeMethodReprKind(w io.Writer) { + doTemplate(` + func ({{ .TypeSymbol }}) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Map + } + `, w, g) +} +func (g MapTraits) EmitNodeMethodLookupIndex(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Map}.emitNodeMethodLookupIndex(w) +} +func (g MapTraits) EmitNodeMethodLookupSegment(w io.Writer) { + doTemplate(` + func (n {{ .TypeSymbol }}) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { + return n.LookupString(seg.String()) + } + `, w, g) +} +func (g MapTraits) EmitNodeMethodListIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Map}.emitNodeMethodListIterator(w) +} +func (g MapTraits) EmitNodeMethodIsUndefined(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Map}.emitNodeMethodIsUndefined(w) +} +func (g MapTraits) EmitNodeMethodIsNull(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Map}.emitNodeMethodIsNull(w) +} +func (g MapTraits) EmitNodeMethodAsBool(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Map}.emitNodeMethodAsBool(w) +} +func (g MapTraits) EmitNodeMethodAsInt(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Map}.emitNodeMethodAsInt(w) +} +func (g MapTraits) EmitNodeMethodAsFloat(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Map}.emitNodeMethodAsFloat(w) +} +func (g MapTraits) EmitNodeMethodAsString(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Map}.emitNodeMethodAsString(w) +} +func (g MapTraits) EmitNodeMethodAsBytes(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Map}.emitNodeMethodAsBytes(w) +} +func (g MapTraits) EmitNodeMethodAsLink(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_Map}.emitNodeMethodAsLink(w) +} + +type MapAssemblerTraits struct { + PkgName string + TypeName string // see doc in kindAssemblerTraitsGenerator + AppliedPrefix string // see doc in kindAssemblerTraitsGenerator +} + +func (MapAssemblerTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_Map +} +func (g MapAssemblerTraits) EmitNodeAssemblerMethodBeginList(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Map}.emitNodeAssemblerMethodBeginList(w) +} +func (g MapAssemblerTraits) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Map}.emitNodeAssemblerMethodAssignNull(w) +} +func (g MapAssemblerTraits) EmitNodeAssemblerMethodAssignBool(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Map}.emitNodeAssemblerMethodAssignBool(w) +} +func (g MapAssemblerTraits) EmitNodeAssemblerMethodAssignInt(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Map}.emitNodeAssemblerMethodAssignInt(w) +} +func (g MapAssemblerTraits) EmitNodeAssemblerMethodAssignFloat(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Map}.emitNodeAssemblerMethodAssignFloat(w) +} +func (g MapAssemblerTraits) EmitNodeAssemblerMethodAssignString(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Map}.emitNodeAssemblerMethodAssignString(w) +} +func (g MapAssemblerTraits) EmitNodeAssemblerMethodAssignBytes(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Map}.emitNodeAssemblerMethodAssignBytes(w) +} +func (g MapAssemblerTraits) EmitNodeAssemblerMethodAssignLink(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Map}.emitNodeAssemblerMethodAssignLink(w) +} +func (g MapAssemblerTraits) EmitNodeAssemblerMethodStyle(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_Map}.emitNodeAssemblerMethodStyle(w) +} diff --git a/schema/gen/go/mixins/stringGenMixin.go b/schema/gen/go/mixins/stringGenMixin.go new file mode 100644 index 00000000..2da02620 --- /dev/null +++ b/schema/gen/go/mixins/stringGenMixin.go @@ -0,0 +1,103 @@ +package mixins + +import ( + "io" + + ipld "github.com/ipld/go-ipld-prime" +) + +type StringTraits struct { + PkgName string + TypeName string // see doc in kindTraitsGenerator + TypeSymbol string // see doc in kindTraitsGenerator +} + +func (StringTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_String +} +func (g StringTraits) EmitNodeMethodReprKind(w io.Writer) { + doTemplate(` + func ({{ .TypeSymbol }}) ReprKind() ipld.ReprKind { + return ipld.ReprKind_String + } + `, w, g) +} +func (g StringTraits) EmitNodeMethodLookupString(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodLookupString(w) +} +func (g StringTraits) EmitNodeMethodLookup(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodLookup(w) +} +func (g StringTraits) EmitNodeMethodLookupIndex(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodLookupIndex(w) +} +func (g StringTraits) EmitNodeMethodLookupSegment(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodLookupSegment(w) +} +func (g StringTraits) EmitNodeMethodMapIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodMapIterator(w) +} +func (g StringTraits) EmitNodeMethodListIterator(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodListIterator(w) +} +func (g StringTraits) EmitNodeMethodLength(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodLength(w) +} +func (g StringTraits) EmitNodeMethodIsUndefined(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodIsUndefined(w) +} +func (g StringTraits) EmitNodeMethodIsNull(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodIsNull(w) +} +func (g StringTraits) EmitNodeMethodAsBool(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodAsBool(w) +} +func (g StringTraits) EmitNodeMethodAsInt(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodAsInt(w) +} +func (g StringTraits) EmitNodeMethodAsFloat(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodAsFloat(w) +} +func (g StringTraits) EmitNodeMethodAsBytes(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodAsBytes(w) +} +func (g StringTraits) EmitNodeMethodAsLink(w io.Writer) { + kindTraitsGenerator{g.PkgName, g.TypeName, g.TypeSymbol, ipld.ReprKind_String}.emitNodeMethodAsLink(w) +} + +type StringAssemblerTraits struct { + PkgName string + TypeName string // see doc in kindAssemblerTraitsGenerator + AppliedPrefix string // see doc in kindAssemblerTraitsGenerator +} + +func (StringAssemblerTraits) ReprKind() ipld.ReprKind { + return ipld.ReprKind_String +} +func (g StringAssemblerTraits) EmitNodeAssemblerMethodBeginMap(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_String}.emitNodeAssemblerMethodBeginMap(w) +} +func (g StringAssemblerTraits) EmitNodeAssemblerMethodBeginList(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_String}.emitNodeAssemblerMethodBeginList(w) +} +func (g StringAssemblerTraits) EmitNodeAssemblerMethodAssignNull(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_String}.emitNodeAssemblerMethodAssignNull(w) +} +func (g StringAssemblerTraits) EmitNodeAssemblerMethodAssignBool(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_String}.emitNodeAssemblerMethodAssignBool(w) +} +func (g StringAssemblerTraits) EmitNodeAssemblerMethodAssignInt(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_String}.emitNodeAssemblerMethodAssignInt(w) +} +func (g StringAssemblerTraits) EmitNodeAssemblerMethodAssignFloat(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_String}.emitNodeAssemblerMethodAssignFloat(w) +} +func (g StringAssemblerTraits) EmitNodeAssemblerMethodAssignBytes(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_String}.emitNodeAssemblerMethodAssignBytes(w) +} +func (g StringAssemblerTraits) EmitNodeAssemblerMethodAssignLink(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_String}.emitNodeAssemblerMethodAssignLink(w) +} +func (g StringAssemblerTraits) EmitNodeAssemblerMethodStyle(w io.Writer) { + kindAssemblerTraitsGenerator{g.PkgName, g.TypeName, g.AppliedPrefix, ipld.ReprKind_String}.emitNodeAssemblerMethodStyle(w) +} diff --git a/schema/gen/go/mixins/templateUtil.go b/schema/gen/go/mixins/templateUtil.go new file mode 100644 index 00000000..9afacabc --- /dev/null +++ b/schema/gen/go/mixins/templateUtil.go @@ -0,0 +1,20 @@ +package mixins + +import ( + "io" + "strings" + "text/template" + + wish "github.com/warpfork/go-wish" +) + +func doTemplate(tmplstr string, w io.Writer, data interface{}) { + tmpl := template.Must(template.New(""). + Funcs(template.FuncMap{ + "title": func(s string) string { return strings.Title(s) }, + }). + Parse(wish.Dedent(tmplstr))) + if err := tmpl.Execute(w, data); err != nil { + panic(err) + } +} diff --git a/schema/gen/go/munges.go b/schema/gen/go/munges.go deleted file mode 100644 index 328c47e1..00000000 --- a/schema/gen/go/munges.go +++ /dev/null @@ -1,61 +0,0 @@ -package gengo - -import ( - "github.com/ipld/go-ipld-prime/schema" -) - -func mungeTypeNodeIdent(t schema.Type) string { - return string(t.Name()) -} - -// future: something return a "_x__Node" might also make an appearance, -// which would address the fun detail that a gen'd struct type might not actually implement Node directly. - -func mungeTypeNodeItrIdent(t schema.Type) string { - return "_" + string(t.Name()) + "__Itr" -} - -// maps and lists are going to want a nicely-typed rather than boxed iterator too. -// the method on the node will probby just be called "Iterator" because we can. -// but we might want to change the above "__Itr" const to something else -// (maybe even just back to "__MapItr" and "__ListItr" is good). - -func mungeTypeNodebuilderIdent(t schema.Type) string { - return "_" + string(t.Name()) + "__NodeBuilder" -} - -func mungeTypeNodeMapBuilderIdent(t schema.Type) string { - return "_" + string(t.Name()) + "__MapBuilder" -} - -func mungeTypeNodeListBuilderIdent(t schema.Type) string { - return "_" + string(t.Name()) + "__ListBuilder" -} - -func mungeTypeReprNodeIdent(t schema.Type) string { - return "_" + string(t.Name()) + "__Repr" -} - -func mungeTypeReprNodeItrIdent(t schema.Type) string { - return "_" + string(t.Name()) + "__ReprItr" -} - -func mungeTypeReprNodebuilderIdent(t schema.Type) string { - return "_" + string(t.Name()) + "__ReprBuilder" -} - -func mungeTypeReprNodeMapBuilderIdent(t schema.Type) string { - return "_" + string(t.Name()) + "__ReprMapBuilder" -} - -func mungeTypeReprNodeListBuilderIdent(t schema.Type) string { - return "_" + string(t.Name()) + "__ReprListBuilder" -} - -func mungeNodebuilderConstructorIdent(t schema.Type) string { - return string(t.Name()) + "__NodeBuilder" -} - -func mungeReprNodebuilderConstructorIdent(t schema.Type) string { - return string(t.Name()) + "__ReprBuilder" -} diff --git a/schema/gen/go/templateUtil.go b/schema/gen/go/templateUtil.go index 6e28b0f4..2dcfa1d7 100644 --- a/schema/gen/go/templateUtil.go +++ b/schema/gen/go/templateUtil.go @@ -5,43 +5,66 @@ import ( "strings" "text/template" - ipld "github.com/ipld/go-ipld-prime" - wish "github.com/warpfork/go-wish" + + ipld "github.com/ipld/go-ipld-prime" ) -func doTemplate(tmplstr string, w io.Writer, data interface{}) { +func doTemplate(tmplstr string, w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 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. - }, - - // 'Add' does what it says on the tin. - "Add": func(a, b int) int { - return a + b + "TypeSymbol": adjCfg.TypeSymbol, + "FieldSymbolLower": adjCfg.FieldSymbolLower, + "FieldSymbolUpper": adjCfg.FieldSymbolUpper, + "MaybeUsesPtr": adjCfg.MaybeUsesPtr, + "KindPrim": func(k ipld.ReprKind) string { + switch k { + case ipld.ReprKind_Map: + panic("this isn't useful for non-scalars") + case ipld.ReprKind_List: + panic("this isn't useful for non-scalars") + case ipld.ReprKind_Null: + panic("this isn't useful for null") + case ipld.ReprKind_Bool: + return "bool" + case ipld.ReprKind_Int: + return "int" + case ipld.ReprKind_Float: + return "float64" + case ipld.ReprKind_String: + return "string" + case ipld.ReprKind_Bytes: + return "[]byte" + case ipld.ReprKind_Link: + return "ipld.Link" + default: + panic("invalid enumeration value!") + } }, - - // Title case. Used to make a exported symbol. Could be more efficient. - "titlize": strings.Title, - - "mungeTypeNodeIdent": mungeTypeNodeIdent, - "mungeTypeNodeItrIdent": mungeTypeNodeItrIdent, - "mungeTypeNodebuilderIdent": mungeTypeNodebuilderIdent, - "mungeTypeNodeMapBuilderIdent": mungeTypeNodeMapBuilderIdent, - "mungeTypeNodeListBuilderIdent": mungeTypeNodeListBuilderIdent, - "mungeTypeReprNodeIdent": mungeTypeReprNodeIdent, - "mungeTypeReprNodeItrIdent": mungeTypeReprNodeItrIdent, - "mungeTypeReprNodebuilderIdent": mungeTypeReprNodebuilderIdent, - "mungeTypeReprNodeMapBuilderIdent": mungeTypeReprNodeMapBuilderIdent, - "mungeTypeReprNodeListBuilderIdent": mungeTypeReprNodeListBuilderIdent, - - "mungeNodebuilderConstructorIdent": mungeNodebuilderConstructorIdent, - "mungeReprNodebuilderConstructorIdent": mungeReprNodebuilderConstructorIdent, + "add": func(a, b int) int { return a + b }, + "title": func(s string) string { return strings.Title(s) }, }). + // Seriously consider prepending `{{ $dot := . }}` (or 'top', or something). + // Or a func into the map that causes `dot` to mean `func() interface{} { return data }`. + // The number of times that the range feature has a dummy capture line above it is... not reasonable. + // (Grep for "/* ranging modifies dot, unhelpfully */" -- empirically, it's over 20 times already.) Parse(wish.Dedent(tmplstr))) if err := tmpl.Execute(w, data); err != nil { panic(err) } } + +// We really need to do some more composable stuff around here. +// Generators should probably be carrying down their own doTemplate methods that curry customizations. +// E.g., map generators would benefit hugely from being able to make a clause for "entTypeStrung", "mTypeStrung", etc. +// +// Open question: how exactly? Should some of this stuff should be composed by: +// - composing template fragments; +// - amending the funcmap; +// - computing the whole result and injecting it as a string; +// - ... combinations of the above? +// Adding to the complexity of the question is that sometimes we want to be +// doing composition inside the output (e.g. DRY by functions in the result, +// rather than by DRY'ing the templates). +// Best practice to make this evolve nicely is not at all obvious to this author. +// diff --git a/schema/gen/go/testEngine_disabled_test.go b/schema/gen/go/testEngine_disabled_test.go new file mode 100644 index 00000000..a63d0d8b --- /dev/null +++ b/schema/gen/go/testEngine_disabled_test.go @@ -0,0 +1,40 @@ +// +build skipgenbehavtests + +package gengo + +import ( + "fmt" + "io" + "os" + "os/exec" + "testing" +) + +func buildGennedCode(t *testing.T, prefix string, pkgName string) { + // Emit a small file with a 'main' method. + // 'go build' doesn't like it we're in a package called "main" and there isn't one + // (and at the same time, plugins demand that they be in a package called 'main', + // so 'pkgName' in practice is almost always "main"). + // I dunno, friend. I didn't write the rules. + if pkgName == "main" { + withFile("./_test/"+prefix+"/main.go", func(w io.Writer) { + fmt.Fprintf(w, "package %s\n\n", pkgName) + fmt.Fprintf(w, "func main() {}\n") + }) + } + + // Invoke 'go build' -- nothing fancy. + cmd := exec.Command("go", "build", "./_test/"+prefix) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + t.Fatalf("genned code failed to compile: %s", err) + } +} + +func runBehavioralTests(t *testing.T, _ string, _ behavioralTests) { + t.Run("bhvtest=skip", func(t *testing.T) { + t.Skip("behavioral tests for generated code skipped: you used the 'skipgenbehavtests' build tag.") + }) +} diff --git a/schema/gen/go/testEngine_nocgo_test.go b/schema/gen/go/testEngine_nocgo_test.go new file mode 100644 index 00000000..a427f7cc --- /dev/null +++ b/schema/gen/go/testEngine_nocgo_test.go @@ -0,0 +1,55 @@ +// +build !cgo,!skipgenbehavtests + +// Confession: +// This build tag specification is NOT sufficient nor necessarily correct -- +// it's a vague approximation of what's present in the stdlib 'plugin' package. +// It's also not at all a sure thing that cgo will actually *work* just +// because a build tag hasn't explicitly stated that it *mayn't* -- cgo can +// and will fail for environmental reasons at the point the compiler uses it. +// +// Ideally, there'd be a way to *ask* the plugin package if it's going to +// work or not before we try to use it; unfortunately, at the time of writing, +// it does not appear there is such an ability. +// +// If you run afoul of these build tags somehow (e.g., building plugins isn't +// possible in your environment for some reason), use the 'skipgenbehavtests' +// build tag to right yourself. That's what it's there for. + +package gengo + +import ( + "fmt" + "io" + "os" + "os/exec" + "testing" +) + +func buildGennedCode(t *testing.T, prefix string, pkgName string) { + // Emit a small file with a 'main' method. + // 'go build' doesn't like it we're in a package called "main" and there isn't one + // (and at the same time, plugins demand that they be in a package called 'main', + // so 'pkgName' in practice is almost always "main"). + // I dunno, friend. I didn't write the rules. + if pkgName == "main" { + withFile("./_test/"+prefix+"/main.go", func(w io.Writer) { + fmt.Fprintf(w, "package %s\n\n", pkgName) + fmt.Fprintf(w, "func main() {}\n") + }) + } + + // Invoke 'go build' -- nothing fancy. + cmd := exec.Command("go", "build", "./_test/"+prefix) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + t.Fatalf("genned code failed to compile: %s", err) + } +} + +func runBehavioralTests(t *testing.T, _ string, _ behavioralTests) { + t.Run("bhvtest=skip", func(t *testing.T) { + t.Skip("behavioral tests for generated code skipped: cgo is required for these tests") + }) +} diff --git a/schema/gen/go/testEngine_plugin_test.go b/schema/gen/go/testEngine_plugin_test.go new file mode 100644 index 00000000..1018850e --- /dev/null +++ b/schema/gen/go/testEngine_plugin_test.go @@ -0,0 +1,40 @@ +// +build cgo,!skipgenbehavtests + +package gengo + +import ( + "os" + "os/exec" + "plugin" + "testing" + + "github.com/ipld/go-ipld-prime" +) + +func buildGennedCode(t *testing.T, prefix string, _ string) { + // Invoke `go build` with flags to create a plugin -- we'll be able to + // load into this plugin into this selfsame process momentarily. + cmd := exec.Command("go", "build", "-o=./_test/"+prefix+"/obj.so", "-buildmode=plugin", "./_test/"+prefix) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + t.Fatalf("genned code failed to compile: %s", err) + } +} + +func runBehavioralTests(t *testing.T, prefix string, testsFn behavioralTests) { + plg, err := plugin.Open("./_test/" + prefix + "/obj.so") + if err != nil { + panic(err) // Panic because if this was going to flunk, we expected it to flunk earlier when we ran 'go build'. + } + sym, err := plg.Lookup("GetStyleByName") + if err != nil { + panic(err) + } + getStyleByName := sym.(func(string) ipld.NodeStyle) + + t.Run("bhvtest", func(t *testing.T) { + testsFn(t, getStyleByName) + }) +} diff --git a/schema/gen/go/testEngine_test.go b/schema/gen/go/testEngine_test.go new file mode 100644 index 00000000..dad1c5ce --- /dev/null +++ b/schema/gen/go/testEngine_test.go @@ -0,0 +1,86 @@ +package gengo + +import ( + "io" + "os" + "testing" + + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/schema" +) + +// behavioralTests describes the interface of a function that can be supplied +// in order to run tests on generated code. +// +// The getStyleByName function can get its job done using only interface types +// that we already know from outside any generated code, so you can write tests +// that have no _compile time_ dependency on the generated code. This makes it +// easier for IDEs and suchlike to help you write and check the test functions. +// +// Ask for styles using the type name alone (no package prefix); +// their representation styles can be obtained by appending ".Repr". +type behavioralTests func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) + +func genAndCompileAndTest( + t *testing.T, + prefix string, + pkgName string, + ts schema.TypeSystem, + adjCfg *AdjunctCfg, + testsFn behavioralTests, +) { + t.Run("generate", func(t *testing.T) { + // Make directories for the package we're about to generate. + // Everything will be prefixed with "./_test". + // You can rm-rf the whole "./_test" dir at your leisure. + // We don't by default because it's nicer to let go's builds of things cache. + // If you change the names of types, though, you'll have garbage files leftover, + // and that's currently a manual cleanup problem. Sorry. + os.Mkdir("./_test/", 0755) + os.Mkdir("./_test/"+prefix, 0755) + + // Generate... everything, really. + Generate("./_test/"+prefix, pkgName, ts, adjCfg) + + // Emit an exported top level function for getting nodestyles. + // This part isn't necessary except for a special need we have with this plugin trick; + // normally, user code uses the `{pkgname}.Style.{TypeName}` constant (so-to-speak, anyway) to get a hold of nodestyles... + // but for plugins, we need a top-level exported symbol to grab ahold of, and we can't easily look through the `Style` value + // without an interface... so we generate this function to fit the bill instead. + withFile("./_test/"+prefix+"/styleGetter.go", func(w io.Writer) { + doTemplate(` + package `+pkgName+` + + import "github.com/ipld/go-ipld-prime" + + func GetStyleByName(name string) ipld.NodeStyle { + switch name { + {{- range . }} + case "{{ .Name }}": + return _{{ . | TypeSymbol }}__Style{} + case "{{ .Name }}.Repr": + return _{{ . | TypeSymbol }}__ReprStyle{} + {{- end}} + default: + return nil + } + } + `, w, adjCfg, ts.GetTypes()) + }) + + t.Run("compile", func(t *testing.T) { + // Build the genned code. + // This will either make a plugin (which we can run behavioral tests on next!), + // or just build it quietly just to see if there are compile-time errors, + // depending on your build tags. + // See 'HACKME_testing.md' for discussion. + buildGennedCode(t, prefix, pkgName) + + // This will either load the plugin and run behavioral tests, + // or emit a dummy t.Run and a skip, + // depending on your build tags. + // See 'HACKME_testing.md' for discussion. + runBehavioralTests(t, prefix, testsFn) + }) + }) +} diff --git a/schema/gen/go/testLists_test.go b/schema/gen/go/testLists_test.go new file mode 100644 index 00000000..85315054 --- /dev/null +++ b/schema/gen/go/testLists_test.go @@ -0,0 +1,210 @@ +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) + }) + }) +} + +// TestListsContainingLists is probing *two* things: +// - that lists can nest, obviously +// - that representation semantics are held correctly when we recurse, both in builders and in reading +// To cover that latter situation, this depends on structs (so we can use rename directives on the representation to make it distinctive). +func TestListsContainingLists(t *testing.T) { + ts := schema.TypeSystem{} + ts.Init() + adjCfg := &AdjunctCfg{ + maybeUsesPtr: map[schema.TypeName]bool{}, + } + ts.Accumulate(schema.SpawnString("String")) + ts.Accumulate(schema.SpawnStruct("Frub", + []schema.StructField{ + schema.SpawnStructField("field", ts.TypeByName("String"), false, false), // plain field. + }, + schema.SpawnStructRepresentationMap(map[string]string{ + "field": "encoded", + }), + )) + ts.Accumulate(schema.SpawnList("List__Frub", + ts.TypeByName("Frub"), false)) + ts.Accumulate(schema.SpawnList("List__List__Frub", + ts.TypeByName("List__Frub"), true)) + + prefix := "lists-of-lists" + pkgName := "main" + genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) { + ns := getStyleByName("List__List__Frub") + nsr := getStyleByName("List__List__Frub.Repr") + var n schema.TypedNode + t.Run("typed-create", func(t *testing.T) { + n = fluent.MustBuildList(ns, 3, func(la fluent.ListAssembler) { + la.AssembleValue().CreateList(3, func(la fluent.ListAssembler) { + la.AssembleValue().CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry("field").AssignString("11") }) + la.AssembleValue().CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry("field").AssignString("12") }) + la.AssembleValue().CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry("field").AssignString("13") }) + }) + la.AssembleValue().CreateList(1, func(la fluent.ListAssembler) { + la.AssembleValue().CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry("field").AssignString("21") }) + }) + la.AssembleValue().CreateList(2, func(la fluent.ListAssembler) { + la.AssembleValue().CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry("field").AssignString("31") }) + la.AssembleValue().CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry("field").AssignString("32") }) + }) + }).(schema.TypedNode) + t.Run("typed-read", func(t *testing.T) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_List) + Require(t, n.Length(), ShouldEqual, 3) + Require(t, must.Node(n.LookupIndex(0)).Length(), ShouldEqual, 3) + Require(t, must.Node(n.LookupIndex(1)).Length(), ShouldEqual, 1) + Require(t, must.Node(n.LookupIndex(2)).Length(), ShouldEqual, 2) + + Wish(t, must.String(must.Node(must.Node(must.Node(n.LookupIndex(0)).LookupIndex(0)).LookupString("field"))), ShouldEqual, "11") + Wish(t, must.String(must.Node(must.Node(must.Node(n.LookupIndex(0)).LookupIndex(2)).LookupString("field"))), ShouldEqual, "13") + Wish(t, must.String(must.Node(must.Node(must.Node(n.LookupIndex(1)).LookupIndex(0)).LookupString("field"))), ShouldEqual, "21") + Wish(t, must.String(must.Node(must.Node(must.Node(n.LookupIndex(2)).LookupIndex(1)).LookupString("field"))), ShouldEqual, "32") + }) + t.Run("repr-read", func(t *testing.T) { + nr := n.Representation() + Require(t, nr.ReprKind(), ShouldEqual, ipld.ReprKind_List) + Require(t, nr.Length(), ShouldEqual, 3) + Require(t, must.Node(nr.LookupIndex(0)).Length(), ShouldEqual, 3) + Require(t, must.Node(nr.LookupIndex(1)).Length(), ShouldEqual, 1) + Require(t, must.Node(nr.LookupIndex(2)).Length(), ShouldEqual, 2) + + Wish(t, must.String(must.Node(must.Node(must.Node(nr.LookupIndex(0)).LookupIndex(0)).LookupString("encoded"))), ShouldEqual, "11") + Wish(t, must.String(must.Node(must.Node(must.Node(nr.LookupIndex(0)).LookupIndex(2)).LookupString("encoded"))), ShouldEqual, "13") + Wish(t, must.String(must.Node(must.Node(must.Node(nr.LookupIndex(1)).LookupIndex(0)).LookupString("encoded"))), ShouldEqual, "21") + Wish(t, must.String(must.Node(must.Node(must.Node(nr.LookupIndex(2)).LookupIndex(1)).LookupString("encoded"))), ShouldEqual, "32") + }) + }) + t.Run("repr-create", func(t *testing.T) { + nr := fluent.MustBuildList(nsr, 2, func(la fluent.ListAssembler) { + // This is the same as the type-level create earlier, except note the field names are now all different. + la.AssembleValue().CreateList(3, func(la fluent.ListAssembler) { + la.AssembleValue().CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry("encoded").AssignString("11") }) + la.AssembleValue().CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry("encoded").AssignString("12") }) + la.AssembleValue().CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry("encoded").AssignString("13") }) + }) + la.AssembleValue().CreateList(1, func(la fluent.ListAssembler) { + la.AssembleValue().CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry("encoded").AssignString("21") }) + }) + la.AssembleValue().CreateList(2, func(la fluent.ListAssembler) { + la.AssembleValue().CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry("encoded").AssignString("31") }) + la.AssembleValue().CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry("encoded").AssignString("32") }) + }) + }) + Wish(t, n, ShouldEqual, nr) + }) + }) + +} diff --git a/schema/gen/go/testMaps_test.go b/schema/gen/go/testMaps_test.go new file mode 100644 index 00000000..bebf0ce1 --- /dev/null +++ b/schema/gen/go/testMaps_test.go @@ -0,0 +1,276 @@ +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 TestMapsContainingMaybe(t *testing.T) { + ts := schema.TypeSystem{} + ts.Init() + adjCfg := &AdjunctCfg{ + maybeUsesPtr: map[schema.TypeName]bool{}, + } + ts.Accumulate(schema.SpawnString("String")) + ts.Accumulate(schema.SpawnMap("Map__String__String", + ts.TypeByName("String"), ts.TypeByName("String"), false)) + ts.Accumulate(schema.SpawnMap("Map__String__nullableString", + ts.TypeByName("String"), ts.TypeByName("String"), true)) + + test := func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) { + t.Run("non-nullable", func(t *testing.T) { + ns := getStyleByName("Map__String__String") + nsr := getStyleByName("Map__String__String.Repr") + var n schema.TypedNode + t.Run("typed-create", func(t *testing.T) { + n = fluent.MustBuildMap(ns, 2, func(ma fluent.MapAssembler) { + ma.AssembleEntry("one").AssignString("1") + ma.AssembleEntry("two").AssignString("2") + }).(schema.TypedNode) + t.Run("typed-read", func(t *testing.T) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 2) + Wish(t, must.String(must.Node(n.LookupString("one"))), ShouldEqual, "1") + Wish(t, must.String(must.Node(n.LookupString("two"))), ShouldEqual, "2") + _, err := n.LookupString("miss") + Wish(t, err, ShouldBeSameTypeAs, ipld.ErrNotExists{}) + }) + t.Run("repr-read", func(t *testing.T) { + nr := n.Representation() + Require(t, nr.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, nr.Length(), ShouldEqual, 2) + Wish(t, must.String(must.Node(nr.LookupString("one"))), ShouldEqual, "1") + Wish(t, must.String(must.Node(nr.LookupString("two"))), ShouldEqual, "2") + _, err := nr.LookupString("miss") + Wish(t, err, ShouldBeSameTypeAs, ipld.ErrNotExists{}) + }) + }) + t.Run("repr-create", func(t *testing.T) { + nr := fluent.MustBuildMap(nsr, 2, func(ma fluent.MapAssembler) { + ma.AssembleEntry("one").AssignString("1") + ma.AssembleEntry("two").AssignString("2") + }) + Wish(t, n, ShouldEqual, nr) + }) + }) + t.Run("nullable", func(t *testing.T) { + ns := getStyleByName("Map__String__nullableString") + nsr := getStyleByName("Map__String__nullableString.Repr") + var n schema.TypedNode + t.Run("typed-create", func(t *testing.T) { + n = fluent.MustBuildMap(ns, 2, func(ma fluent.MapAssembler) { + ma.AssembleEntry("one").AssignString("1") + ma.AssembleEntry("none").AssignNull() + }).(schema.TypedNode) + t.Run("typed-read", func(t *testing.T) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 2) + Wish(t, must.String(must.Node(n.LookupString("one"))), ShouldEqual, "1") + Wish(t, must.Node(n.LookupString("none")), ShouldEqual, ipld.Null) + _, err := n.LookupString("miss") + Wish(t, err, ShouldBeSameTypeAs, ipld.ErrNotExists{}) + }) + t.Run("repr-read", func(t *testing.T) { + nr := n.Representation() + Require(t, nr.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, nr.Length(), ShouldEqual, 2) + Wish(t, must.String(must.Node(nr.LookupString("one"))), ShouldEqual, "1") + Wish(t, must.Node(nr.LookupString("none")), ShouldEqual, ipld.Null) + _, err := nr.LookupString("miss") + Wish(t, err, ShouldBeSameTypeAs, ipld.ErrNotExists{}) + }) + }) + t.Run("repr-create", func(t *testing.T) { + nr := fluent.MustBuildMap(nsr, 2, func(ma fluent.MapAssembler) { + ma.AssembleEntry("one").AssignString("1") + ma.AssembleEntry("none").AssignNull() + }) + Wish(t, n, ShouldEqual, nr) + }) + }) + } + + t.Run("maybe-using-embed", func(t *testing.T) { + adjCfg.maybeUsesPtr["String"] = false + + prefix := "maps-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 := "maps-mptr" + pkgName := "main" + genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) { + test(t, getStyleByName) + }) + }) +} + +// TestMapsContainingMaps is probing *two* things: +// - that maps can nest, obviously +// - that representation semantics are held correctly when we recurse, both in builders and in reading +// To cover that latter situation, this depends on structs (so we can use rename directives on the representation to make it distinctive). +func TestMapsContainingMaps(t *testing.T) { + ts := schema.TypeSystem{} + ts.Init() + adjCfg := &AdjunctCfg{ + maybeUsesPtr: map[schema.TypeName]bool{}, + } + ts.Accumulate(schema.SpawnString("String")) + ts.Accumulate(schema.SpawnStruct("Frub", // "type Frub struct { field String (rename "encoded") }" + []schema.StructField{ + schema.SpawnStructField("field", ts.TypeByName("String"), false, false), // plain field. + }, + schema.SpawnStructRepresentationMap(map[string]string{ + "field": "encoded", + }), + )) + ts.Accumulate(schema.SpawnMap("Map__String__Frub", // "{String:Frub}" + ts.TypeByName("String"), ts.TypeByName("Frub"), false)) + ts.Accumulate(schema.SpawnMap("Map__String__nullableMap__String__Frub", // "{String:nullable {String:Frub}}" + ts.TypeByName("String"), ts.TypeByName("Map__String__Frub"), true)) + + prefix := "maps-recursive" + pkgName := "main" + genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) { + ns := getStyleByName("Map__String__nullableMap__String__Frub") + nsr := getStyleByName("Map__String__nullableMap__String__Frub.Repr") + creation := func(t *testing.T, ns ipld.NodeStyle, fieldName string) schema.TypedNode { + return fluent.MustBuildMap(ns, 3, func(ma fluent.MapAssembler) { + ma.AssembleEntry("one").CreateMap(2, func(ma fluent.MapAssembler) { + ma.AssembleEntry("zot").CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry(fieldName).AssignString("11") }) + ma.AssembleEntry("zop").CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry(fieldName).AssignString("12") }) + }) + ma.AssembleEntry("two").CreateMap(1, func(ma fluent.MapAssembler) { + ma.AssembleEntry("zim").CreateMap(1, func(ma fluent.MapAssembler) { ma.AssembleEntry(fieldName).AssignString("21") }) + }) + ma.AssembleEntry("none").AssignNull() + }).(schema.TypedNode) + } + reading := func(t *testing.T, n ipld.Node, fieldName string) { + withNode(n, func(n ipld.Node) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 3) + withNode(must.Node(n.LookupString("one")), func(n ipld.Node) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 2) + withNode(must.Node(n.LookupString("zot")), func(n ipld.Node) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 1) + Wish(t, must.String(must.Node(n.LookupString(fieldName))), ShouldEqual, "11") + }) + withNode(must.Node(n.LookupString("zop")), func(n ipld.Node) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 1) + Wish(t, must.String(must.Node(n.LookupString(fieldName))), ShouldEqual, "12") + }) + }) + withNode(must.Node(n.LookupString("two")), func(n ipld.Node) { + Wish(t, n.Length(), ShouldEqual, 1) + withNode(must.Node(n.LookupString("zim")), func(n ipld.Node) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 1) + Wish(t, must.String(must.Node(n.LookupString(fieldName))), ShouldEqual, "21") + }) + }) + withNode(must.Node(n.LookupString("none")), func(n ipld.Node) { + Wish(t, n, ShouldEqual, ipld.Null) + }) + _, err := n.LookupString("miss") + Wish(t, err, ShouldBeSameTypeAs, ipld.ErrNotExists{}) + }) + } + var n schema.TypedNode + t.Run("typed-create", func(t *testing.T) { + n = creation(t, ns, "field") + t.Run("typed-read", func(t *testing.T) { + reading(t, n, "field") + }) + t.Run("repr-read", func(t *testing.T) { + reading(t, n.Representation(), "encoded") + }) + }) + t.Run("repr-create", func(t *testing.T) { + nr := creation(t, nsr, "encoded") + Wish(t, n, ShouldEqual, nr) + }) + }) +} + +func TestMapsWithComplexKeys(t *testing.T) { + ts := schema.TypeSystem{} + ts.Init() + adjCfg := &AdjunctCfg{ + maybeUsesPtr: map[schema.TypeName]bool{}, + } + ts.Accumulate(schema.SpawnString("String")) + ts.Accumulate(schema.SpawnStruct("StringyStruct", + []schema.StructField{ + schema.SpawnStructField("foo", ts.TypeByName("String"), false, false), + schema.SpawnStructField("bar", ts.TypeByName("String"), false, false), + }, + schema.SpawnStructRepresentationStringjoin(":"), + )) + ts.Accumulate(schema.SpawnMap("Map__StringyStruct__String", + ts.TypeByName("StringyStruct"), ts.TypeByName("String"), false)) + + prefix := "maps-cmplx-keys" + pkgName := "main" + genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) { + ns := getStyleByName("Map__StringyStruct__String") + nsr := getStyleByName("Map__StringyStruct__String.Repr") + var n schema.TypedNode + t.Run("typed-create", func(t *testing.T) { + n = fluent.MustBuildMap(ns, 3, func(ma fluent.MapAssembler) { + ma.AssembleKey().CreateMap(2, func(ma fluent.MapAssembler) { + ma.AssembleEntry("foo").AssignString("a") + ma.AssembleEntry("bar").AssignString("b") + }) + ma.AssembleValue().AssignString("1") + ma.AssembleKey().CreateMap(2, func(ma fluent.MapAssembler) { + ma.AssembleEntry("foo").AssignString("c") + ma.AssembleEntry("bar").AssignString("d") + }) + ma.AssembleValue().AssignString("2") + ma.AssembleKey().CreateMap(2, func(ma fluent.MapAssembler) { + ma.AssembleEntry("foo").AssignString("e") + ma.AssembleEntry("bar").AssignString("f") + }) + ma.AssembleValue().AssignString("3") + }).(schema.TypedNode) + t.Run("typed-read", func(t *testing.T) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 3) + n2 := must.Node(n.LookupString("c:d")) + Require(t, n2.ReprKind(), ShouldEqual, ipld.ReprKind_String) + Wish(t, must.String(n2), ShouldEqual, "2") + }) + t.Run("repr-read", func(t *testing.T) { + nr := n.Representation() + Require(t, nr.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, nr.Length(), ShouldEqual, 3) + n2 := must.Node(nr.LookupString("c:d")) + Require(t, n2.ReprKind(), ShouldEqual, ipld.ReprKind_String) + Wish(t, must.String(n2), ShouldEqual, "2") + }) + }) + t.Run("repr-create", func(t *testing.T) { + nr := fluent.MustBuildMap(nsr, 3, func(ma fluent.MapAssembler) { + ma.AssembleEntry("a:b").AssignString("1") + ma.AssembleEntry("c:d").AssignString("2") + ma.AssembleEntry("e:f").AssignString("3") + }) + Wish(t, n, ShouldEqual, nr) + }) + }) +} diff --git a/schema/gen/go/testString_test.go b/schema/gen/go/testString_test.go new file mode 100644 index 00000000..67650f5b --- /dev/null +++ b/schema/gen/go/testString_test.go @@ -0,0 +1,43 @@ +package gengo + +import ( + "testing" + + . "github.com/warpfork/go-wish" + + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/schema" +) + +func TestString(t *testing.T) { + ts := schema.TypeSystem{} + ts.Init() + adjCfg := &AdjunctCfg{ + maybeUsesPtr: map[schema.TypeName]bool{}, + } + + ts.Accumulate(schema.SpawnString("String")) + + prefix := "justString" + pkgName := "main" // has to be 'main' for plugins to work. this stricture makes little sense to me, but i didn't write the rules. + genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) { + ns := getStyleByName("String") + t.Run("create string", func(t *testing.T) { + nb := ns.NewBuilder() + Wish(t, nb.AssignString("woiu"), ShouldEqual, nil) + n := nb.Build().(schema.TypedNode) + t.Run("read string", func(t *testing.T) { + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_String) + }) + t.Run("read representation", func(t *testing.T) { + nr := n.Representation() + Wish(t, nr.ReprKind(), ShouldEqual, ipld.ReprKind_String) + Wish(t, str(nr), ShouldEqual, "woiu") + }) + }) + t.Run("create null is rejected", func(t *testing.T) { + nb := ns.NewBuilder() + Wish(t, nb.AssignNull(), ShouldBeSameTypeAs, ipld.ErrWrongKind{}) + }) + }) +} diff --git a/schema/gen/go/testStructNesting_test.go b/schema/gen/go/testStructNesting_test.go new file mode 100644 index 00000000..16ec23ee --- /dev/null +++ b/schema/gen/go/testStructNesting_test.go @@ -0,0 +1,75 @@ +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 TestStructNesting(t *testing.T) { + ts := schema.TypeSystem{} + ts.Init() + adjCfg := &AdjunctCfg{ + maybeUsesPtr: map[schema.TypeName]bool{}, + } + ts.Accumulate(schema.SpawnString("String")) + ts.Accumulate(schema.SpawnStruct("SmolStruct", + []schema.StructField{ + schema.SpawnStructField("s", ts.TypeByName("String"), false, false), + }, + schema.SpawnStructRepresentationMap(map[string]string{ + "s": "q", + }), + )) + ts.Accumulate(schema.SpawnStruct("GulpoStruct", + []schema.StructField{ + schema.SpawnStructField("x", ts.TypeByName("SmolStruct"), false, false), + }, + schema.SpawnStructRepresentationMap(map[string]string{ + "x": "r", + }), + )) + + prefix := "struct-nesting" + pkgName := "main" + genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) { + ns := getStyleByName("GulpoStruct") + nsr := getStyleByName("GulpoStruct.Repr") + var n schema.TypedNode + t.Run("typed-create", func(t *testing.T) { + n = fluent.MustBuildMap(ns, 1, func(ma fluent.MapAssembler) { + ma.AssembleEntry("x").CreateMap(1, func(ma fluent.MapAssembler) { + ma.AssembleEntry("s").AssignString("woo") + }) + }).(schema.TypedNode) + t.Run("typed-read", func(t *testing.T) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 1) + n2 := must.Node(n.LookupString("x")) + Require(t, n2.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, must.String(must.Node(n2.LookupString("s"))), ShouldEqual, "woo") + }) + t.Run("repr-read", func(t *testing.T) { + nr := n.Representation() + Require(t, nr.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, nr.Length(), ShouldEqual, 1) + n2 := must.Node(nr.LookupString("r")) + Require(t, n2.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, must.String(must.Node(n2.LookupString("q"))), ShouldEqual, "woo") + }) + }) + t.Run("repr-create", func(t *testing.T) { + nr := fluent.MustBuildMap(nsr, 1, func(ma fluent.MapAssembler) { + ma.AssembleEntry("r").CreateMap(1, func(ma fluent.MapAssembler) { + ma.AssembleEntry("q").AssignString("woo") + }) + }) + Wish(t, n, ShouldEqual, nr) + }) + }) +} diff --git a/schema/gen/go/testStructReprStringjoin_test.go b/schema/gen/go/testStructReprStringjoin_test.go new file mode 100644 index 00000000..cea608db --- /dev/null +++ b/schema/gen/go/testStructReprStringjoin_test.go @@ -0,0 +1,144 @@ +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" +) + +// TestStructReprStringjoin exercises... well, what it says on the tin. +// +// These should pass even if the natural map representation doesn't. +// No maybes are exercised. +func TestStructReprStringjoin(t *testing.T) { + prefix := "structstrjoin" + pkgName := "main" + + ts := schema.TypeSystem{} + ts.Init() + adjCfg := &AdjunctCfg{ + maybeUsesPtr: map[schema.TypeName]bool{}, + } + ts.Accumulate(schema.SpawnString("String")) + ts.Accumulate(schema.SpawnStruct("StringyStruct", + []schema.StructField{ + schema.SpawnStructField("field", ts.TypeByName("String"), false, false), + }, + schema.SpawnStructRepresentationStringjoin(":"), + )) + ts.Accumulate(schema.SpawnStruct("ManystringStruct", + []schema.StructField{ + schema.SpawnStructField("foo", ts.TypeByName("String"), false, false), + schema.SpawnStructField("bar", ts.TypeByName("String"), false, false), + }, + schema.SpawnStructRepresentationStringjoin(":"), + )) + ts.Accumulate(schema.SpawnStruct("Recurzorator", + []schema.StructField{ + schema.SpawnStructField("foo", ts.TypeByName("String"), false, false), + schema.SpawnStructField("zap", ts.TypeByName("ManystringStruct"), false, false), + schema.SpawnStructField("bar", ts.TypeByName("String"), false, false), + }, + schema.SpawnStructRepresentationStringjoin("-"), + )) + + genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) { + t.Run("single field works", func(t *testing.T) { + ns := getStyleByName("StringyStruct") + nsr := getStyleByName("StringyStruct.Repr") + var n schema.TypedNode + t.Run("typed-create", func(t *testing.T) { + n = fluent.MustBuildMap(ns, 1, func(ma fluent.MapAssembler) { + ma.AssembleEntry("field").AssignString("valoo") + }).(schema.TypedNode) + t.Run("typed-read", func(t *testing.T) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 1) + Wish(t, must.String(must.Node(n.LookupString("field"))), ShouldEqual, "valoo") + }) + t.Run("repr-read", func(t *testing.T) { + nr := n.Representation() + Require(t, nr.ReprKind(), ShouldEqual, ipld.ReprKind_String) + Wish(t, must.String(nr), ShouldEqual, "valoo") + }) + }) + t.Run("repr-create", func(t *testing.T) { + nr := fluent.MustBuild(nsr, func(na fluent.NodeAssembler) { + na.AssignString("valoo") + }) + Wish(t, n, ShouldEqual, nr) + }) + }) + + t.Run("several fields work", func(t *testing.T) { + ns := getStyleByName("ManystringStruct") + nsr := getStyleByName("ManystringStruct.Repr") + var n schema.TypedNode + t.Run("typed-create", func(t *testing.T) { + n = fluent.MustBuildMap(ns, 2, func(ma fluent.MapAssembler) { + ma.AssembleEntry("foo").AssignString("v1") + ma.AssembleEntry("bar").AssignString("v2") + }).(schema.TypedNode) + t.Run("typed-read", func(t *testing.T) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 2) + Wish(t, must.String(must.Node(n.LookupString("foo"))), ShouldEqual, "v1") + Wish(t, must.String(must.Node(n.LookupString("bar"))), ShouldEqual, "v2") + }) + t.Run("repr-read", func(t *testing.T) { + nr := n.Representation() + Require(t, nr.ReprKind(), ShouldEqual, ipld.ReprKind_String) + Wish(t, must.String(nr), ShouldEqual, "v1:v2") + }) + }) + t.Run("repr-create", func(t *testing.T) { + nr := fluent.MustBuild(nsr, func(na fluent.NodeAssembler) { + na.AssignString("v1:v2") + }) + Wish(t, n, ShouldEqual, nr) + }) + }) + + t.Run("nested stringjoin structs work", func(t *testing.T) { + ns := getStyleByName("Recurzorator") + nsr := getStyleByName("Recurzorator.Repr") + var n schema.TypedNode + t.Run("typed-create", func(t *testing.T) { + n = fluent.MustBuildMap(ns, 3, func(ma fluent.MapAssembler) { + ma.AssembleEntry("foo").AssignString("v1") + ma.AssembleEntry("zap").CreateMap(2, func(ma fluent.MapAssembler) { + ma.AssembleEntry("foo").AssignString("v2") + ma.AssembleEntry("bar").AssignString("v3") + }) + ma.AssembleEntry("bar").AssignString("v4") + }).(schema.TypedNode) + t.Run("typed-read", func(t *testing.T) { + Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 3) + Wish(t, must.String(must.Node(n.LookupString("foo"))), ShouldEqual, "v1") + Wish(t, must.String(must.Node(n.LookupString("bar"))), ShouldEqual, "v4") + n2 := must.Node(n.LookupString("zap")) + Wish(t, n2.Length(), ShouldEqual, 2) + Wish(t, must.String(must.Node(n2.LookupString("foo"))), ShouldEqual, "v2") + Wish(t, must.String(must.Node(n2.LookupString("bar"))), ShouldEqual, "v3") + }) + t.Run("repr-read", func(t *testing.T) { + nr := n.Representation() + Require(t, nr.ReprKind(), ShouldEqual, ipld.ReprKind_String) + Wish(t, must.String(nr), ShouldEqual, "v1-v2:v3-v4") + }) + }) + t.Run("repr-create", func(t *testing.T) { + nr := fluent.MustBuild(nsr, func(na fluent.NodeAssembler) { + na.AssignString("v1-v2:v3-v4") + }) + Wish(t, n, ShouldEqual, nr) + }) + }) + }) +} diff --git a/schema/gen/go/testStructsContainingMaybe_test.go b/schema/gen/go/testStructsContainingMaybe_test.go new file mode 100644 index 00000000..06c91ba9 --- /dev/null +++ b/schema/gen/go/testStructsContainingMaybe_test.go @@ -0,0 +1,357 @@ +package gengo + +import ( + "testing" + + . "github.com/warpfork/go-wish" + + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/schema" +) + +// TestStructsContainingMaybe checks all the variations of "nullable" and "optional" on struct fields. +// It does this twice: once for the child maybes being implemented with pointers, +// and once with maybes implemented as embeds. +// The child values are scalars. +// +// Both type-level generic build and access as well as representation build and access are exercised; +// the representation used is map (the native representation for structs). +func TestStructsContainingMaybe(t *testing.T) { + // There's a lot of cases to cover so a shorthand labels helper funcs: + // - 'v' -- value in that entry + // - 'z' -- null in that entry + // - 'u' -- undefined/absent entry + build_vvvvv := func(t *testing.T, ns ipld.NodeStyle) schema.TypedNode { + nb := ns.NewBuilder() + ma, err := nb.BeginMap(5) + Require(t, err, ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f1"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("a"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f2"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("b"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f3"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("c"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f4"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("d"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f5"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("e"), ShouldEqual, nil) + Wish(t, ma.Finish(), ShouldEqual, nil) + return nb.Build().(schema.TypedNode) + } + build_vvvvv_repr := func(t *testing.T, ns ipld.NodeStyle) schema.TypedNode { + nb := ns.NewBuilder() + ma, err := nb.BeginMap(5) + Require(t, err, ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("r1"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("a"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("r2"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("b"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("r3"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("c"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("r4"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("d"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f5"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("e"), ShouldEqual, nil) + Wish(t, ma.Finish(), ShouldEqual, nil) + return nb.Build().(schema.TypedNode) + } + testLookups_vvvvv := func(t *testing.T, n ipld.Node) { + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 5) + Wish(t, plzStr(n.LookupString("f1")), ShouldEqual, "a") + Wish(t, plzStr(n.LookupString("f2")), ShouldEqual, "b") + Wish(t, plzStr(n.LookupString("f3")), ShouldEqual, "c") + Wish(t, plzStr(n.LookupString("f4")), ShouldEqual, "d") + Wish(t, plzStr(n.LookupString("f5")), ShouldEqual, "e") + } + testLookups_vvvvv_repr := func(t *testing.T, n ipld.Node) { + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 5) + Wish(t, plzStr(n.LookupString("r1")), ShouldEqual, "a") + Wish(t, plzStr(n.LookupString("r2")), ShouldEqual, "b") + Wish(t, plzStr(n.LookupString("r3")), ShouldEqual, "c") + Wish(t, plzStr(n.LookupString("r4")), ShouldEqual, "d") + Wish(t, plzStr(n.LookupString("f5")), ShouldEqual, "e") + } + testIteration_vvvvv := func(t *testing.T, n ipld.Node) { + itr := n.MapIterator() + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ := itr.Next() + Wish(t, str(k), ShouldEqual, "f1") + Wish(t, str(v), ShouldEqual, "a") + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ = itr.Next() + Wish(t, str(k), ShouldEqual, "f2") + Wish(t, str(v), ShouldEqual, "b") + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ = itr.Next() + Wish(t, str(k), ShouldEqual, "f3") + Wish(t, str(v), ShouldEqual, "c") + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ = itr.Next() + Wish(t, str(k), ShouldEqual, "f4") + Wish(t, str(v), ShouldEqual, "d") + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ = itr.Next() + Wish(t, str(k), ShouldEqual, "f5") + Wish(t, str(v), ShouldEqual, "e") + Wish(t, itr.Done(), ShouldEqual, true) + k, v, err := itr.Next() + Wish(t, k, ShouldEqual, nil) + Wish(t, v, ShouldEqual, nil) + Wish(t, err, ShouldEqual, ipld.ErrIteratorOverread{}) + } + testIteration_vvvvv_repr := func(t *testing.T, n ipld.Node) { + itr := n.MapIterator() + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ := itr.Next() + Wish(t, str(k), ShouldEqual, "r1") + Wish(t, str(v), ShouldEqual, "a") + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ = itr.Next() + Wish(t, str(k), ShouldEqual, "r2") + Wish(t, str(v), ShouldEqual, "b") + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ = itr.Next() + Wish(t, str(k), ShouldEqual, "r3") + Wish(t, str(v), ShouldEqual, "c") + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ = itr.Next() + Wish(t, str(k), ShouldEqual, "r4") + Wish(t, str(v), ShouldEqual, "d") + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ = itr.Next() + Wish(t, str(k), ShouldEqual, "f5") + Wish(t, str(v), ShouldEqual, "e") + Wish(t, itr.Done(), ShouldEqual, true) + k, v, err := itr.Next() + Wish(t, k, ShouldEqual, nil) + Wish(t, v, ShouldEqual, nil) + Wish(t, err, ShouldEqual, ipld.ErrIteratorOverread{}) + } + build_vvzzv := func(t *testing.T, ns ipld.NodeStyle) schema.TypedNode { + nb := ns.NewBuilder() + ma, err := nb.BeginMap(5) + Require(t, err, ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f1"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("a"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f2"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("b"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f3"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignNull(), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f4"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignNull(), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f5"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("e"), ShouldEqual, nil) + Wish(t, ma.Finish(), ShouldEqual, nil) + return nb.Build().(schema.TypedNode) + } + build_vvzzv_repr := func(t *testing.T, ns ipld.NodeStyle) schema.TypedNode { + nb := ns.NewBuilder() + ma, err := nb.BeginMap(5) + Require(t, err, ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("r1"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("a"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("r2"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("b"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("r3"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignNull(), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("r4"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignNull(), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f5"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("e"), ShouldEqual, nil) + Wish(t, ma.Finish(), ShouldEqual, nil) + return nb.Build().(schema.TypedNode) + } + testLookups_vvzzv := func(t *testing.T, n ipld.Node) { + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 5) + Wish(t, plzStr(n.LookupString("f1")), ShouldEqual, "a") + Wish(t, plzStr(n.LookupString("f2")), ShouldEqual, "b") + Wish(t, erp(n.LookupString("f3")), ShouldEqual, ipld.Null) + Wish(t, erp(n.LookupString("f4")), ShouldEqual, ipld.Null) + Wish(t, plzStr(n.LookupString("f5")), ShouldEqual, "e") + } + build_vuvuv := func(t *testing.T, ns ipld.NodeStyle) schema.TypedNode { + nb := ns.NewBuilder() + ma, err := nb.BeginMap(3) + Require(t, err, ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f1"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("a"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f3"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("c"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f5"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("e"), ShouldEqual, nil) + Wish(t, ma.Finish(), ShouldEqual, nil) + return nb.Build().(schema.TypedNode) + } + testLookups_vuvuv := func(t *testing.T, n ipld.Node) { + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 5) + Wish(t, plzStr(n.LookupString("f1")), ShouldEqual, "a") + Wish(t, erp(n.LookupString("f2")), ShouldEqual, ipld.Undef) + Wish(t, plzStr(n.LookupString("f3")), ShouldEqual, "c") + Wish(t, erp(n.LookupString("f4")), ShouldEqual, ipld.Undef) + Wish(t, plzStr(n.LookupString("f5")), ShouldEqual, "e") + } + testIteration_vuvuv_repr := func(t *testing.T, n ipld.Node) { + itr := n.MapIterator() + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ := itr.Next() + Wish(t, str(k), ShouldEqual, "r1") + Wish(t, str(v), ShouldEqual, "a") + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ = itr.Next() + Wish(t, str(k), ShouldEqual, "r3") + Wish(t, str(v), ShouldEqual, "c") + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ = itr.Next() + Wish(t, str(k), ShouldEqual, "f5") + Wish(t, str(v), ShouldEqual, "e") + Wish(t, itr.Done(), ShouldEqual, true) + k, v, err := itr.Next() + Wish(t, k, ShouldEqual, nil) + Wish(t, v, ShouldEqual, nil) + Wish(t, err, ShouldEqual, ipld.ErrIteratorOverread{}) + } + build_vvzuu := func(t *testing.T, ns ipld.NodeStyle) schema.TypedNode { + nb := ns.NewBuilder() + ma, err := nb.BeginMap(3) + Require(t, err, ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f1"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("a"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f2"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignString("b"), ShouldEqual, nil) + Wish(t, ma.AssembleKey().AssignString("f3"), ShouldEqual, nil) + Wish(t, ma.AssembleValue().AssignNull(), ShouldEqual, nil) + Wish(t, ma.Finish(), ShouldEqual, nil) + return nb.Build().(schema.TypedNode) + } + testIteration_vvzuu_repr := func(t *testing.T, n ipld.Node) { + itr := n.MapIterator() + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ := itr.Next() + Wish(t, str(k), ShouldEqual, "r1") + Wish(t, str(v), ShouldEqual, "a") + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ = itr.Next() + Wish(t, str(k), ShouldEqual, "r2") + Wish(t, str(v), ShouldEqual, "b") + Wish(t, itr.Done(), ShouldEqual, false) + k, v, _ = itr.Next() + Wish(t, str(k), ShouldEqual, "r3") + Wish(t, v, ShouldEqual, ipld.Null) + Wish(t, itr.Done(), ShouldEqual, true) + k, v, err := itr.Next() + Wish(t, k, ShouldEqual, nil) + Wish(t, v, ShouldEqual, nil) + Wish(t, err, ShouldEqual, ipld.ErrIteratorOverread{}) + } + + // Okay, now the test actions: + test := func(t *testing.T, ns ipld.NodeStyle, nsr ipld.NodeStyle) { + t.Run("all fields set", func(t *testing.T) { + t.Run("typed-create", func(t *testing.T) { + n := build_vvvvv(t, ns) + t.Run("typed-read", func(t *testing.T) { + testLookups_vvvvv(t, n) + testIteration_vvvvv(t, n) + }) + t.Run("repr-read", func(t *testing.T) { + testLookups_vvvvv_repr(t, n.Representation()) + testIteration_vvvvv_repr(t, n.Representation()) + }) + }) + t.Run("repr-create", func(t *testing.T) { + Wish(t, build_vvvvv_repr(t, nsr), ShouldEqual, build_vvvvv(t, ns)) + }) + }) + t.Run("setting nulls", func(t *testing.T) { + t.Run("typed-create", func(t *testing.T) { + n := build_vvzzv(t, ns) + t.Run("typed-read", func(t *testing.T) { + testLookups_vvzzv(t, n) + }) + t.Run("repr-read", func(t *testing.T) { + // nyi + }) + }) + t.Run("repr-create", func(t *testing.T) { + Wish(t, build_vvzzv_repr(t, nsr), ShouldEqual, build_vvzzv(t, ns)) + }) + }) + t.Run("absent optionals", func(t *testing.T) { + t.Run("typed-create", func(t *testing.T) { + n := build_vuvuv(t, ns) + t.Run("typed-read", func(t *testing.T) { + testLookups_vuvuv(t, n) + }) + t.Run("repr-read", func(t *testing.T) { + testIteration_vuvuv_repr(t, n.Representation()) + }) + }) + t.Run("repr-create", func(t *testing.T) { + // nyi + }) + }) + t.Run("absent trailing optionals", func(t *testing.T) { + // Trailing optionals are especially touchy in a few details of iterators, so this gets an extra focused test. + t.Run("typed-create", func(t *testing.T) { + n := build_vvzuu(t, ns) + t.Run("typed-read", func(t *testing.T) { + // Not very interesting; still returns absent explicitly, same as 'vuvuv' scenario. + }) + t.Run("repr-read", func(t *testing.T) { + testIteration_vvzuu_repr(t, n.Representation()) + }) + }) + t.Run("repr-create", func(t *testing.T) { + // nyi + }) + }) + } + + // Do most of the type declarations. + ts := schema.TypeSystem{} + ts.Init() + adjCfg := &AdjunctCfg{ + maybeUsesPtr: map[schema.TypeName]bool{}, + } + ts.Accumulate(schema.SpawnString("String")) + ts.Accumulate(schema.SpawnStruct("Stroct", + []schema.StructField{ + // Every field in this struct (including their order) is exercising an interesting case... + schema.SpawnStructField("f1", ts.TypeByName("String"), false, false), // plain field. + schema.SpawnStructField("f2", ts.TypeByName("String"), true, false), // optional; later we have more than one optional field, nonsequentially. + schema.SpawnStructField("f3", ts.TypeByName("String"), false, true), // nullable; but required. + schema.SpawnStructField("f4", ts.TypeByName("String"), true, true), // optional and nullable; trailing optional. + schema.SpawnStructField("f5", ts.TypeByName("String"), true, false), // optional; and the second one in a row, trailing. + }, + schema.SpawnStructRepresentationMap(map[string]string{ + "f1": "r1", + "f2": "r2", + "f3": "r3", + "f4": "r4", + }), + )) + + // And finally, launch tests! ...while specializing the adjunct config a bit. + t.Run("maybe-using-embed", func(t *testing.T) { + adjCfg.maybeUsesPtr["String"] = false + + prefix := "stroct" + pkgName := "main" + genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) { + test(t, getStyleByName("Stroct"), getStyleByName("Stroct.Repr")) + }) + }) + t.Run("maybe-using-ptr", func(t *testing.T) { + adjCfg.maybeUsesPtr["String"] = true + + prefix := "stroct2" + pkgName := "main" + genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getStyleByName func(string) ipld.NodeStyle) { + test(t, getStyleByName("Stroct"), getStyleByName("Stroct.Repr")) + }) + }) +} diff --git a/schema/gen/go/testutil_test.go b/schema/gen/go/testutil_test.go new file mode 100644 index 00000000..2838318f --- /dev/null +++ b/schema/gen/go/testutil_test.go @@ -0,0 +1,46 @@ +package gengo + +import ( + ipld "github.com/ipld/go-ipld-prime" +) + +// This file is full of helper functions. Most are moderately embarassing. +// +// We should probably turn half of this into Wish Checkers; +// they'd probably be much less fragile and give better error messages that way. +// On the other hand, the functions for condensing two-arg returns wouldn't go away anyway. + +func plz(n ipld.Node, e error) ipld.Node { + if e != nil { + panic(e) + } + return n +} +func plzStr(n ipld.Node, e error) string { + if e != nil { + panic(e) + } + if s, ok := n.AsString(); ok == nil { + return s + } else { + panic(ok) + } +} +func str(n ipld.Node) string { + if s, ok := n.AsString(); ok == nil { + return s + } else { + panic(ok) + } +} +func erp(n ipld.Node, e error) interface{} { + if e != nil { + return e + } + return n +} + +// purely to syntactically flip large inline closures so we can see the argument at the top rather than the bottom of the block. +func withNode(n ipld.Node, cb func(n ipld.Node)) { + cb(n) +} diff --git a/schema/maybe.go b/schema/maybe.go index 670a6bcf..bdc46933 100644 --- a/schema/maybe.go +++ b/schema/maybe.go @@ -3,7 +3,7 @@ package schema type Maybe uint8 const ( - Maybe_Value = Maybe(0) + Maybe_Absent = Maybe(0) Maybe_Null = Maybe(1) - Maybe_Absent = Maybe(2) + Maybe_Value = Maybe(2) ) diff --git a/schema/tmpBuilders.go b/schema/tmpBuilders.go index ed12f112..db7a6cdf 100644 --- a/schema/tmpBuilders.go +++ b/schema/tmpBuilders.go @@ -28,17 +28,57 @@ func SpawnLink(name TypeName) TypeLink { 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} } +func SpawnMap(name TypeName, keyType Type, valueType Type, nullable bool) TypeMap { + return TypeMap{anyType{name, nil}, false, keyType, valueType, nullable} +} + func SpawnStruct(name TypeName, fields []StructField, repr StructRepresentation) TypeStruct { - fieldsMap := make(map[string]StructField, len(fields)) - for _, field := range fields { - fieldsMap[field.name] = field + v := TypeStruct{ + anyType{name, nil}, + fields, + make(map[string]StructField, len(fields)), + repr, + } + for i := range fields { + fields[i].parent = &v + v.fieldsMap[fields[i].name] = fields[i] } - return TypeStruct{anyType{name, nil}, fields, fieldsMap, repr} + switch repr.(type) { + case StructRepresentation_Stringjoin: + for _, f := range fields { + if f.IsMaybe() { + panic("neither nullable nor optional is supported on struct stringjoin representation") + } + } + } + return v } func SpawnStructField(name string, typ Type, optional bool, nullable bool) StructField { - return StructField{name, typ, optional, nullable} + return StructField{nil /*populated later*/, name, typ, optional, nullable} +} +func SpawnStructRepresentationMap(renames map[string]string) StructRepresentation_Map { + return StructRepresentation_Map{renames, nil} +} +func SpawnStructRepresentationStringjoin(delim string) StructRepresentation_Stringjoin { + return StructRepresentation_Stringjoin{delim} +} + +// The methods relating to TypeSystem are also mutation-heavy and placeholdery. + +func (ts *TypeSystem) Init() { + ts.namedTypes = make(map[TypeName]Type) +} +func (ts *TypeSystem) Accumulate(typ Type) { + ts.namedTypes[typ.Name()] = typ +} +func (ts TypeSystem) GetTypes() map[TypeName]Type { + return ts.namedTypes +} +func (ts TypeSystem) TypeByName(n string) Type { + return ts.namedTypes[TypeName(n)] } diff --git a/schema/type.go b/schema/type.go index dd6c622f..a9da9369 100644 --- a/schema/type.go +++ b/schema/type.go @@ -6,6 +6,8 @@ import ( type TypeName string // = ast.TypeName +func (tn TypeName) String() string { return string(tn) } + // typesystem.Type is an union interface; each of the `Type*` concrete types // in this package are one of its members. // @@ -161,6 +163,7 @@ type TypeStruct struct { representation StructRepresentation } type StructField struct { + parent *TypeStruct name string typ Type optional bool @@ -172,17 +175,28 @@ type StructRepresentation interface{ _StructRepresentation() } func (StructRepresentation_Map) _StructRepresentation() {} func (StructRepresentation_Tuple) _StructRepresentation() {} func (StructRepresentation_StringPairs) _StructRepresentation() {} -func (StructRepresentation_StringJoin) _StructRepresentation() {} +func (StructRepresentation_Stringjoin) _StructRepresentation() {} type StructRepresentation_Map struct { renames map[string]string - implicits map[string]interface{} + implicits map[string]ImplicitValue } type StructRepresentation_Tuple struct{} type StructRepresentation_StringPairs struct{ sep1, sep2 string } -type StructRepresentation_StringJoin struct{ sep string } +type StructRepresentation_Stringjoin struct{ sep string } type TypeEnum struct { anyType members []string } + +// ImplicitValue is an sum type holding values that are implicits. +// It's not an 'Any' value because it can't be recursive +// (or to be slightly more specific, it can be one of the recursive kinds, +// but if so, only its empty value is valid here). +type ImplicitValue interface{ _ImplicitValue() } + +type ImplicitValue_EmptyList struct{} +type ImplicitValue_EmptyMap struct{} +type ImplicitValue_String struct{ x string } +type ImplicitValue_Int struct{ x int } diff --git a/schema/typeMethods.go b/schema/typeMethods.go index 0e7d23fb..3d53f006 100644 --- a/schema/typeMethods.go +++ b/schema/typeMethods.go @@ -97,6 +97,17 @@ func (t TypeStruct) Field(name string) *StructField { return nil } +// Parent returns the type information that this field describes a part of. +// +// While in many cases, you may know the parent already from context, +// there may still be situations where want to pass around a field and +// not need to continue passing down the parent type with it; this method +// helps your code be less redundant in such a situation. +// (You'll find this useful for looking up any rename directives, for example, +// when holding onto a field, since that requires looking up information from +// the representation strategy, which is a property of the type as a whole.) +func (f StructField) Parent() *TypeStruct { return f.parent } + // Name returns the string name of this field. The name is the string that // will be used as a map key if the structure this field is a member of is // serialized as a map representation. @@ -124,6 +135,12 @@ func (f StructField) IsOptional() bool { return f.optional } // or either, or neither. func (f StructField) IsNullable() bool { return f.nullable } +// IsMaybe returns true if the field value is allowed to be either null or absent. +// +// This is a simple "or" of the two properties, +// but this method is a shorthand that turns out useful often. +func (f StructField) IsMaybe() bool { return f.nullable || f.optional } + func (t TypeStruct) RepresentationStrategy() StructRepresentation { return t.representation } @@ -135,6 +152,10 @@ func (r StructRepresentation_Map) GetFieldKey(field StructField) string { return field.name } +func (r StructRepresentation_Stringjoin) GetDelim() string { + return r.sep +} + // Members returns a slice the strings which are valid inhabitants of this enum. func (t TypeEnum) Members() []string { a := make([]string, len(t.members))