From 4eb8c55cd68966319f99d3887fe0033358c76cfa Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Mon, 2 Mar 2020 21:35:10 +0100 Subject: [PATCH 01/11] Promote NodeAssembler/NodeStyle solution to core. This is a *lot* of changes. It's the most significant change to date, both in semantics and in character count, since the start of this repo. It changes the most central interfaces, and significantly so. But all tests pass. And all benchmarks are *improved*. The Node interface (the reading side) is mostly unchanged -- a lot of consuming code will still compile and work just fine without changes -- but any other Node implementations out there might need some updating. The NodeBuilder interface (the writing side) is *extremely* changed -- any implementations out there will *definitely* need change -- and most consumers will too. It's unavoidable with a semantic fix this big. The performance improvements should make it worth your while, though. If you want more background on how and why we got here, you've got quite a few commits on the "research-admissions" branches to catch up on reading. But here's a rundown of the changes: (Get a glass of water or something calming before reading...) === NodeAssembler introduced! === NodeAssembler is a new interface that describes most of the work of creating and filling data into a new Node. The NodeBuilder interface is still around, but changed in role. A NodeBuilder is now always also a NodeAssembler; additionally, it can return the final Node to you. A NodeAssembler, unlike NodeBuilder, can **not** return a Node to you. In this way, a NodeBuilder represents the ability to allocate memory. A NodeAssembler often *does not*: it's just *filling in* memory. This design overall is much more friendly to efficient operations: in this model, we do allocations in bulk when a NodeBuilder is used, and then NodeAssemblers are used thereafter to fill it in -- this mental model is very friendly to amortizing memory allocations. Previously, the NodeBuilder interface made such a pattern of use somewhere between difficult and outright impossible, because it was modeled around building small values, then creating a bigger value and inserting the smaller ones into it. This is the key change that cascaded into producing the entire other set of changes which land in this commit. The NodeBuilder methods for getting "child builders" are also gone as a result of these changes. The result feels a lot smoother. (You can still ask for the NodeStyle for children of a recursive kind! But you'll find that even though it's possible, it's rarely necessary.) We see some direct improvements from this interface change already. We'll see even more in the future: creating values when using codegen'd implementations of Node was hugely encumbered by the old NodeBuilder model; NodeAssembler *radically* raises the possible ceiling for performance of codegen Node implementations. === NodeStyle introduced === NodeStyle is a new interface type that is used to carry information about concrete node implementations. You can always use a NodeStyle to get a NodeBuilder. NodeStyle may also have additional features on it which can be detected by interface checks. (This isn't heavily used yet, but we imagine it might become handy in the future.) NodeStyle replaces NodeBuilder in many function arguments, because often what we wanted was to communicate a selection of Node implementation strategy, but not actually the start of construction; the NodeStyle interface now allows us to *say that*. NodeStyle typically cost nothing to pass around, whereas a NodeBuilder generally requires an allocation to create and initialize. This means we can use NodeStyle more freely in many contexts. === node package paths changed === Node implementations are now in packages under the "node/*" directory. Previously, they were under an "impl/*" directory. The "impl/free" package is replaced by the the "node/basic" package! The package name was "ipldfree"; it's now "basicnode". === basicnode is an improved runtime/anycontent Node implementation === The `basicnode` package works much the same as the `ipldfree` package used to -- you can store any kind of data in it, and it just does as best it can to represent and handle that, and it works without any kind of type info nor needs of compile-time special support, etc -- while being just quietly *better at it*. The resident memory size of most things has gone down. (We're not using "fat unions" in the implementation anymore.) The cost of iterating maps has gone down *dramatically*. Iteration previously suffered from O(n) allocations due to expensive `runtime.conv*` calls when yielding keys. Iteration is now O(1) (!!) because we redesigned `basicnode` internals to use "internal pointers" more heavily, and this avoids the costs from `runtime.conv*`. (We could've done this separately from the NodeAssembler change, admittedly. But both are the product of research into how impactful clever use of "internal pointers" can be, and lots of code in the neighborhood had to be rewritten for the NodeAssembler interface, so, these diffs arrive as one.) Error messages are more informative. Many small operations should get a few nanoseconds faster. (The implementation uses more concrete types and fewer switch statements. The difference probably isn't the most noticeable part of all these changes, but it's there.) --- basicnode constructor helpers do all return pointers --- All the "New*" helper functions in the basicnode package return interfaces which are filled by a pointer now. This is change from how they worked previously when they were first implemented in the "rsrch" package. The experience of integrating basicnode with the tests in the traversal package made it clear that having a mixture of pointer and non-pointer values flying around will be irritating in practice. And since it is the case that when returning values from inside a larger structure, we *must* end up returning a pointer, pointers are thus what we standardize on. (There was even some writeup in the HACKME file about how we *might* encounter issues on this, and need to change to pointers-everywhere -- the "pointer-vs-value inhabitant consistency" heading. Yep: we did. And since this detail is now resolved, that doc section is dropped.) This doesn't really make any difference to performance. The old way would cause an alloc in those method via 'conv*' methods; the new way just makes it more explicit and go through a different runtime method at the bottom, but it's still the same number of allocations for essentially the same reasons. (I do wonder if at some future point, the golang compiler might get cleverer about eliding 'conv*' calls, and then this change we make here might be unfortunate; but that's certainly not true today, nor in the future at any proximity that I can foresee.) === iterator getters return nil for wrong-kind === The Node.MapIterator and Node.ListIterator methods now return nil if you call them on non-maps or non-lists. Previously, they would return an iterator, but using it would just constantly error. I don't think anyone was honestly really checking those error thunks, and they made a lot of boilerplate white noise in the implementations, and the error is still entirely avoidable by checking the node kind up-front (and this is strictly preferable anyway, since it's faster than getting an error thunk, poking it to get the error, etc)... so, in total, there seem like very few reasons these were useful: the idea is thus dropped. Docs in the Node interface reflect this. === node/mixins makes new Node implementations easier === The mixins package isn't usable directly, but if you're going to make a new Node implementation, it should save you a lot of typing... and also, boost consistency of basic error handling. Codegen will look forward to using this. (Codegen already had much of these semantics internally, and so this package is sort of lifting that back out to be more generally usable. By making it live out here as exported symbols in the core library, we should also reduce the sheer character count of codegen output.) === 'typed.Node' is now 'schema.TypedNode' === A bunch of interfaces that were under the "impl/typed" path moved to be in the "schema" package instead. This probably makes sense to you if you look at them and needs no further explanation. (The reason it comes in this diff, though, is that it was forced: adding better tests to the traversal package highlighted a bunch of cyclic dependency issues that came from 'typed.Node' being in a package that had concrete use of 'basicnode'.) === codecs === The 'encoding' package is now named 'codec'. This name is shorter; it's more in line with vocabulary we use elsewhere in the IPLD project (whereas 'encoding' was more of a nod to the naming found in the golang standard library); and in my personal opinion it does better at describing the both directions of the process (whereas 'encoding' sounds like only the to-linear-bytes direction). I just like it better. === unmarshal functions no longer return node === Unmarshal functions accept an NodeAssembler parameter (rather than a NodeBuilder, as before, nor a NodeStyle, which might also make sense in the new family of interfaces). This means they no longer need to return a Node, either -- the caller can decide where the unmarshalled data lands. If the caller is using a NodeBuilder, it means they can call Build on that to get the value. (If it's a codegen NodeBuilder with More Information, the caller can use any specialized functions to get the more informative pointers without need for casting!) Broadly speaking, this means users of unmarshal functions have more control over how memory allocation comes into play. We may want to add more helper functions to the various codec packages which take a NodeStyle argument and do return a Node. That's not in this diff, though. (Need to decide what pattern of naming these various APIs would deserve, among other things.) === the fluent package === The fluent package changed significantly. The readonly/Node side of it is dropped. It didn't seem to get a ton of exercise in practice; the 'traversal' package (and in the future, perhaps also a 'cursor' package) addresses a lot of the same needs, and what remains is also covered well these days by the 'must' package; and the performance cost of fluent node wrappers as well as the composability obstruction of them... is just too much to be worth it. The few things that used fluent.Node for reading data now mostly use the 'must' package instead (and look better for it, imo). It's possible that some sort of fluent.Node will be rebuilt someday, but it's not entirely clear to me what it should look like, and indeed whether or not it's a good idea to have in the repo at all if the performance of it is counterindicated in a majority of situations... so, it's not part of today's update. The writing/NodeBuilder/NodeAssembler fluent wrappers are continued. It's similar to before (panics promptly on errors, and has a lot of closures)... but also reflects all of the changes made in the migration towards NodeAssembler: it doesn't return intermediate nodes, and there's much less kerfuffle with getting child builders. Overall, the fluent builders are now even more streamlined than before; the closures need even fewer parameters; great success! The fluent.NodeAssembler interface retains the "Create" terminology around maps and lists, even though in the core interfaces, the ipld.NodeAssembler interface now says "Begin" for maps and lists. This is because the fluent.NodeAssembler approach, with its use of closures, really does do the whole operation in one swoop. (It's amusing to note that this change includes finally nuking some fairly old "REVIEW" comment blocks from the old fluent package which regarded the "knb" value and other such sadness around typed recursion. Indeed, we've finally reviewed that: and the answer was indeed to do something drastically different to make those recursions dance well.) === selectors === Selectors essentially didn't change as part of this diff. Neat. (They should get a lot faster when applied, because our node implementations hit a lot less interface boxing in common operations! But the selector code itself didn't need to change to get the gains.) The 'selector/builder' helper package *did* change a bit. The changes are mostly invisible to the user. I do have some questions about the performance of the result; I've got a sneaking suspicion there's now a bunch of improvements that might be easier to get to now than they would've been previously. But, this is not my quest today. Perhaps it will deserve some review in the future. The 'selector/builder' package should be noted as having some interesting error handling strategies. Namely, it doesn't. Any panics raised by the fluent package will just keep rising; there's no place where they're converted to regular error value returns. I'm not sure this is a good interface, but it's the way it was before I started passing through, so that's the way it stays after this patch. ExploreFieldsSpecBuilder.Delete disappears. I hope no one misses it. I don't think anyone will. I suspect it was there only because the ipld.MapBuilder interface had such a method and it seemed like a reasonable conservative choice at the time to proxy it; now that the method proxied is gone, though, so too shall go this. === traversal === Traversal is mostly the same, but a few pieces of config have new names. `traversal.Config.LinkNodeBuilderChooser` is now `traversal.Config.LinkTargetNodeStyleChooser`. Still a mouthful; slightly more accurate; and reflects that it now works in terms of NodeStyle, which gives us a little more finesse in reasoning about where NodeBuilders are actually created, and thus better control and insight into where allocations happen. `traversal.NodeBuilderChooser` is now `traversal.LinkTargetNodeStyleChooser` for the same reasons. The actual type of the `LinkTargetNodeStyleChooser` now requires returning a `NodeStyle`, in case all the naming hasn't made it obvious. === disappearing node packages === A couple of packages under 'impl/*' are just dropped. This is no real loss. The packages dropped were Node implementations that simply weren't done. Deleting them is an increase in honesty. This doesn't mean something with the same intentions as those packages won't come back; it's just not today. --- runtime typed node wrapper disappeared --- This one will come back. It was just too much of a pain to carry along in this diff. Since it was also a fairly unfinished proof-of-concept with no downstream users, it's easier to drop and later reincarnate it than it is to carry it along now. === linking === Link.Load now takes a `NodeAssembler` parameter instead of a `NodeBuilder`, and no longer returns a `Node`! This should result in callers having a little more control over where allocations may occur, letting them potentially reuse builders, etc. This change should also make sense considering how codec.Unmarshal now similarly takes a NodeAssembler argument and does not return a Node value since its understood that the caller has some way to access or gather the effects, and it's none of our business. Something about the Link interface still feels a bit contorted. Having to give the Load method a Loader that takes half the same arguments all over again is definitely odd. And it's tempting to take a peek at fixing this, since the method is getting a signature change. It's unclear what exactly to do about this, though, and probably a consequential design decision space... so it shall not be reopened today during this other large refactor. Maybe soon. Maybe. === the dag-json codec === The dag-json codec got harder to implement. Rrgh. Since we can't tell if something is going to become a Link until *several tokens in*, dag-json is always a bit annoying to deal with. Previously, however, dag-json could still start optimistically building a map node, and then just... quietly drop it if we turn out to be dealing with a link instead. *That's no longer possible*: the process of using NodeAssembler doesn't have a general purpose mechanism for backtracking. So. Now the dag-json codec has to do even more custom work to buffer tokens until it knows what to do with them. Yey. The upside is: of course, the result is actually faster, and does fewer memory allocations, since it gathers enough information to decide what it's doing before it begins to do it. (This is a lovely example of the disciplined design of NodeAssembler's interface forcing other code to be better behaved and disciplined!) === traversal is faster === The `BenchmarkSpec_Walk_MapNStrMap3StrInt/n=32` test has about doubled in speed on the new `basicnode` implementation in comparison to the old `ipldfree.Node` implementation. This is derived primarily from the drop in costs of iteration on `basicnode` compared to the old `ipldfree.Node` implementation. Some back-of-the-envelope math on the allocation still left around suggest it could double in speed again. The next thing to target would be allocations of paths, followed by iterator allocations. Both are a tad trickier, though (see a recently merge-ignore'd commit for notes on iterators; and paths... paths will be a doozy because the path forward almost certainly involves path values becoming invalid if retained beyond a scope, which is... unsafe), so certainly need their own efforts and separate commits. === marshalling is faster === Marshalling is much faster on the new `basicnode` implementation in comparison to the old `ipldfree.Node` implementation. Same reasons as traversal. Some fixes to marshalling which previously caused unnecessary allocations of token objects during recursions have also been made. These improve speed a bit (though it's not nearly as noticeable as the boost provided by the Node implementation improvements to iteration). === size hints showed up all over the place === The appearance of size hint arguments to assembly of maps and lists is of course inevitable from the new NodeAssembler interface. It's particularly interesting to see how many of them showed up in the selector and selectorbuilder packages as constants. And super especially interesting how many of them are very small constants. 44 zeros. 86 ones. 25 twos. 9 threes. 2 fours. (Counted via variations of `grep -r 'Map(.*4, func' | wc -l`.) It's quite a distribution, neh? We should probably consider some more optimizations specifically targeted to small maps. (This is an unscientific sample, and shifted by what we chose to focus on in testing, etc etc, but the general point stands.) `-1` is used to indicate "no idea" for size. There's a small fix to the basicnode implementations to allow this. A zero would work just as well in practice, but using a negative number as a hint to the human seems potentially useful. It's a shame we can't make the argument optional; oh well. === codegen === The codegen packages still all compile... but do nonsensical things, for the moment: they've not been updated to emit NodeAssembler. Since the output of codegen still isn't well rigged to test harnesses, this breakage is silent. The codegen packages will probably undergo a fairly tabula-rasa sweep in the near future. There's been a lot of lessons learned since the start of the code currently there. Updating to emit the NodeAssembler interface will be such a large endeavor it probably represents a good point to just do a fresh pass on the whole thing all at once. -------- ... and that's all! Fun reading, eh? Please do forgive the refactors necessary for all this. Truly, the performance improvements should make it all worth your while. --- _rsrch/nodesolution/HACKME.md => HACKME.md | 0 ...Behaviors.md => HACKME_builderBehaviors.md | 0 README.md | 9 +- _rsrch/nodesolution/doc.go | 10 - _rsrch/nodesolution/errors.go | 80 ----- _rsrch/nodesolution/kind.go | 1 - _rsrch/nodesolution/link.go | 4 - _rsrch/nodesolution/node.go | 274 ------------------ _rsrch/nodesolution/node/mixins/tests/util.go | 22 -- _rsrch/nodesolution/nodeBuilder.go | 129 --------- _rsrch/nodesolution/path.go | 1 - _rsrch/nodesolution/pathSegment.go | 1 - bench/benchMarshalling_test.go | 44 --- {encoding => codec}/dagcbor/common.go | 0 {encoding => codec}/dagcbor/marshal.go | 33 ++- {encoding => codec}/dagcbor/multicodec.go | 10 +- .../dagcbor/roundtripCidlink_test.go | 13 +- codec/dagcbor/roundtrip_test.go | 64 ++++ {encoding => codec}/dagcbor/unmarshal.go | 107 ++++--- {encoding => codec}/dagjson/marshal.go | 0 {encoding => codec}/dagjson/multicodec.go | 14 +- codec/dagjson/roundtripCidlink_test.go | 74 +++++ codec/dagjson/roundtrip_test.go | 80 +++++ codec/dagjson/unmarshal.go | 219 ++++++++++++++ .../nodesolution/codec => codec}/marshal.go | 4 +- .../nodesolution/codec => codec}/unmarshal.go | 6 +- doc.go | 24 +- encoding/dagcbor/roundtrip_test.go | 61 ---- encoding/dagjson/roundtripCidlink_test.go | 38 --- encoding/dagjson/roundtrip_test.go | 77 ----- encoding/dagjson/unmarshal.go | 147 ---------- encoding/marshal.go | 132 --------- encoding/unmarshal.go | 146 ---------- errors.go | 26 +- fluent/fluentBuilder.go | 193 ++++++++++++ fluent/fluentBuilder_test.go | 73 +++++ fluent/fluentNode.go | 223 -------------- fluent/fluentNodeBuilder.go | 220 -------------- fluent/fluentRecover.go | 8 + fluent/fluentRecover_test.go | 26 +- fluent/fluentUtilities.go | 13 - impl/bind/boundNode.go | 256 ---------------- impl/bind/boundNodeTokenizing.go | 9 - impl/bind/boundNode_test.go | 25 -- impl/cbor/cborNode.go | 16 - impl/free/bench_test.go | 124 -------- impl/free/freeNode.go | 242 ---------------- impl/free/freeNodeBuilder.go | 176 ----------- impl/free/freeNode_test.go | 19 -- impl/free/justString.go | 106 ------- impl/free/justString_test.go | 45 --- impl/typed/wrapStruct.go | 224 -------------- impl/util/iteratorThunks.go | 21 -- linking.go | 10 +- linking/cid/cidLink.go | 18 +- linking/cid/multicodec.go | 8 +- must/must.go | 22 ++ node.go | 98 ++++--- .../node => node}/basic/HACKME.md | 14 - .../nodesolution/node => node}/basic/any.go | 2 +- .../node => node}/basic/any_test.go | 2 +- node/basic/bench_test.go | 15 + .../nodesolution/node => node}/basic/bool.go | 7 +- .../nodesolution/node => node}/basic/bytes.go | 7 +- .../nodesolution/node => node}/basic/float.go | 7 +- .../nodesolution/node => node}/basic/int.go | 7 +- .../nodesolution/node => node}/basic/link.go | 4 +- .../nodesolution/node => node}/basic/list.go | 7 +- .../nodesolution/node => node}/basic/map.go | 7 +- .../node => node}/basic/map_test.go | 2 +- .../node => node}/basic/string.go | 7 +- .../node => node}/basic/string_test.go | 2 +- {_rsrch/nodesolution/node => node}/doc.go | 0 .../node => node}/gendemo/HACKME.md | 0 .../node => node}/gendemo/HACKME_abbrevs.md | 0 .../node => node}/gendemo/HACKME_scalars.md | 0 .../node => node}/gendemo/HACKME_tradeoffs.md | 0 .../nodesolution/node => node}/gendemo/int.go | 4 +- .../node => node}/gendemo/map_K2_T2.go | 2 +- .../node => node}/gendemo/map_K_T.go | 2 +- .../node => node}/gendemo/map_K_T_test.go | 2 +- .../node => node}/gendemo/string.go | 4 +- .../node => node}/mixins/HACKME.md | 0 .../node => node}/mixins/boolMixin.go | 2 +- .../node => node}/mixins/bytesMixin.go | 2 +- .../node => node}/mixins/floatMixin.go | 2 +- .../node => node}/mixins/intMixin.go | 2 +- .../node => node}/mixins/linkMixin.go | 2 +- .../node => node}/mixins/listMixin.go | 2 +- .../node => node}/mixins/mapMixin.go | 2 +- .../node => node}/mixins/stringMixin.go | 2 +- .../node => node}/mixins/tmplMixin.txt | 2 +- {tests => node/tests}/HACKME.md | 0 {tests => node/tests}/corpus/corpus.go | 0 {tests => node/tests}/corpus/corpus_test.go | 0 {tests => node/tests}/corpus/util.go | 0 .../mixins => node}/tests/mapBenchmarks.go | 2 +- .../tests/mapBenchmarks_test.go | 0 .../node/mixins => node}/tests/mapFixtures.go | 2 +- .../node/mixins => node}/tests/mapSpecs.go | 2 +- .../tests/marshalBenchmarks.go | 8 +- .../node/mixins => node}/tests/stringSpecs.go | 2 +- {tests => node/tests}/traversalBenchmarks.go | 14 +- .../tests/unmarshalBenchmarks.go | 6 +- {tests => node/tests}/util.go | 19 +- nodeBuilder.go | 260 ++++++++--------- schema/tests/testsForStructs.go | 30 +- schema/typedNode.go | 8 +- tests/building.go | 90 ------ tests/marshalBenchmarks.go | 68 ----- tests/marshalling.go | 102 ------- tests/unmarshalBenchmarks.go | 68 ----- tests/unmarshalling.go | 131 --------- thunks.go | 76 ----- traversal/common.go | 8 +- traversal/fns.go | 18 +- traversal/focus.go | 7 +- traversal/focus_test.go | 62 ++-- traversal/selector/builder/builder.go | 94 +++--- traversal/selector/builder/builder_test.go | 153 +++++----- traversal/selector/exploreAll_test.go | 22 +- traversal/selector/exploreFields_test.go | 34 +-- traversal/selector/exploreIndex_test.go | 64 ++-- traversal/selector/exploreRange_test.go | 108 ++++--- traversal/selector/exploreRecursive_test.go | 178 ++++++------ traversal/selector/exploreUnion_test.go | 53 ++-- traversal/walk.go | 7 +- traversal/walk_test.go | 102 +++---- _rsrch/nodesolution/unit.go => unit.go | 0 129 files changed, 1640 insertions(+), 4305 deletions(-) rename _rsrch/nodesolution/HACKME.md => HACKME.md (100%) rename _rsrch/nodesolution/HACKME_builderBehaviors.md => HACKME_builderBehaviors.md (100%) delete mode 100644 _rsrch/nodesolution/doc.go delete mode 100644 _rsrch/nodesolution/errors.go delete mode 120000 _rsrch/nodesolution/kind.go delete mode 100644 _rsrch/nodesolution/link.go delete mode 100644 _rsrch/nodesolution/node.go delete mode 100644 _rsrch/nodesolution/node/mixins/tests/util.go delete mode 100644 _rsrch/nodesolution/nodeBuilder.go delete mode 120000 _rsrch/nodesolution/path.go delete mode 120000 _rsrch/nodesolution/pathSegment.go delete mode 100644 bench/benchMarshalling_test.go rename {encoding => codec}/dagcbor/common.go (100%) rename {encoding => codec}/dagcbor/marshal.go (82%) rename {encoding => codec}/dagcbor/multicodec.go (79%) rename {encoding => codec}/dagcbor/roundtripCidlink_test.go (72%) create mode 100644 codec/dagcbor/roundtrip_test.go rename {encoding => codec}/dagcbor/unmarshal.go (50%) rename {encoding => codec}/dagjson/marshal.go (100%) rename {encoding => codec}/dagjson/multicodec.go (85%) create mode 100644 codec/dagjson/roundtripCidlink_test.go create mode 100644 codec/dagjson/roundtrip_test.go create mode 100644 codec/dagjson/unmarshal.go rename {_rsrch/nodesolution/codec => codec}/marshal.go (93%) rename {_rsrch/nodesolution/codec => codec}/unmarshal.go (92%) delete mode 100644 encoding/dagcbor/roundtrip_test.go delete mode 100644 encoding/dagjson/roundtripCidlink_test.go delete mode 100644 encoding/dagjson/roundtrip_test.go delete mode 100644 encoding/dagjson/unmarshal.go delete mode 100644 encoding/marshal.go delete mode 100644 encoding/unmarshal.go create mode 100644 fluent/fluentBuilder.go create mode 100644 fluent/fluentBuilder_test.go delete mode 100644 fluent/fluentNode.go delete mode 100644 fluent/fluentNodeBuilder.go delete mode 100644 fluent/fluentUtilities.go delete mode 100644 impl/bind/boundNode.go delete mode 100644 impl/bind/boundNodeTokenizing.go delete mode 100644 impl/bind/boundNode_test.go delete mode 100644 impl/cbor/cborNode.go delete mode 100644 impl/free/bench_test.go delete mode 100644 impl/free/freeNode.go delete mode 100644 impl/free/freeNodeBuilder.go delete mode 100644 impl/free/freeNode_test.go delete mode 100644 impl/free/justString.go delete mode 100644 impl/free/justString_test.go delete mode 100644 impl/typed/wrapStruct.go delete mode 100644 impl/util/iteratorThunks.go rename {_rsrch/nodesolution/node => node}/basic/HACKME.md (88%) rename {_rsrch/nodesolution/node => node}/basic/any.go (98%) rename {_rsrch/nodesolution/node => node}/basic/any_test.go (81%) create mode 100644 node/basic/bench_test.go rename {_rsrch/nodesolution/node => node}/basic/bool.go (95%) rename {_rsrch/nodesolution/node => node}/basic/bytes.go (96%) rename {_rsrch/nodesolution/node => node}/basic/float.go (96%) rename {_rsrch/nodesolution/node => node}/basic/int.go (95%) rename {_rsrch/nodesolution/node => node}/basic/link.go (96%) rename {_rsrch/nodesolution/node => node}/basic/list.go (98%) rename {_rsrch/nodesolution/node => node}/basic/map.go (99%) rename {_rsrch/nodesolution/node => node}/basic/map_test.go (95%) rename {_rsrch/nodesolution/node => node}/basic/string.go (96%) rename {_rsrch/nodesolution/node => node}/basic/string_test.go (62%) rename {_rsrch/nodesolution/node => node}/doc.go (100%) rename {_rsrch/nodesolution/node => node}/gendemo/HACKME.md (100%) rename {_rsrch/nodesolution/node => node}/gendemo/HACKME_abbrevs.md (100%) rename {_rsrch/nodesolution/node => node}/gendemo/HACKME_scalars.md (100%) rename {_rsrch/nodesolution/node => node}/gendemo/HACKME_tradeoffs.md (100%) rename {_rsrch/nodesolution/node => node}/gendemo/int.go (96%) rename {_rsrch/nodesolution/node => node}/gendemo/map_K2_T2.go (99%) rename {_rsrch/nodesolution/node => node}/gendemo/map_K_T.go (99%) rename {_rsrch/nodesolution/node => node}/gendemo/map_K_T_test.go (92%) rename {_rsrch/nodesolution/node => node}/gendemo/string.go (97%) rename {_rsrch/nodesolution/node => node}/mixins/HACKME.md (100%) rename {_rsrch/nodesolution/node => node}/mixins/boolMixin.go (98%) rename {_rsrch/nodesolution/node => node}/mixins/bytesMixin.go (98%) rename {_rsrch/nodesolution/node => node}/mixins/floatMixin.go (98%) rename {_rsrch/nodesolution/node => node}/mixins/intMixin.go (98%) rename {_rsrch/nodesolution/node => node}/mixins/linkMixin.go (98%) rename {_rsrch/nodesolution/node => node}/mixins/listMixin.go (98%) rename {_rsrch/nodesolution/node => node}/mixins/mapMixin.go (98%) rename {_rsrch/nodesolution/node => node}/mixins/stringMixin.go (98%) rename {_rsrch/nodesolution/node => node}/mixins/tmplMixin.txt (98%) rename {tests => node/tests}/HACKME.md (100%) rename {tests => node/tests}/corpus/corpus.go (100%) rename {tests => node/tests}/corpus/corpus_test.go (100%) rename {tests => node/tests}/corpus/util.go (100%) rename {_rsrch/nodesolution/node/mixins => node}/tests/mapBenchmarks.go (97%) rename {_rsrch/nodesolution/node/mixins => node}/tests/mapBenchmarks_test.go (100%) rename {_rsrch/nodesolution/node/mixins => node}/tests/mapFixtures.go (95%) rename {_rsrch/nodesolution/node/mixins => node}/tests/mapSpecs.go (99%) rename {_rsrch/nodesolution/node/mixins => node}/tests/marshalBenchmarks.go (91%) rename {_rsrch/nodesolution/node/mixins => node}/tests/stringSpecs.go (89%) rename {tests => node/tests}/traversalBenchmarks.go (74%) rename {_rsrch/nodesolution/node/mixins => node}/tests/unmarshalBenchmarks.go (91%) rename {tests => node/tests}/util.go (52%) delete mode 100644 tests/building.go delete mode 100644 tests/marshalBenchmarks.go delete mode 100644 tests/marshalling.go delete mode 100644 tests/unmarshalBenchmarks.go delete mode 100644 tests/unmarshalling.go delete mode 100644 thunks.go rename _rsrch/nodesolution/unit.go => unit.go (100%) diff --git a/_rsrch/nodesolution/HACKME.md b/HACKME.md similarity index 100% rename from _rsrch/nodesolution/HACKME.md rename to HACKME.md diff --git a/_rsrch/nodesolution/HACKME_builderBehaviors.md b/HACKME_builderBehaviors.md similarity index 100% rename from _rsrch/nodesolution/HACKME_builderBehaviors.md rename to HACKME_builderBehaviors.md diff --git a/README.md b/README.md index ffb3732b..22e5ca7e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ The most central interfaces are the base package, but you'll certainly need to import additional packages to get concrete implementations into action. Roughly speaking, the core package interfaces are all about the IPLD Data Model; -the encoding packages contain some codecs, and turn serial data into Data Model; +the codec packages contain functions for parsing serial data into the IPLD Data Model, +and converting Data Model content back into serial formats; the traversal package is an example of higher-order functions on the Data Model; concrete 'Node' implementations ready to use can be found under 'impl/*'; and several additional packages contain advanced features such as IPLD Schemas. @@ -31,9 +32,9 @@ or new codecs, or new higher-order order functions!) - `github.com/ipld/go-ipld-prime/impl/cbor` -- imported as `ipldcbor` -- provides concrete implementations of `Node` and `NodeBuilder` which have some special features to accelerate certain workloads with CBOR. - `github.com/ipld/go-ipld-prime/traversal` -- contains higher-order functions for traversing graphs of data easily. - `github.com/ipld/go-ipld-prime/traversal/selector` -- contains selectors, which are sort of like regexps, but for trees and graphs of IPLD data! -- `github.com/ipld/go-ipld-prime/encoding` -- parent package of all the codec implementations! -- `github.com/ipld/go-ipld-prime/encoding/dagcbor` -- implementations of marshalling and unmarshalling as CBOR (a fast, binary serialization format). -- `github.com/ipld/go-ipld-prime/encoding/dagjson` -- implementations of marshalling and unmarshalling as JSON (a popular human readable format). +- `github.com/ipld/go-ipld-prime/codec -- parent package of all the codec implementations! +- `github.com/ipld/go-ipld-prime/codec/dagcbor` -- implementations of marshalling and unmarshalling as CBOR (a fast, binary serialization format). +- `github.com/ipld/go-ipld-prime/codec/dagjson` -- implementations of marshalling and unmarshalling as JSON (a popular human readable format). - `github.com/ipld/go-ipld-prime/linking/cid` -- imported as `cidlink` -- provides concrete implementations of `Link` as a CID. Also, the multicodec registry. - `github.com/ipld/go-ipld-prime/schema` -- contains the `schema.Type` and `schema.TypedNode` interface declarations, which represent IPLD Schema type information. - `github.com/ipld/go-ipld-prime/impl/typed` -- provides concrete implementations of `schema.TypedNode` which decorate a basic `Node` at runtime to have additional features described by IPLD Schemas. diff --git a/_rsrch/nodesolution/doc.go b/_rsrch/nodesolution/doc.go deleted file mode 100644 index 87a46128..00000000 --- a/_rsrch/nodesolution/doc.go +++ /dev/null @@ -1,10 +0,0 @@ -package ipld - -/* - This package accumulates some of the lessons learned from - the 'style' experiment, the 'assembler' experiment, and a few others, - while taking a strong stance on immediately returning errors. - - These choices don't maximize ergonomics, but they should maximize - correctness and performance. Ergonomics can come on top. -*/ diff --git a/_rsrch/nodesolution/errors.go b/_rsrch/nodesolution/errors.go deleted file mode 100644 index a392c6dc..00000000 --- a/_rsrch/nodesolution/errors.go +++ /dev/null @@ -1,80 +0,0 @@ -package ipld - -import "fmt" - -// ErrWrongKind may be returned from functions on the Node interface when -// a method is invoked which doesn't make sense for the Kind and/or ReprKind -// that node concretely contains. -// -// For example, calling AsString on a map will return ErrWrongKind. -// Calling Lookup on an int will similarly return ErrWrongKind. -// -// REVIEW: would it improve clarity to call this 'ErrWrongKindForNodeStyle'? -type ErrWrongKind struct { - // TypeName may optionally indicate the named type of a node the function - // was called on (if the node was typed!), or, may be the empty string. - TypeName string - - // MethodName is literally the string for the operation attempted, e.g. - // "AsString". - // - // For methods on nodebuilders, we say e.g. "NodeBuilder.CreateMap". - MethodName string - - // ApprorpriateKind describes which ReprKinds the erroring method would - // make sense for. - AppropriateKind ReprKindSet - - // ActualKind describes the ReprKind of the node the method was called on. - // - // In the case of typed nodes, this will typically refer to the 'natural' - // data-model kind for such a type (e.g., structs will say 'map' here). - ActualKind ReprKind -} - -func (e ErrWrongKind) Error() string { - if e.TypeName == "" { - return fmt.Sprintf("func called on wrong kind: %s called on a %s node, but only makes sense on %s", e.MethodName, e.ActualKind, e.AppropriateKind) - } else { - return fmt.Sprintf("func called on wrong kind: %s called on a %s node (kind: %s), but only makes sense on %s", e.MethodName, e.TypeName, e.ActualKind, e.AppropriateKind) - } -} - -// ErrNotExists may be returned from the lookup functions of the Node interface -// to indicate a missing value. -// -// Note that schema.ErrNoSuchField is another type of error which sometimes -// occurs in similar places as ErrNotExists. ErrNoSuchField is preferred -// when handling data with constraints provided by a schema that mean that -// a field can *never* exist (as differentiated from a map key which is -// simply absent in some data). -type ErrNotExists struct { - Segment PathSegment -} - -func (e ErrNotExists) Error() string { - return fmt.Sprintf("key not found: %q", e.Segment) -} - -type ErrRepeatedMapKey struct { - Key Node -} - -func (e ErrRepeatedMapKey) Error() string { - return fmt.Sprintf("cannot repeat map key (\"%s\")", e.Key) -} - -// ErrIteratorOverread is returned when calling 'Next' on a MapIterator or -// ListIterator when it is already done. -type ErrIteratorOverread struct{} - -func (e ErrIteratorOverread) Error() string { - return "iterator overread" -} - -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/_rsrch/nodesolution/kind.go b/_rsrch/nodesolution/kind.go deleted file mode 120000 index e671d650..00000000 --- a/_rsrch/nodesolution/kind.go +++ /dev/null @@ -1 +0,0 @@ -../../kind.go \ No newline at end of file diff --git a/_rsrch/nodesolution/link.go b/_rsrch/nodesolution/link.go deleted file mode 100644 index 32a5dca0..00000000 --- a/_rsrch/nodesolution/link.go +++ /dev/null @@ -1,4 +0,0 @@ -package ipld - -// dead-end punt to make this package compile; revisit and complete later. -type Link interface{} diff --git a/_rsrch/nodesolution/node.go b/_rsrch/nodesolution/node.go deleted file mode 100644 index 45834d12..00000000 --- a/_rsrch/nodesolution/node.go +++ /dev/null @@ -1,274 +0,0 @@ -package ipld - -// Node represents a value in IPLD. Any point in a tree of data is a node: -// scalar values (like int, string, etc) are nodes, and -// so are recursive values (like map and list). -// -// Nodes and kinds are described in the IPLD specs at -// https://github.com/ipld/specs/blob/master/data-model-layer/data-model.md . -// -// Methods on the Node interface cover the superset of all possible methods for -// all possible kinds -- but some methods only make sense for particular kinds, -// and thus will only make sense to call on values of the appropriate kind. -// (For example, 'Length' on an int doesn't make sense, -// and 'AsInt' on a map certainly doesn't work either!) -// Use the ReprKind method to find out the kind of value before -// calling kind-specific methods. -// Individual method documentation state which kinds the method is valid for. -// (If you're familiar with the stdlib reflect package, you'll find -// the design of the Node interface very comparable to 'reflect.Value'.) -// -// The Node interface is read-only. All of the methods on the interface are -// for examining values, and implementations should be immutable. -// The companion interface, NodeBuilder, provides the matching writable -// methods, and should be use to create a (thence immutable) Node. -// -// Keeping Node immutable and separating mutation into NodeBuilder makes -// it possible to perform caching (or rather, memoization, since there's no -// such thing as cache invalidation for immutable systems) of computed -// properties of Node; use copy-on-write algorithms for memory efficiency; -// and to generally build pleasant APIs. -// Many library functions will rely on the immutability of Node (e.g., -// assuming that pointer-equal nodes do not change in value over time), -// so any user-defined Node implementations should be careful to uphold -// the immutability contract.) -// -// There are many different concrete types which implement Node. -// The primary purpose of various node implementations is to organize -// memory in the program in different ways -- some in-memory layouts may -// be more optimal for some programs than others, and changing the Node -// (and NodeBuilder) implementations lets the programmer choose. -// -// For concrete implementations of Node, check out the "./impl/" folder, -// and the packages within it. -// "impl/free" should probably be your first start; the Node and NodeBuilder -// implementations in that package work for any data. -// Other packages are optimized for specific use-cases. -// Codegen tools can also be used to produce concrete implementations of Node; -// these may be specific to certain data, but still conform to the Node -// interface for interoperability and to support higher-level functions. -// -// Nodes may also be *typed* -- see the 'schema' and 'impl/typed' packages. -// Typed nodes have additional constraints and behaviors (and have a -// `.Type().Kind()` in addition to their `.ReprKind()`!), but still behave -// as a regular Node in all the basic ways. -type Node interface { - // ReprKind returns a value from the ReprKind enum describing what the - // essential serializable kind of this node is (map, list, int, etc). - // Most other handling of a node requires first switching upon the kind. - ReprKind() ReprKind - - // LookupString looks up a child object in this node and returns it. - // The returned Node may be any of the ReprKind: - // a primitive (string, int, etc), a map, a list, or a link. - // - // If the Kind of this Node is not ReprKind_Map, a nil node and an error - // will be returned. - // - // If the key does not exist, a nil node and an error will be returned. - LookupString(key string) (Node, error) - - // Lookup is the equivalent of LookupString, but takes a reified Node - // as a parameter instead of a plain string. - // This mechanism is useful if working with typed maps (if the key types - // have constraints, and you already have a reified `schema.TypedNode` value, - // using that value can save parsing and validation costs); - // and may simply be convenient if you already have a Node value in hand. - // - // (When writing generic functions over Node, a good rule of thumb is: - // when handling a map, check for `schema.TypedNode`, and in this case prefer - // the Lookup(Node) method; otherwise, favor LookupString; typically - // implementations will have their fastest paths thusly.) - Lookup(key Node) (Node, error) - - // LookupIndex is the equivalent of LookupString but for indexing into a list. - // As with LookupString, the returned Node may be any of the ReprKind: - // a primitive (string, int, etc), a map, a list, or a link. - // - // If the Kind of this Node is not ReprKind_List, a nil node and an error - // will be returned. - // - // If idx is out of range, a nil node and an error will be returned. - LookupIndex(idx int) (Node, error) - - // LookupSegment is will act as either LookupString or LookupIndex, - // whichever is contextually appropriate. - // - // Using LookupSegment may imply an "atoi" conversion if used on a list node, - // or an "itoa" conversion if used on a map node. If an "itoa" conversion - // takes place, it may error, and this method may return that error. - LookupSegment(seg PathSegment) (Node, error) - - // Note that when using codegenerated types, there may be a fifth variant - // of lookup method on maps: `Get($GeneratedTypeKey) $GeneratedTypeValue`! - - // MapIterator returns an iterator which yields key-value pairs - // traversing the node. - // If the node kind is anything other than a map, nil will be returned. - // - // The iterator will yield every entry in the map; that is, it - // can be expected that itr.Next will be called node.Length times - // before itr.Done becomes true. - MapIterator() MapIterator - - // ListIterator returns an iterator which yields key-value pairs - // traversing the node. - // If the node kind is anything other than a list, nil will be returned. - // - // The iterator will yield every entry in the list; that is, it - // can be expected that itr.Next will be called node.Length times - // before itr.Done becomes true. - ListIterator() ListIterator - - // Length returns the length of a list, or the number of entries in a map, - // or -1 if the node is not of list nor map kind. - Length() int - - // Undefined nodes are returned when traversing a struct field that is - // defined by a schema but unset in the data. (Undefined nodes are not - // possible otherwise; you'll only see them from `schema.TypedNode`.) - // The undefined flag is necessary so iterating over structs can - // unambiguously make the distinction between values that are - // present-and-null versus values that are absent. - IsUndefined() bool - - IsNull() bool - AsBool() (bool, error) - AsInt() (int, error) - AsFloat() (float64, error) - AsString() (string, error) - AsBytes() ([]byte, error) - AsLink() (Link, error) - - // Style returns a NodeStyle which can describe some properties of this node's implementation, - // and also be used to get a NodeBuilder, - // which can be use to create new nodes with the same implementation as this one. - // - // For typed nodes, the NodeStyle will also implement schema.Type. - // - // For Advanced Data Layouts, the NodeStyle will encapsulate any additional - // parameters and configuration of the ADL, and will also (usually) - // implement NodeStyleSupportingAmend. - // - // Calling this method should not cause an allocation. - Style() NodeStyle -} - -// NodeStyle describes a node implementation (all Node have a NodeStyle), -// and a NodeStyle can always be used to get a NodeBuilder. -// -// A NodeStyle may also provide other information about implementation; -// such information is specific to this library ("style" isn't a concept -// you'll find in the IPLD Specifications), and is usually provided through -// feature-detection interfaces (for example, see NodeStyleSupportingAmend). -// -// Generic algorithms for working with IPLD Nodes make use of NodeStyle -// to get builders for new nodes when creating data, and can also use the -// feature-detection interfaces to help decide what kind of operations -// will be optimal to use on a given node implementation. -// -// Note that NodeStyle is not the same as schema.Type. -// NodeStyle is a (golang-specific!) way to reflect upon the implementation -// and in-memory layout of some IPLD data. -// schema.Type is information about how a group of nodes is related in a schema -// (if they have one!) and the rules that the type mandates the node must follow. -// (Every node must have a style; but schema types are an optional feature.) -type NodeStyle interface { - // NewBuilder returns a NodeBuilder that can be used to create a new Node. - // - // Note that calling NewBuilder often performs an allocation - // (while in contrast, getting a NodeStyle typically does not!) -- - // this may be consequential when writing high performance code. - NewBuilder() NodeBuilder -} - -// NodeStyleSupportingAmend is a feature-detection interface that can be -// used on a NodeStyle to see if it's possible to build new nodes of this style -// while sharing some internal data in a copy-on-write way. -// -// For example, Nodes using an Advanced Data Layout will typically -// support this behavior, and since ADLs are often used for handling large -// volumes of data, detecting and using this feature can result in significant -// performance savings. -type NodeStyleSupportingAmend interface { - AmendingBuilder(base Node) NodeBuilder - // FUTURE: probably also needs a `AmendingWithout(base Node, filter func(k,v) bool) NodeBuilder`, or similar. - // ("deletion" based APIs are also possible but both more complicated in interfaces added, and prone to accidentally quadratic usage.) - // FUTURE: there should be some stdlib `Copy` (?) methods that automatically look for this feature, and fallback if absent. - // Might include a wide range of point `Transform`, etc, methods. - // FUTURE: consider putting this (and others like it) in a `feature` package, if there begin to be enough of them and docs get crowded. -} - -// MapIterator is an interface for traversing map nodes. -// Sequential calls to Next() will yield key-value pairs; -// Done() describes whether iteration should continue. -// -// Iteration order is defined to be stable: two separate MapIterator -// created to iterate the same Node will yield the same key-value pairs -// in the same order. -// The order itself may be defined by the Node implementation: some -// Nodes may retain insertion order, and some may return iterators which -// always yield data in sorted order, for example. -type MapIterator interface { - // Next returns the next key-value pair. - // - // An error value can also be returned at any step: in the case of advanced - // data structures with incremental loading, it's possible to encounter - // cancellation or I/O errors at any point in iteration. - // If an error is returned, the boolean will always be false (so it's - // correct to check the bool first and short circuit to continuing if true). - // If an error is returned, the key and value may be nil. - Next() (key Node, value Node, err error) - - // Done returns false as long as there's at least one more entry to iterate. - // When Done returns true, iteration can stop. - // - // Note when implementing iterators for advanced data layouts (e.g. more than - // one chunk of backing data, which is loaded incrementally): if your - // implementation does any I/O during the Done method, and it encounters - // an error, it must return 'false', so that the following Next call - // has an opportunity to return the error. - Done() bool -} - -// ListIterator is an interface for traversing list nodes. -// Sequential calls to Next() will yield index-value pairs; -// Done() describes whether iteration should continue. -// -// A loop which iterates from 0 to Node.Length is a valid -// alternative to using a ListIterator. -type ListIterator interface { - // Next returns the next index and value. - // - // An error value can also be returned at any step: in the case of advanced - // data structures with incremental loading, it's possible to encounter - // cancellation or I/O errors at any point in iteration. - // If an error is returned, the boolean will always be false (so it's - // correct to check the bool first and short circuit to continuing if true). - // If an error is returned, the key and value may be nil. - Next() (idx int, value Node, err error) - - // Done returns false as long as there's at least one more entry to iterate. - // When Done returns false, iteration can stop. - // - // Note when implementing iterators for advanced data layouts (e.g. more than - // one chunk of backing data, which is loaded incrementally): if your - // implementation does any I/O during the Done method, and it encounters - // an error, it must return 'false', so that the following Next call - // has an opportunity to return the error. - Done() bool -} - -// REVIEW: immediate-mode AsBytes() method (as opposed to e.g. returning -// an io.Reader instance) might be problematic, esp. if we introduce -// AdvancedLayouts which support large bytes natively. -// -// Probable solution is having both immediate and iterator return methods. -// Returning a reader for bytes when you know you want a slice already -// is going to be high friction without purpose in many common uses. -// -// Unclear what SetByteStream() would look like for advanced layouts. -// One could try to encapsulate the chunking entirely within the advlay -// node impl... but would it be graceful? Not sure. Maybe. Hopefully! -// Yes? The advlay impl would still tend to use SetBytes for the raw -// data model layer nodes its composing, so overall, it shakes out nicely. diff --git a/_rsrch/nodesolution/node/mixins/tests/util.go b/_rsrch/nodesolution/node/mixins/tests/util.go deleted file mode 100644 index 3d01c357..00000000 --- a/_rsrch/nodesolution/node/mixins/tests/util.go +++ /dev/null @@ -1,22 +0,0 @@ -package tests - -import ( - "bytes" - - refmtjson "github.com/polydawn/refmt/json" - - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/codec" -) - -// various benchmarks assign their final result here, -// in order to defuse the possibility of their work being elided. -var sink interface{} - -func mustNodeFromJsonString(nb ipld.NodeBuilder, str string) ipld.Node { - err := codec.Unmarshal(nb, refmtjson.NewDecoder(bytes.NewBufferString(str))) - if err != nil { - panic(err) - } - return nb.Build() -} diff --git a/_rsrch/nodesolution/nodeBuilder.go b/_rsrch/nodesolution/nodeBuilder.go deleted file mode 100644 index 513b4f4b..00000000 --- a/_rsrch/nodesolution/nodeBuilder.go +++ /dev/null @@ -1,129 +0,0 @@ -package ipld - -// NodeAssembler is the interface that describes all the ways we can set values -// in a node that's under construction. -// -// To create a Node, you should start with a NodeBuilder (which contains a -// superset of the NodeAssembler methods, and can return the finished Node -// from its `Build` method). -// -// Why do both this and the NodeBuilder interface exist? -// When creating trees of nodes, recursion works over the NodeAssembler interface. -// This is important to efficient library internals, because avoiding the -// requirement to be able to return a Node at any random point in the process -// relieves internals from needing to implement 'freeze' features. -// (This is useful in turn because implementing those 'freeze' features in a -// language without first-class/compile-time support for them (as golang is) -// would tend to push complexity and costs to execution time; we'd rather not.) -type NodeAssembler interface { - BeginMap(sizeHint int) (MapNodeAssembler, error) - BeginList(sizeHint int) (ListNodeAssembler, error) - AssignNull() error - AssignBool(bool) error - AssignInt(int) error - AssignFloat(float64) error - AssignString(string) error - AssignBytes([]byte) error - AssignLink(Link) error - - AssignNode(Node) error // if you already have a completely constructed subtree, this method puts the whole thing in place at once. - - // Style returns a NodeStyle describing what kind of value we're assembling. - // - // You often don't need this (because you should be able to - // just feed data and check errors), but it's here. - // - // Using `this.Style().NewBuilder()` to produce a new `Node`, - // then giving that node to `this.AssignNode(n)` should always work. - // (Note that this is not necessarily an _exclusive_ statement on what - // sort of values will be accepted by `this.AssignNode(n)`.) - Style() NodeStyle -} - -// MapNodeAssembler assembles a map node! (You guessed it.) -// -// Methods on MapNodeAssembler must be called in a valid order: -// assemble a key, then assemble a value, then loop as long as desired; -// when finished, call 'Finish'. -// -// Incorrect order invocations will panic. -// Calling AssembleKey twice in a row will panic; -// calling AssembleValue before finishing using the NodeAssembler from AssembleKey will panic; -// calling AssembleValue twice in a row will panic; -// etc. -// -// Note that the NodeAssembler yielded from AssembleKey has additional behavior: -// if the node assembled there matches a key already present in the map, -// that assembler will emit the error! -type MapNodeAssembler interface { - AssembleKey() NodeAssembler // must be followed by call to AssembleValue. - AssembleValue() NodeAssembler // must be called immediately after AssembleKey. - - AssembleDirectly(k string) (NodeAssembler, error) // shortcut combining AssembleKey and AssembleValue into one step; valid when the key is a string kind. - - Finish() error - - // KeyStyle returns a NodeStyle that knows how to build keys of a type this map uses. - // - // You often don't need this (because you should be able to - // just feed data and check errors), but it's here. - // - // For all Data Model maps, this will answer with a basic concept of "string". - // For Schema typed maps, this may answer with a more complex type (potentially even a struct type). - KeyStyle() NodeStyle - - // ValueStyle returns a NodeStyle that knows how to build values this map can contain. - // - // You often don't need this (because you should be able to - // just feed data and check errors), but it's here. - // - // ValueStyle requires a parameter describing the key in order to say what - // NodeStyle will be acceptable as a value for that key, because when using - // struct types (or union types) from the Schemas system, they behave as maps - // but have different acceptable types for each field (or member, for unions). - // For plain maps (that is, not structs or unions masquerading as maps), - // the empty string can be used as a parameter, and the returned NodeStyle - // can be assumed applicable for all values. - // Using an empty string for a struct or union will return a nil NodeStyle. - // (Design note: a string is sufficient for the parameter here rather than - // a full Node, because the only cases where the value types vary are also - // cases where the keys may not be complex.) - ValueStyle(k string) NodeStyle -} - -type ListNodeAssembler interface { - AssembleValue() NodeAssembler - - Finish() error - - // ValueStyle returns a NodeStyle that knows how to build values this map can contain. - // - // You often don't need this (because you should be able to - // just feed data and check errors), but it's here. - // - // In contrast to the `MapNodeAssembler.ValueStyle(key)` function, - // to determine the ValueStyle for lists we need no parameters; - // lists always contain one value type (even if it's "any"). - ValueStyle() NodeStyle -} - -type NodeBuilder interface { - NodeAssembler - - // Build returns the new value after all other assembly has been completed. - // - // A method on the NodeAssembler that finishes assembly of the data must - // be called first (e.g., any of the "Assign*" methods, or "Finish" if - // the assembly was for a map or a list); that finishing method still has - // all responsibility for validating the assembled data and returning - // any errors from that process. - // (Correspondingly, there is no error return from this method.) - Build() Node - - // Resets the builder. It can hereafter be used again. - // Reusing a NodeBuilder can reduce allocations and improve performance. - // - // Only call this if you're going to reuse the builder. - // (Otherwise, it's unnecessary, and may cause an unwanted allocation). - Reset() -} diff --git a/_rsrch/nodesolution/path.go b/_rsrch/nodesolution/path.go deleted file mode 120000 index 41ea902e..00000000 --- a/_rsrch/nodesolution/path.go +++ /dev/null @@ -1 +0,0 @@ -../../path.go \ No newline at end of file diff --git a/_rsrch/nodesolution/pathSegment.go b/_rsrch/nodesolution/pathSegment.go deleted file mode 120000 index 652276aa..00000000 --- a/_rsrch/nodesolution/pathSegment.go +++ /dev/null @@ -1 +0,0 @@ -../../pathSegment.go \ No newline at end of file diff --git a/bench/benchMarshalling_test.go b/bench/benchMarshalling_test.go deleted file mode 100644 index 5b31458c..00000000 --- a/bench/benchMarshalling_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package bench - -import ( - "bytes" - "testing" - - "github.com/polydawn/refmt/json" - - "github.com/ipld/go-ipld-prime/encoding" - "github.com/ipld/go-ipld-prime/impl/free" -) - -// This is identical to something in the refmt project benchmarks. -// We have a *radically* different approach than refmt's "obj" package does, so this will be... interesting. -// ... update: actually, they're... pretty close. -// Unmarshal is within 20% of the same performance, before we've done any work to optimize go-ipld-prime (but a bit slower). -// Marshal is actually taking only 70% as long as refmt did on a map -- our node accessors are *more* efficient than reflection. -// Okay then! Not bad. -var fixture_structAlpha_json = []byte(`{"B":{"R":{"M":"quir","R":{"M":"asdf","R":{"M":"","R":null}}}},"C":{"M":13,"N":"n"},"C2":{"M":14,"N":"n2"},"W":"4","X":1,"Y":2,"Z":"3"}`) - -var sink interface{} - -func Benchmark_MapAlpha_UnmarshalIntoFreenode(b *testing.B) { - for i := 0; i < b.N; i++ { - nb := ipldfree.NodeBuilder() - n, err := encoding.Unmarshal(nb, json.NewDecoder(bytes.NewReader(fixture_structAlpha_json))) - if err != nil { - panic(err) - } - sink = n - } -} - -func Benchmark_MapAlpha_MarshalFromFreenode(b *testing.B) { - nb := ipldfree.NodeBuilder() - n, err := encoding.Unmarshal(nb, json.NewDecoder(bytes.NewReader(fixture_structAlpha_json))) - if err != nil { - panic(err) - } - var buf bytes.Buffer - for i := 0; i < b.N; i++ { - encoding.Marshal(n, json.NewEncoder(&buf, json.EncodeOptions{})) - } -} diff --git a/encoding/dagcbor/common.go b/codec/dagcbor/common.go similarity index 100% rename from encoding/dagcbor/common.go rename to codec/dagcbor/common.go diff --git a/encoding/dagcbor/marshal.go b/codec/dagcbor/marshal.go similarity index 82% rename from encoding/dagcbor/marshal.go rename to codec/dagcbor/marshal.go index 722b0fc8..ad95d9cc 100644 --- a/encoding/dagcbor/marshal.go +++ b/codec/dagcbor/marshal.go @@ -13,21 +13,24 @@ import ( // This should be identical to the general feature in the parent package, // except for the `case ipld.ReprKind_Link` block, // which is dag-cbor's special sauce for schemafree links. - func Marshal(n ipld.Node, sink shared.TokenSink) error { var tk tok.Token + return marshal(n, &tk, sink) +} + +func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error { switch n.ReprKind() { case ipld.ReprKind_Invalid: return fmt.Errorf("cannot traverse a node that is undefined") case ipld.ReprKind_Null: tk.Type = tok.TNull - _, err := sink.Step(&tk) + _, err := sink.Step(tk) return err case ipld.ReprKind_Map: // Emit start of map. tk.Type = tok.TMapOpen tk.Length = n.Length() - if _, err := sink.Step(&tk); err != nil { + if _, err := sink.Step(tk); err != nil { return err } // Emit map contents (and recurse). @@ -41,23 +44,23 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error { if err != nil { return err } - if _, err := sink.Step(&tk); err != nil { + if _, err := sink.Step(tk); err != nil { return err } - if err := Marshal(v, sink); err != nil { + if err := marshal(v, tk, sink); err != nil { return err } } // Emit map close. tk.Type = tok.TMapClose - _, err := sink.Step(&tk) + _, err := sink.Step(tk) return err case ipld.ReprKind_List: // Emit start of list. tk.Type = tok.TArrOpen l := n.Length() tk.Length = l - if _, err := sink.Step(&tk); err != nil { + if _, err := sink.Step(tk); err != nil { return err } // Emit list contents (and recurse). @@ -66,13 +69,13 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error { if err != nil { return err } - if err := Marshal(v, sink); err != nil { + if err := marshal(v, tk, sink); err != nil { return err } } // Emit list close. tk.Type = tok.TArrClose - _, err := sink.Step(&tk) + _, err := sink.Step(tk) return err case ipld.ReprKind_Bool: v, err := n.AsBool() @@ -81,7 +84,7 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error { } tk.Type = tok.TBool tk.Bool = v - _, err = sink.Step(&tk) + _, err = sink.Step(tk) return err case ipld.ReprKind_Int: v, err := n.AsInt() @@ -90,7 +93,7 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error { } tk.Type = tok.TInt tk.Int = int64(v) - _, err = sink.Step(&tk) + _, err = sink.Step(tk) return err case ipld.ReprKind_Float: v, err := n.AsFloat() @@ -99,7 +102,7 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error { } tk.Type = tok.TFloat64 tk.Float64 = v - _, err = sink.Step(&tk) + _, err = sink.Step(tk) return err case ipld.ReprKind_String: v, err := n.AsString() @@ -108,7 +111,7 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error { } tk.Type = tok.TString tk.Str = v - _, err = sink.Step(&tk) + _, err = sink.Step(tk) return err case ipld.ReprKind_Bytes: v, err := n.AsBytes() @@ -117,7 +120,7 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error { } tk.Type = tok.TBytes tk.Bytes = v - _, err = sink.Step(&tk) + _, err = sink.Step(tk) return err case ipld.ReprKind_Link: v, err := n.AsLink() @@ -130,7 +133,7 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error { tk.Bytes = append([]byte{0}, lnk.Bytes()...) tk.Tagged = true tk.Tag = linkTag - _, err = sink.Step(&tk) + _, err = sink.Step(tk) return err default: return fmt.Errorf("schemafree link emission only supported by this codec for CID type links!") diff --git a/encoding/dagcbor/multicodec.go b/codec/dagcbor/multicodec.go similarity index 79% rename from encoding/dagcbor/multicodec.go rename to codec/dagcbor/multicodec.go index 0a7a3f19..34b822ac 100644 --- a/encoding/dagcbor/multicodec.go +++ b/codec/dagcbor/multicodec.go @@ -19,17 +19,17 @@ func init() { cidlink.RegisterMulticodecEncoder(0x71, Encoder) } -func Decoder(nb ipld.NodeBuilder, r io.Reader) (ipld.Node, error) { +func Decoder(na ipld.NodeAssembler, r io.Reader) error { // Probe for a builtin fast path. Shortcut to that if possible. // (ipldcbor.NodeBuilder supports this, for example.) type detectFastPath interface { - DecodeDagCbor(io.Reader) (ipld.Node, error) + DecodeDagCbor(io.Reader) error } - if nb2, ok := nb.(detectFastPath); ok { - return nb2.DecodeDagCbor(r) + if na2, ok := na.(detectFastPath); ok { + return na2.DecodeDagCbor(r) } // Okay, generic builder path. - return Unmarshal(nb, cbor.NewDecoder(cbor.DecodeOptions{}, r)) + return Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r)) } func Encoder(n ipld.Node, w io.Writer) error { diff --git a/encoding/dagcbor/roundtripCidlink_test.go b/codec/dagcbor/roundtripCidlink_test.go similarity index 72% rename from encoding/dagcbor/roundtripCidlink_test.go rename to codec/dagcbor/roundtripCidlink_test.go index 065a893c..9cf089f7 100644 --- a/encoding/dagcbor/roundtripCidlink_test.go +++ b/codec/dagcbor/roundtripCidlink_test.go @@ -10,8 +10,8 @@ import ( cid "github.com/ipfs/go-cid" ipld "github.com/ipld/go-ipld-prime" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" ) func TestRoundtripCidlink(t *testing.T) { @@ -21,18 +21,21 @@ func TestRoundtripCidlink(t *testing.T) { MhType: 0x17, MhLength: 4, }} + buf := bytes.Buffer{} lnk, err := lb.Build(context.Background(), ipld.LinkContext{}, n, func(ipld.LinkContext) (io.Writer, ipld.StoreCommitter, error) { return &buf, func(lnk ipld.Link) error { return nil }, nil }, ) - Wish(t, err, ShouldEqual, nil) - n2, err := lnk.Load(context.Background(), ipld.LinkContext{}, ipldfree.NodeBuilder(), + Require(t, err, ShouldEqual, nil) + + nb := basicnode.Style__Any{}.NewBuilder() + err = lnk.Load(context.Background(), ipld.LinkContext{}, nb, func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) { return bytes.NewBuffer(buf.Bytes()), nil }, ) - Wish(t, err, ShouldEqual, nil) - Wish(t, n2, ShouldEqual, n) + Require(t, err, ShouldEqual, nil) + Wish(t, nb.Build(), ShouldEqual, n) } diff --git a/codec/dagcbor/roundtrip_test.go b/codec/dagcbor/roundtrip_test.go new file mode 100644 index 00000000..94fed572 --- /dev/null +++ b/codec/dagcbor/roundtrip_test.go @@ -0,0 +1,64 @@ +package dagcbor + +import ( + "bytes" + "testing" + + . "github.com/warpfork/go-wish" + + "github.com/ipld/go-ipld-prime/fluent" + basicnode "github.com/ipld/go-ipld-prime/node/basic" +) + +var n = fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("plain").AssignString("olde string") + na.AssembleDirectly("map").CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("one").AssignInt(1) + na.AssembleDirectly("two").AssignInt(2) + }) + na.AssembleDirectly("list").CreateList(2, func(na fluent.ListNodeAssembler) { + na.AssembleValue().AssignString("three") + na.AssembleValue().AssignString("four") + }) + na.AssembleDirectly("nested").CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("deeper").CreateList(1, func(na fluent.ListNodeAssembler) { + na.AssembleValue().AssignString("things") + }) + }) +}) +var serial = "\xa4eplainkolde stringcmap\xa2cone\x01ctwo\x02dlist\x82ethreedfourfnested\xa1fdeeper\x81fthings" + +func TestRoundtrip(t *testing.T) { + t.Run("encoding", func(t *testing.T) { + var buf bytes.Buffer + err := Encoder(n, &buf) + Require(t, err, ShouldEqual, nil) + Wish(t, buf.String(), ShouldEqual, serial) + }) + t.Run("decoding", func(t *testing.T) { + buf := bytes.NewBufferString(serial) + nb := basicnode.Style__Map{}.NewBuilder() + err := Decoder(nb, buf) + Require(t, err, ShouldEqual, nil) + Wish(t, nb.Build(), ShouldEqual, n) + }) +} + +func TestRoundtripScalar(t *testing.T) { + nb := basicnode.Style__String{}.NewBuilder() + nb.AssignString("applesauce") + simple := nb.Build() + t.Run("encoding", func(t *testing.T) { + var buf bytes.Buffer + err := Encoder(simple, &buf) + Require(t, err, ShouldEqual, nil) + Wish(t, buf.String(), ShouldEqual, `japplesauce`) + }) + t.Run("decoding", func(t *testing.T) { + buf := bytes.NewBufferString(`japplesauce`) + nb := basicnode.Style__String{}.NewBuilder() + err := Decoder(nb, buf) + Require(t, err, ShouldEqual, nil) + Wish(t, nb.Build(), ShouldEqual, simple) + }) +} diff --git a/encoding/dagcbor/unmarshal.go b/codec/dagcbor/unmarshal.go similarity index 50% rename from encoding/dagcbor/unmarshal.go rename to codec/dagcbor/unmarshal.go index 741498c1..34bb0a73 100644 --- a/encoding/dagcbor/unmarshal.go +++ b/codec/dagcbor/unmarshal.go @@ -21,134 +21,131 @@ var ( // except for the `case tok.TBytes` block, // which has dag-cbor's special sauce for detecting schemafree links. -func Unmarshal(nb ipld.NodeBuilder, tokSrc shared.TokenSource) (ipld.Node, error) { +func Unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource) error { var tk tok.Token done, err := tokSrc.Step(&tk) if err != nil { - return nil, err + return err } if done && !tk.Type.IsValue() { - return nil, err + return fmt.Errorf("unexpected eof") } - return unmarshal(nb, tokSrc, &tk) + return unmarshal(na, tokSrc, &tk) } // starts with the first token already primed. Necessary to get recursion // to flow right without a peek+unpeek system. -func unmarshal(nb ipld.NodeBuilder, tokSrc shared.TokenSource, tk *tok.Token) (ipld.Node, error) { +func unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token) error { // FUTURE: check for schema.TypedNodeBuilder that's going to parse a Link (they can slurp any token kind they want). switch tk.Type { case tok.TMapOpen: - mb, err := nb.CreateMap() - if err != nil { - return nil, err - } expectLen := tk.Length + allocLen := tk.Length if tk.Length == -1 { expectLen = math.MaxInt32 + allocLen = 0 + } + ma, err := na.BeginMap(allocLen) + if err != nil { + return err } observedLen := 0 - var k string - var v ipld.Node for { _, err := tokSrc.Step(tk) if err != nil { - return nil, err + return err } switch tk.Type { case tok.TMapClose: if expectLen != math.MaxInt32 && observedLen != expectLen { - return nil, fmt.Errorf("unexpected mapClose before declared length") + return fmt.Errorf("unexpected mapClose before declared length") } - return mb.Build() + return ma.Finish() case tok.TString: // continue default: - return nil, fmt.Errorf("unexpected %s token while expecting map key", tk.Type) + return fmt.Errorf("unexpected %s token while expecting map key", tk.Type) } observedLen++ if observedLen > expectLen { - return nil, fmt.Errorf("unexpected continuation of map elements beyond declared length") + return fmt.Errorf("unexpected continuation of map elements beyond declared length") } - k = tk.Str - v, err = Unmarshal(mb.BuilderForValue(k), tokSrc) - if err != nil { - return nil, err - } - kn, err := mb.BuilderForKeys().CreateString(k) - if err != nil { - return nil, fmt.Errorf("value rejected as key: %s", err) + mva, err := ma.AssembleDirectly(tk.Str) + if err != nil { // return in error if the key was rejected + return err } - if err := mb.Insert(kn, v); err != nil { - return nil, err + err = Unmarshal(mva, tokSrc) + if err != nil { // return in error if some part of the recursion errored + return err } } case tok.TMapClose: - return nil, fmt.Errorf("unexpected mapClose token") + return fmt.Errorf("unexpected mapClose token") case tok.TArrOpen: - lb, err := nb.CreateList() - if err != nil { - return nil, err - } expectLen := tk.Length + allocLen := tk.Length if tk.Length == -1 { expectLen = math.MaxInt32 + allocLen = 0 + } + la, err := na.BeginList(allocLen) + if err != nil { + return err } - i := 0 + observedLen := 0 for { _, err := tokSrc.Step(tk) if err != nil { - return nil, err + return err } switch tk.Type { case tok.TArrClose: - if expectLen != math.MaxInt32 && i != expectLen { - return nil, fmt.Errorf("unexpected arrClose before declared length") + if expectLen != math.MaxInt32 && observedLen != expectLen { + return fmt.Errorf("unexpected arrClose before declared length") } - return lb.Build() + return la.Finish() default: - if i >= expectLen { - return nil, fmt.Errorf("unexpected continuation of array elements beyond declared length") + observedLen++ + if observedLen > expectLen { + return fmt.Errorf("unexpected continuation of array elements beyond declared length") } - v, err := unmarshal(lb.BuilderForValue(i), tokSrc, tk) - i++ - if err != nil { - return nil, err + err := unmarshal(la.AssembleValue(), tokSrc, tk) + if err != nil { // return in error if some part of the recursion errored + return err } - lb.Append(v) } } case tok.TArrClose: - return nil, fmt.Errorf("unexpected arrClose token") + return fmt.Errorf("unexpected arrClose token") case tok.TNull: - return nb.CreateNull() + return na.AssignNull() case tok.TString: - return nb.CreateString(tk.Str) + return na.AssignString(tk.Str) case tok.TBytes: if !tk.Tagged { - return nb.CreateBytes(tk.Bytes) + return na.AssignBytes(tk.Bytes) } switch tk.Tag { case linkTag: if tk.Bytes[0] != 0 { - return nil, ErrInvalidMultibase + return ErrInvalidMultibase } elCid, err := cid.Cast(tk.Bytes[1:]) if err != nil { - return nil, err + return err } - return nb.CreateLink(cidlink.Link{elCid}) + return na.AssignLink(cidlink.Link{elCid}) default: - return nil, fmt.Errorf("unhandled cbor tag %d", tk.Tag) + return fmt.Errorf("unhandled cbor tag %d", tk.Tag) } case tok.TBool: - return nb.CreateBool(tk.Bool) + return na.AssignBool(tk.Bool) case tok.TInt: - return nb.CreateInt(int(tk.Int)) // FIXME overflow check + return na.AssignInt(int(tk.Int)) // FIXME overflow check case tok.TUint: - return nb.CreateInt(int(tk.Uint)) // FIXME overflow check + return na.AssignInt(int(tk.Uint)) // FIXME overflow check case tok.TFloat64: - return nb.CreateFloat(tk.Float64) + return na.AssignFloat(tk.Float64) default: panic("unreachable") } diff --git a/encoding/dagjson/marshal.go b/codec/dagjson/marshal.go similarity index 100% rename from encoding/dagjson/marshal.go rename to codec/dagjson/marshal.go diff --git a/encoding/dagjson/multicodec.go b/codec/dagjson/multicodec.go similarity index 85% rename from encoding/dagjson/multicodec.go rename to codec/dagjson/multicodec.go index c0084492..e54d3aff 100644 --- a/encoding/dagjson/multicodec.go +++ b/codec/dagjson/multicodec.go @@ -20,12 +20,12 @@ func init() { cidlink.RegisterMulticodecEncoder(0x0129, Encoder) } -func Decoder(nb ipld.NodeBuilder, r io.Reader) (ipld.Node, error) { +func Decoder(na ipld.NodeAssembler, r io.Reader) error { // Shell out directly to generic builder path. // (There's not really any fastpaths of note for json.) - n, err := Unmarshal(nb, json.NewDecoder(r)) + err := Unmarshal(na, json.NewDecoder(r)) if err != nil { - return n, err + return err } // Slurp any remaining whitespace. // (This is relevant if our reader is tee'ing bytes to a hasher, and @@ -39,17 +39,17 @@ func Decoder(nb ipld.NodeBuilder, r io.Reader) (ipld.Node, error) { switch buf[0] { case ' ', 0x0, '\t', '\r', '\n': // continue default: - return n, fmt.Errorf("unexpected content after end of json object") + return fmt.Errorf("unexpected content after end of json object") } if err == nil { continue } else if err == io.EOF { - return n, nil + return nil } else { - return n, err + return err } } - return n, err + return err } func Encoder(n ipld.Node, w io.Writer) error { diff --git a/codec/dagjson/roundtripCidlink_test.go b/codec/dagjson/roundtripCidlink_test.go new file mode 100644 index 00000000..74f4f8b2 --- /dev/null +++ b/codec/dagjson/roundtripCidlink_test.go @@ -0,0 +1,74 @@ +package dagjson + +import ( + "bytes" + "context" + "io" + "io/ioutil" + "testing" + + . "github.com/warpfork/go-wish" + + cid "github.com/ipfs/go-cid" + ipld "github.com/ipld/go-ipld-prime" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" +) + +func TestRoundtripCidlink(t *testing.T) { + lb := cidlink.LinkBuilder{cid.Prefix{ + Version: 1, + Codec: 0x0129, + MhType: 0x17, + MhLength: 4, + }} + + buf := bytes.Buffer{} + lnk, err := lb.Build(context.Background(), ipld.LinkContext{}, n, + func(ipld.LinkContext) (io.Writer, ipld.StoreCommitter, error) { + return &buf, func(lnk ipld.Link) error { return nil }, nil + }, + ) + Require(t, err, ShouldEqual, nil) + + nb := basicnode.Style__Any{}.NewBuilder() + err = lnk.Load(context.Background(), ipld.LinkContext{}, nb, + func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) { + return bytes.NewBuffer(buf.Bytes()), nil + }, + ) + Require(t, err, ShouldEqual, nil) + Wish(t, nb.Build(), ShouldEqual, n) +} + +// Make sure that a map that *almost* looks like a link is handled safely. +// +// This is aiming very specifically at the corner case where a minimal number of +// tokens have to be reprocessed before a recursion that find a real link appears. +func TestUnmarshalTrickyMapContainingLink(t *testing.T) { + // Create a link; don't particularly care about its contents. + lnk, err := cidlink.LinkBuilder{cid.Prefix{ + Version: 1, + Codec: 0x0129, + MhType: 0x17, + MhLength: 4, + }}.Build(context.Background(), ipld.LinkContext{}, n, + func(ipld.LinkContext) (io.Writer, ipld.StoreCommitter, error) { + return ioutil.Discard, func(lnk ipld.Link) error { return nil }, nil + }, + ) + Require(t, err, ShouldEqual, nil) + + // Compose the tricky corpus. (lnk.String "happens" to work here, although this isn't recommended or correct in general.) + tricky := `{"/":{"/":"` + lnk.String() + `"}}` + + // Unmarshal. Hopefully we get a map with a link in it. + nb := basicnode.Style__Any{}.NewBuilder() + err = Decoder(nb, bytes.NewBufferString(tricky)) + Require(t, err, ShouldEqual, nil) + n := nb.Build() + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + n2, err := n.LookupString("/") + Require(t, err, ShouldEqual, nil) + Wish(t, n2.ReprKind(), ShouldEqual, ipld.ReprKind_Link) +} diff --git a/codec/dagjson/roundtrip_test.go b/codec/dagjson/roundtrip_test.go new file mode 100644 index 00000000..b555d797 --- /dev/null +++ b/codec/dagjson/roundtrip_test.go @@ -0,0 +1,80 @@ +package dagjson + +import ( + "bytes" + "testing" + + . "github.com/warpfork/go-wish" + + "github.com/ipld/go-ipld-prime/fluent" + basicnode "github.com/ipld/go-ipld-prime/node/basic" +) + +var n = fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("plain").AssignString("olde string") + na.AssembleDirectly("map").CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("one").AssignInt(1) + na.AssembleDirectly("two").AssignInt(2) + }) + na.AssembleDirectly("list").CreateList(2, func(na fluent.ListNodeAssembler) { + na.AssembleValue().AssignString("three") + na.AssembleValue().AssignString("four") + }) + na.AssembleDirectly("nested").CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("deeper").CreateList(1, func(na fluent.ListNodeAssembler) { + na.AssembleValue().AssignString("things") + }) + }) +}) +var serial = `{ + "plain": "olde string", + "map": { + "one": 1, + "two": 2 + }, + "list": [ + "three", + "four" + ], + "nested": { + "deeper": [ + "things" + ] + } +} +` + +func TestRoundtrip(t *testing.T) { + t.Run("encoding", func(t *testing.T) { + var buf bytes.Buffer + err := Encoder(n, &buf) + Require(t, err, ShouldEqual, nil) + Wish(t, buf.String(), ShouldEqual, serial) + }) + t.Run("decoding", func(t *testing.T) { + buf := bytes.NewBufferString(serial) + nb := basicnode.Style__Map{}.NewBuilder() + err := Decoder(nb, buf) + Require(t, err, ShouldEqual, nil) + Wish(t, nb.Build(), ShouldEqual, n) + }) +} + +func TestRoundtripScalar(t *testing.T) { + nb := basicnode.Style__String{}.NewBuilder() + nb.AssignString("applesauce") + simple := nb.Build() + t.Run("encoding", func(t *testing.T) { + var buf bytes.Buffer + err := Encoder(simple, &buf) + Require(t, err, ShouldEqual, nil) + Wish(t, buf.String(), ShouldEqual, `"applesauce"`) + }) + t.Run("decoding", func(t *testing.T) { + buf := bytes.NewBufferString(`"applesauce"`) + nb := basicnode.Style__String{}.NewBuilder() + err := Decoder(nb, buf) + Require(t, err, ShouldEqual, nil) + Wish(t, nb.Build(), ShouldEqual, simple) + }) +} diff --git a/codec/dagjson/unmarshal.go b/codec/dagjson/unmarshal.go new file mode 100644 index 00000000..5e8484ec --- /dev/null +++ b/codec/dagjson/unmarshal.go @@ -0,0 +1,219 @@ +package dagjson + +import ( + "fmt" + + cid "github.com/ipfs/go-cid" + "github.com/polydawn/refmt/shared" + "github.com/polydawn/refmt/tok" + + ipld "github.com/ipld/go-ipld-prime" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" +) + +// This drifts pretty far from the general unmarshal in the parent package: +// - we know JSON never has length hints, so we ignore that field in tokens; +// - we know JSON never has tags, so we ignore that field as well; +// - we have dag-json's special sauce for detecting schemafree links +// (and this unfortunately turns out to *significantly* convolute the first +// several steps of handling maps, because it necessitates peeking several +// tokens before deciding what kind of value to create). + +func Unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource) error { + var st unmarshalState + done, err := tokSrc.Step(&st.tk[0]) + if err != nil { + return err + } + if done && !st.tk[0].Type.IsValue() { + return fmt.Errorf("unexpected eof") + } + return st.unmarshal(na, tokSrc) +} + +type unmarshalState struct { + tk [4]tok.Token // mostly, only 0'th is used... but [1:4] are used during lookahead for links. + shift int // how many times to slide something out of tk[1:4] instead of getting a new token. +} + +// step leaves a "new" token in tk[0], +// taking account of an shift left by linkLookahead. +// It's only necessary to use this when handling maps, +// since the situations resulting in nonzero shift are otherwise unreachable. +// +// At most, 'step' will be shifting buffered tokens for: +// - the first map key +// - the first map value (which will be a string) +// - the second map key +// and so (fortunately! whew!) we can do this in a fixed amount of memory, +// since none of those states can reach a recursion. +func (st *unmarshalState) step(tokSrc shared.TokenSource) error { + switch st.shift { + case 0: + _, err := tokSrc.Step(&st.tk[0]) + return err + case 1: + st.tk[0] = st.tk[1] + st.shift-- + return nil + case 2: + st.tk[0] = st.tk[1] + st.tk[1] = st.tk[2] + st.shift-- + return nil + case 3: + st.tk[0] = st.tk[1] + st.tk[1] = st.tk[2] + st.tk[2] = st.tk[3] + st.shift-- + return nil + default: + panic("unreachable") + } +} + +// linkLookahead is called after receiving a TMapOpen token; +// when it returns, we will have either created a link, OR +// it's not a link, and the caller should proceed to start a map +// and while using st.step to ensure the peeked tokens are handled, OR +// in case of error, the error should just rise. +// If the bool return is true, we got a link, and you should not +// continue to attempt to build a map. +func (st *unmarshalState) linkLookahead(na ipld.NodeAssembler, tokSrc shared.TokenSource) (bool, error) { + // Peek next token. If it's a "/" string, link is still a possibility + _, err := tokSrc.Step(&st.tk[1]) + if err != nil { + return false, err + } + if st.tk[1].Type != tok.TString { + st.shift = 1 + return false, nil + } + if st.tk[1].Str != "/" { + st.shift = 1 + return false, nil + } + // Peek next token. If it's a string, link is still a possibility. + // We won't try to parse it as a CID until we're sure it's the only thing in the map, though. + _, err = tokSrc.Step(&st.tk[2]) + if err != nil { + return false, err + } + if st.tk[2].Type != tok.TString { + st.shift = 2 + return false, nil + } + // Peek next token. If it's map close, we've got a link! + // (Otherwise it had better be a string, because another map key is the + // only other valid transition here... but we'll leave that check to the caller. + _, err = tokSrc.Step(&st.tk[3]) + if err != nil { + return false, err + } + if st.tk[3].Type != tok.TMapClose { + st.shift = 3 + return false, nil + } + // Okay, we made it -- this looks like a link. Parse it. + // If it *doesn't* parse as a CID, we treat this as an error. + elCid, err := cid.Decode(st.tk[2].Str) + if err != nil { + return false, err + } + if err := na.AssignLink(cidlink.Link{elCid}); err != nil { + return false, err + } + return true, nil + +} + +// starts with the first token already primed. Necessary to get recursion +// to flow right without a peek+unpeek system. +func (st *unmarshalState) unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource) error { + // FUTURE: check for schema.TypedNodeBuilder that's going to parse a Link (they can slurp any token kind they want). + switch st.tk[0].Type { + case tok.TMapOpen: + // dag-json has special needs: we pump a few tokens ahead to look for dag-json's "link" pattern. + // We can't actually call BeginMap until we're sure it's not gonna turn out to be a link. + gotLink, err := st.linkLookahead(na, tokSrc) + if err != nil { // return in error if any token peeks failed or if structure looked like a link but failed to parse as CID. + return err + } + if gotLink { + return nil + } + + // Okay, now back to regularly scheduled map logic. + ma, err := na.BeginMap(-1) + if err != nil { + return err + } + for { + err := st.step(tokSrc) // shift next token into slot 0. + if err != nil { // return in error if next token unreadable + return err + } + switch st.tk[0].Type { + case tok.TMapClose: + return ma.Finish() + case tok.TString: + // continue + default: + return fmt.Errorf("unexpected %s token while expecting map key", st.tk[0].Type) + } + mva, err := ma.AssembleDirectly(st.tk[0].Str) + if err != nil { // return in error if the key was rejected + return err + } + // Do another shift so the next token is primed before we recurse. + err = st.step(tokSrc) + if err != nil { // return in error if next token unreadable + return err + } + err = st.unmarshal(mva, tokSrc) + if err != nil { // return in error if some part of the recursion errored + return err + } + } + case tok.TMapClose: + return fmt.Errorf("unexpected mapClose token") + case tok.TArrOpen: + la, err := na.BeginList(-1) + if err != nil { + return err + } + for { + _, err := tokSrc.Step(&st.tk[0]) + if err != nil { + return err + } + switch st.tk[0].Type { + case tok.TArrClose: + return la.Finish() + default: + err := st.unmarshal(la.AssembleValue(), tokSrc) + if err != nil { // return in error if some part of the recursion errored + return err + } + } + } + case tok.TArrClose: + return fmt.Errorf("unexpected arrClose token") + case tok.TNull: + return na.AssignNull() + case tok.TString: + return na.AssignString(st.tk[0].Str) + case tok.TBytes: + return na.AssignBytes(st.tk[0].Bytes) + case tok.TBool: + return na.AssignBool(st.tk[0].Bool) + case tok.TInt: + return na.AssignInt(int(st.tk[0].Int)) // FIXME overflow check + case tok.TUint: + return na.AssignInt(int(st.tk[0].Uint)) // FIXME overflow check + case tok.TFloat64: + return na.AssignFloat(st.tk[0].Float64) + default: + panic("unreachable") + } +} diff --git a/_rsrch/nodesolution/codec/marshal.go b/codec/marshal.go similarity index 93% rename from _rsrch/nodesolution/codec/marshal.go rename to codec/marshal.go index d158792e..67bcbfad 100644 --- a/_rsrch/nodesolution/codec/marshal.go +++ b/codec/marshal.go @@ -6,11 +6,9 @@ import ( "github.com/polydawn/refmt/shared" "github.com/polydawn/refmt/tok" - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) -// FUTURE there are very open questions on how to handle detection and special-track'ing for advLayout nodes when we get to that feature. - // Marshal provides a very general node-to-tokens marshalling feature. // It can handle either cbor or json by being combined with a refmt TokenSink. // diff --git a/_rsrch/nodesolution/codec/unmarshal.go b/codec/unmarshal.go similarity index 92% rename from _rsrch/nodesolution/codec/unmarshal.go rename to codec/unmarshal.go index 504573c4..7a9a4d4d 100644 --- a/_rsrch/nodesolution/codec/unmarshal.go +++ b/codec/unmarshal.go @@ -7,7 +7,7 @@ import ( "github.com/polydawn/refmt/shared" "github.com/polydawn/refmt/tok" - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) // wishlist: if we could reconstruct the ipld.Path of an error while @@ -43,13 +43,11 @@ func Unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource) error { return err } if done && !tk.Type.IsValue() { - panic("unexpected eof") // FIXME this is really awkward, are we sure this can't be avoided? and are we sure done is handled right elsewhere too? + return fmt.Errorf("unexpected eof") } return unmarshal(na, tokSrc, &tk) } -// TODO, yeah, you need a method that does a better oneliner, and that Unmarshal thing above needs a longer name to correspond to its windiness. - // starts with the first token already primed. Necessary to get recursion // to flow right without a peek+unpeek system. func unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token) error { diff --git a/doc.go b/doc.go index e4a6919e..70386703 100644 --- a/doc.go +++ b/doc.go @@ -9,7 +9,7 @@ // Here in the godoc, the first couple of types to look at should be: // // - Node -// - NodeBuilder +// - NodeBuilder (and NodeAssembler) // // These types provide a generic description of the data model. // @@ -27,12 +27,24 @@ // // Particularly interesting subpackages include: // -// - impl/* -- various Node + NodeBuilder implementations -// - encoding/* -- functions for serializing and deserializing Nodes -// - linking/* -- various Link + LinkBuilder implementation +// - node/* -- various Node + NodeBuilder implementations +// - node/basic -- the first Node implementation you should try +// - codec/* -- functions for serializing and deserializing Nodes +// - linking/* -- various Link + LinkBuilder implementations // - traversal -- functions for walking Node graphs (including // automatic link loading) and visiting -// - typed -- Node implementations with constraints -// - fluent -- Node interfaces with streamlined error handling +// - must -- helpful functions for streamlining error handling +// - fluent -- alternative Node interfaces that flip errors to panics +// - schema -- interfaces for working with IPLD Schemas and Nodes +// which use Schema types and constraints +// +// Note that since packages in this interface are the core of the library, +// choices made here maximize correctness and performance -- these choices +// are *not* always the choices that would maximize ergonomics. +// (Ergonomics can come on top; performance generally can't.) +// You can check out the 'must' or 'fluent' packages for more ergonomics; +// 'traversal' provides some ergnomics features for certain uses; +// any use of schemas with codegen tooling will provide more ergnomic options; +// or you can make your own function decorators that do what *you* need. // package ipld diff --git a/encoding/dagcbor/roundtrip_test.go b/encoding/dagcbor/roundtrip_test.go deleted file mode 100644 index fd2972ab..00000000 --- a/encoding/dagcbor/roundtrip_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package dagcbor - -import ( - "bytes" - "testing" - - . "github.com/warpfork/go-wish" - - "github.com/ipld/go-ipld-prime/fluent" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" -) - -var fnb = fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) -var n = fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("plain"), vnb.CreateString("olde string")) - mb.Insert(knb.CreateString("map"), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("one"), vnb.CreateInt(1)) - mb.Insert(knb.CreateString("two"), vnb.CreateInt(2)) - })) - mb.Insert(knb.CreateString("list"), fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateString("three")) - lb.Append(vnb.CreateString("four")) - })) - mb.Insert(knb.CreateString("nested"), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("deeper"), fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateString("things")) - })) - })) -}) -var serial = "\xa4eplainkolde stringcmap\xa2cone\x01ctwo\x02dlist\x82ethreedfourfnested\xa1fdeeper\x81fthings" - -func TestRoundtrip(t *testing.T) { - t.Run("encoding", func(t *testing.T) { - var buf bytes.Buffer - err := Encoder(n, &buf) - Wish(t, err, ShouldEqual, nil) - Wish(t, buf.String(), ShouldEqual, serial) - }) - t.Run("decoding", func(t *testing.T) { - buf := bytes.NewBufferString(serial) - n2, err := Decoder(ipldfree.NodeBuilder(), buf) - Wish(t, err, ShouldEqual, nil) - Wish(t, n2, ShouldEqual, n) - }) -} - -func TestRoundtripSimple(t *testing.T) { - simple := fnb.CreateString("applesauce") - t.Run("encoding", func(t *testing.T) { - var buf bytes.Buffer - err := Encoder(simple, &buf) - Wish(t, err, ShouldEqual, nil) - Wish(t, buf.String(), ShouldEqual, `japplesauce`) - }) - t.Run("decoding", func(t *testing.T) { - buf := bytes.NewBufferString(`japplesauce`) - simple2, err := Decoder(ipldfree.NodeBuilder(), buf) - Wish(t, err, ShouldEqual, nil) - Wish(t, simple2, ShouldEqual, simple) - }) -} diff --git a/encoding/dagjson/roundtripCidlink_test.go b/encoding/dagjson/roundtripCidlink_test.go deleted file mode 100644 index 538503cd..00000000 --- a/encoding/dagjson/roundtripCidlink_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package dagjson - -import ( - "bytes" - "context" - "io" - "testing" - - . "github.com/warpfork/go-wish" - - cid "github.com/ipfs/go-cid" - ipld "github.com/ipld/go-ipld-prime" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" - cidlink "github.com/ipld/go-ipld-prime/linking/cid" -) - -func TestRoundtripCidlink(t *testing.T) { - lb := cidlink.LinkBuilder{cid.Prefix{ - Version: 1, - Codec: 0x0129, - MhType: 0x17, - MhLength: 4, - }} - buf := bytes.Buffer{} - lnk, err := lb.Build(context.Background(), ipld.LinkContext{}, n, - func(ipld.LinkContext) (io.Writer, ipld.StoreCommitter, error) { - return &buf, func(lnk ipld.Link) error { return nil }, nil - }, - ) - Wish(t, err, ShouldEqual, nil) - n2, err := lnk.Load(context.Background(), ipld.LinkContext{}, ipldfree.NodeBuilder(), - func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) { - return bytes.NewBuffer(buf.Bytes()), nil - }, - ) - Wish(t, err, ShouldEqual, nil) - Wish(t, n2, ShouldEqual, n) -} diff --git a/encoding/dagjson/roundtrip_test.go b/encoding/dagjson/roundtrip_test.go deleted file mode 100644 index cbeb54ae..00000000 --- a/encoding/dagjson/roundtrip_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package dagjson - -import ( - "bytes" - "testing" - - . "github.com/warpfork/go-wish" - - "github.com/ipld/go-ipld-prime/fluent" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" -) - -var fnb = fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) -var n = fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("plain"), vnb.CreateString("olde string")) - mb.Insert(knb.CreateString("map"), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("one"), vnb.CreateInt(1)) - mb.Insert(knb.CreateString("two"), vnb.CreateInt(2)) - })) - mb.Insert(knb.CreateString("list"), fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateString("three")) - lb.Append(vnb.CreateString("four")) - })) - mb.Insert(knb.CreateString("nested"), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("deeper"), fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateString("things")) - })) - })) -}) -var serial = `{ - "plain": "olde string", - "map": { - "one": 1, - "two": 2 - }, - "list": [ - "three", - "four" - ], - "nested": { - "deeper": [ - "things" - ] - } -} -` - -func TestRoundtrip(t *testing.T) { - t.Run("encoding", func(t *testing.T) { - var buf bytes.Buffer - err := Encoder(n, &buf) - Wish(t, err, ShouldEqual, nil) - Wish(t, buf.String(), ShouldEqual, serial) - }) - t.Run("decoding", func(t *testing.T) { - buf := bytes.NewBufferString(serial) - n2, err := Decoder(ipldfree.NodeBuilder(), buf) - Wish(t, err, ShouldEqual, nil) - Wish(t, n2, ShouldEqual, n) - }) -} - -func TestRoundtripSimple(t *testing.T) { - simple := fnb.CreateString("applesauce") - t.Run("encoding", func(t *testing.T) { - var buf bytes.Buffer - err := Encoder(simple, &buf) - Wish(t, err, ShouldEqual, nil) - Wish(t, buf.String(), ShouldEqual, `"applesauce"`) - }) - t.Run("decoding", func(t *testing.T) { - buf := bytes.NewBufferString(`"applesauce"`) - simple2, err := Decoder(ipldfree.NodeBuilder(), buf) - Wish(t, err, ShouldEqual, nil) - Wish(t, simple2, ShouldEqual, simple) - }) -} diff --git a/encoding/dagjson/unmarshal.go b/encoding/dagjson/unmarshal.go deleted file mode 100644 index d9a8decd..00000000 --- a/encoding/dagjson/unmarshal.go +++ /dev/null @@ -1,147 +0,0 @@ -package dagjson - -import ( - "fmt" - "math" - - cid "github.com/ipfs/go-cid" - "github.com/polydawn/refmt/shared" - "github.com/polydawn/refmt/tok" - - ipld "github.com/ipld/go-ipld-prime" - cidlink "github.com/ipld/go-ipld-prime/linking/cid" -) - -// This should be identical to the general feature in the parent package, -// except for the `case tok.TMapOpen` block, -// which has dag-json's special sauce for detecting schemafree links. - -func Unmarshal(nb ipld.NodeBuilder, tokSrc shared.TokenSource) (ipld.Node, error) { - var tk tok.Token - done, err := tokSrc.Step(&tk) - if err != nil { - return nil, err - } - if done && !tk.Type.IsValue() { - return nil, err - } - return unmarshal(nb, tokSrc, &tk) -} - -// starts with the first token already primed. Necessary to get recursion -// to flow right without a peek+unpeek system. -func unmarshal(nb ipld.NodeBuilder, tokSrc shared.TokenSource, tk *tok.Token) (ipld.Node, error) { - // FUTURE: check for schema.TypedNodeBuilder that's going to parse a Link (they can slurp any token kind they want). - switch tk.Type { - case tok.TMapOpen: - mb, err := nb.CreateMap() - if err != nil { - return nil, err - } - expectLen := tk.Length - if tk.Length == -1 { - expectLen = math.MaxInt32 - } - observedLen := 0 - var k string - var v ipld.Node - for { - _, err := tokSrc.Step(tk) - if err != nil { - return nil, err - } - switch tk.Type { - case tok.TMapClose: - if expectLen != math.MaxInt32 && observedLen != expectLen { - return nil, fmt.Errorf("unexpected mapClose before declared length") - } - if observedLen == 1 { - // Here's our divergence for dag-json special sauce: - if k == "/" && v.ReprKind() == ipld.ReprKind_String { - // *Abandon* the mapbuilder! We've got a link instead! - str, _ := v.AsString() - elCid, err := cid.Decode(str) - if err != nil { - return nil, err - } - return nb.CreateLink(cidlink.Link{elCid}) - } - } - return mb.Build() - case tok.TString: - // continue - default: - return nil, fmt.Errorf("unexpected %s token while expecting map key", tk.Type) - } - observedLen++ - if observedLen > expectLen { - return nil, fmt.Errorf("unexpected continuation of map elements beyond declared length") - } - k = tk.Str - v, err = Unmarshal(mb.BuilderForValue(k), tokSrc) - if err != nil { - return nil, err - } - kn, err := mb.BuilderForKeys().CreateString(k) - if err != nil { - return nil, fmt.Errorf("value rejected as key: %s", err) - } - if err := mb.Insert(kn, v); err != nil { - return nil, err - } - } - case tok.TMapClose: - return nil, fmt.Errorf("unexpected mapClose token") - case tok.TArrOpen: - lb, err := nb.CreateList() - if err != nil { - return nil, err - } - expectLen := tk.Length - if tk.Length == -1 { - expectLen = math.MaxInt32 - } - i := 0 - for { - _, err := tokSrc.Step(tk) - if err != nil { - return nil, err - } - switch tk.Type { - case tok.TArrClose: - if expectLen != math.MaxInt32 && i != expectLen { - return nil, fmt.Errorf("unexpected arrClose before declared length") - } - return lb.Build() - default: - if i >= expectLen { - return nil, fmt.Errorf("unexpected continuation of array elements beyond declared length") - } - v, err := unmarshal(lb.BuilderForValue(i), tokSrc, tk) - i++ - if err != nil { - return nil, err - } - lb.Append(v) - } - } - case tok.TArrClose: - return nil, fmt.Errorf("unexpected arrClose token") - case tok.TNull: - return nb.CreateNull() - case tok.TString: - return nb.CreateString(tk.Str) - case tok.TBytes: - panic("bytes aren't a token you can get in json; what are you doing?") - case tok.TBool: - return nb.CreateBool(tk.Bool) - case tok.TInt: - return nb.CreateInt(int(tk.Int)) // FIXME overflow check - case tok.TUint: - return nb.CreateInt(int(tk.Uint)) // FIXME overflow check - case tok.TFloat64: - return nb.CreateFloat(tk.Float64) - default: - panic("unreachable") - } -} diff --git a/encoding/marshal.go b/encoding/marshal.go deleted file mode 100644 index bd0c8b66..00000000 --- a/encoding/marshal.go +++ /dev/null @@ -1,132 +0,0 @@ -package encoding - -import ( - "fmt" - - "github.com/polydawn/refmt/shared" - "github.com/polydawn/refmt/tok" - - "github.com/ipld/go-ipld-prime" -) - -// FUTURE there are very open questions on how to handle detection and special-track'ing for advLayout nodes when we get to that feature. - -// Marshal provides a very general node-to-tokens marshalling feature. -// It can handle either cbor or json by being combined with a refmt TokenSink. -// -// It is valid for all the data model types except links, which are only -// supported if the nodes are typed and provide additional information -// to clarify how links should be encoded through their type info. -// (The dag-cbor and dag-json formats can be used if links are of CID -// implementation and need to be encoded in a schemafree way.) -func Marshal(n ipld.Node, sink shared.TokenSink) error { - var tk tok.Token - switch n.ReprKind() { - case ipld.ReprKind_Invalid: - return fmt.Errorf("cannot traverse a node that is undefined") - case ipld.ReprKind_Null: - tk.Type = tok.TNull - _, err := sink.Step(&tk) - return err - case ipld.ReprKind_Map: - // Emit start of map. - tk.Type = tok.TMapOpen - tk.Length = n.Length() - if _, err := sink.Step(&tk); err != nil { - return err - } - // Emit map contents (and recurse). - for itr := n.MapIterator(); !itr.Done(); { - k, v, err := itr.Next() - if err != nil { - return err - } - tk.Type = tok.TString - tk.Str, err = k.AsString() - if err != nil { - return err - } - if _, err := sink.Step(&tk); err != nil { - return err - } - if err := Marshal(v, sink); err != nil { - return err - } - } - // Emit map close. - tk.Type = tok.TMapClose - _, err := sink.Step(&tk) - return err - case ipld.ReprKind_List: - // Emit start of list. - tk.Type = tok.TArrOpen - l := n.Length() - tk.Length = l - if _, err := sink.Step(&tk); err != nil { - return err - } - // Emit list contents (and recurse). - for i := 0; i < l; i++ { - v, err := n.LookupIndex(i) - if err != nil { - return err - } - if err := Marshal(v, sink); err != nil { - return err - } - } - // Emit list close. - tk.Type = tok.TArrClose - _, err := sink.Step(&tk) - return err - case ipld.ReprKind_Bool: - v, err := n.AsBool() - if err != nil { - return err - } - tk.Type = tok.TBool - tk.Bool = v - _, err = sink.Step(&tk) - return err - case ipld.ReprKind_Int: - v, err := n.AsInt() - if err != nil { - return err - } - tk.Type = tok.TInt - tk.Int = int64(v) - _, err = sink.Step(&tk) - return err - case ipld.ReprKind_Float: - v, err := n.AsFloat() - if err != nil { - return err - } - tk.Type = tok.TFloat64 - tk.Float64 = v - _, err = sink.Step(&tk) - return err - case ipld.ReprKind_String: - v, err := n.AsString() - if err != nil { - return err - } - tk.Type = tok.TString - tk.Str = v - _, err = sink.Step(&tk) - return err - case ipld.ReprKind_Bytes: - v, err := n.AsBytes() - if err != nil { - return err - } - tk.Type = tok.TBytes - tk.Bytes = v - _, err = sink.Step(&tk) - return err - case ipld.ReprKind_Link: - return fmt.Errorf("link emission not supported by this codec without a schema! (maybe you want dag-cbor or dag-json)") - default: - panic("unreachable") - } -} diff --git a/encoding/unmarshal.go b/encoding/unmarshal.go deleted file mode 100644 index 1c5415d0..00000000 --- a/encoding/unmarshal.go +++ /dev/null @@ -1,146 +0,0 @@ -package encoding - -import ( - "fmt" - "math" - - "github.com/polydawn/refmt/shared" - "github.com/polydawn/refmt/tok" - - "github.com/ipld/go-ipld-prime" -) - -// wishlist: if we could reconstruct the ipld.Path of an error while -// *unwinding* from that error... that'd be nice. -// (trying to build it proactively would waste tons of allocs on the happy path.) -// we can do this; it just requires well-typed errors and a bunch of work. - -// Tests for all this are in the ipld.Node impl tests! -// They're effectively doing double duty: testing the builders, too. -// (Is that sensible? Should it be refactored? Not sure; maybe!) - -// Unmarshal provides a very general tokens-to-node unmarshalling feature. -// It can handle either cbor or json by being combined with a refmt TokenSink. -// -// It is valid for all the data model types except links, which are only -// supported if the nodes are typed and provide additional information -// to clarify how links should be decoded through their type info. -// (The dag-cbor and dag-json formats can be used if links are of CID -// implementation and need to be decoded in a schemafree way.) -func Unmarshal(nb ipld.NodeBuilder, tokSrc shared.TokenSource) (ipld.Node, error) { - var tk tok.Token - done, err := tokSrc.Step(&tk) - if err != nil { - return nil, err - } - if done && !tk.Type.IsValue() { - return nil, err - } - return unmarshal(nb, tokSrc, &tk) -} - -// starts with the first token already primed. Necessary to get recursion -// to flow right without a peek+unpeek system. -func unmarshal(nb ipld.NodeBuilder, tokSrc shared.TokenSource, tk *tok.Token) (ipld.Node, error) { - // FUTURE: check for schema.TypedNodeBuilder that's going to parse a Link (they can slurp any token kind they want). - switch tk.Type { - case tok.TMapOpen: - mb, err := nb.CreateMap() - if err != nil { - return nil, err - } - expectLen := tk.Length - if tk.Length == -1 { - expectLen = math.MaxInt32 - } - observedLen := 0 - var k string - var v ipld.Node - for { - _, err := tokSrc.Step(tk) - if err != nil { - return nil, err - } - switch tk.Type { - case tok.TMapClose: - if expectLen != math.MaxInt32 && observedLen != expectLen { - return nil, fmt.Errorf("unexpected mapClose before declared length") - } - return mb.Build() - case tok.TString: - // continue - default: - return nil, fmt.Errorf("unexpected %s token while expecting map key", tk.Type) - } - observedLen++ - if observedLen > expectLen { - return nil, fmt.Errorf("unexpected continuation of map elements beyond declared length") - } - k = tk.Str - v, err = Unmarshal(mb.BuilderForValue(k), tokSrc) - if err != nil { - return nil, err - } - kn, err := mb.BuilderForKeys().CreateString(k) - if err != nil { - return nil, fmt.Errorf("value rejected as key: %s", err) - } - if err := mb.Insert(kn, v); err != nil { - return nil, err - } - } - case tok.TMapClose: - return nil, fmt.Errorf("unexpected mapClose token") - case tok.TArrOpen: - lb, err := nb.CreateList() - if err != nil { - return nil, err - } - expectLen := tk.Length - if tk.Length == -1 { - expectLen = math.MaxInt32 - } - i := 0 - for { - _, err := tokSrc.Step(tk) - if err != nil { - return nil, err - } - switch tk.Type { - case tok.TArrClose: - if expectLen != math.MaxInt32 && i != expectLen { - return nil, fmt.Errorf("unexpected arrClose before declared length") - } - return lb.Build() - default: - if i >= expectLen { - return nil, fmt.Errorf("unexpected continuation of array elements beyond declared length") - } - v, err := unmarshal(lb.BuilderForValue(i), tokSrc, tk) - i++ - if err != nil { - return nil, err - } - lb.Append(v) - } - } - case tok.TArrClose: - return nil, fmt.Errorf("unexpected arrClose token") - case tok.TNull: - return nb.CreateNull() - case tok.TString: - return nb.CreateString(tk.Str) - case tok.TBytes: - return nb.CreateBytes(tk.Bytes) - case tok.TBool: - return nb.CreateBool(tk.Bool) - case tok.TInt: - return nb.CreateInt(int(tk.Int)) // FIXME overflow check - case tok.TUint: - return nb.CreateInt(int(tk.Uint)) // FIXME overflow check - case tok.TFloat64: - return nb.CreateFloat(tk.Float64) - default: - panic("unreachable") - } -} diff --git a/errors.go b/errors.go index 46a7fc4b..b63e14f8 100644 --- a/errors.go +++ b/errors.go @@ -10,6 +10,8 @@ import ( // // For example, calling AsString on a map will return ErrWrongKind. // Calling Lookup on an int will similarly return ErrWrongKind. +// +// REVIEW: would it improve clarity to call this 'ErrWrongKindForNodeStyle'? type ErrWrongKind struct { // TypeName may optionally indicate the named type of a node the function // was called on (if the node was typed!), or, may be the empty string. @@ -56,21 +58,12 @@ func (e ErrNotExists) Error() string { return fmt.Sprintf("key not found: %q", e.Segment) } -// ErrInvalidKey may be returned from lookup functions on the Node interface -// when a key is invalid. -// -// Common examples of this are when `Lookup(Node)` is used with a non-string Node; -// typed nodes also introduce other reasons a key may be invalid. -type ErrInvalidKey struct { - Reason string - - // Perhaps schema.ErrNoSuchField could be folded into this? - // Perhaps Reason could be replaced by an enum of "NoSuchField"|"NotAString"|"ConstraintRejected"? - // Might be hard to get rid of the freetext field entirely -- constraints may be nontrivial to describe. +type ErrRepeatedMapKey struct { + Key Node } -func (e ErrInvalidKey) Error() string { - return fmt.Sprintf("invalid key: %s", e.Reason) +func (e ErrRepeatedMapKey) Error() string { + return fmt.Sprintf("cannot repeat map key (\"%s\")", e.Key) } // ErrIteratorOverread is returned when calling 'Next' on a MapIterator or @@ -80,3 +73,10 @@ type ErrIteratorOverread struct{} func (e ErrIteratorOverread) Error() string { return "iterator overread" } + +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/fluent/fluentBuilder.go b/fluent/fluentBuilder.go new file mode 100644 index 00000000..97c560bc --- /dev/null +++ b/fluent/fluentBuilder.go @@ -0,0 +1,193 @@ +/* + The fluent package offers helper utilities for using NodeAssembler + more tersely by providing an interface that handles all errors for you, + and allows use of closures for any recursive assembly + so that creating trees of data results in indentation for legibility. + + Note that the fluent package creates wrapper objects in order to provide + the API conveniences that it does, and this comes at some cost to performance. + If you're optimizing for performance, using the fluent interfaces may be inadvisable. + However, as with any performance questions, benchmark before making decisions; + its entirely possible that your performance bottlenecks will be elsewhere + and there's no reason to deny yourself syntactic sugar if the costs don't + detectably affect the bottom line. +*/ +package fluent + +import ( + ipld "github.com/ipld/go-ipld-prime" +) + +func Build(ns ipld.NodeStyle, fn func(NodeAssembler)) (ipld.Node, error) { + nb := ns.NewBuilder() + fna := WrapAssembler(nb) + err := Recover(func() { + fn(fna) + }) + return nb.Build(), err +} + +func MustBuild(ns ipld.NodeStyle, fn func(NodeAssembler)) ipld.Node { + nb := ns.NewBuilder() + fn(WrapAssembler(nb)) + return nb.Build() +} +func MustBuildMap(ns ipld.NodeStyle, sizeHint int, fn func(MapNodeAssembler)) ipld.Node { + return MustBuild(ns, func(fna NodeAssembler) { fna.CreateMap(sizeHint, fn) }) +} +func MustBuildList(ns ipld.NodeStyle, sizeHint int, fn func(ListNodeAssembler)) ipld.Node { + return MustBuild(ns, func(fna NodeAssembler) { fna.CreateList(sizeHint, fn) }) +} + +func WrapAssembler(na ipld.NodeAssembler) NodeAssembler { + return &nodeAssembler{na} +} + +// NodeAssembler is the same as the interface in the core package, except: +// instead of returning errors, any error will cause panic +// (and you can collect these with `fluent.Recover`); +// and all recursive operations take a function as a parameter, +// within which you will receive another {Map,List,}NodeAssembler. +type NodeAssembler interface { + CreateMap(sizeHint int, fn func(MapNodeAssembler)) + CreateList(sizeHint int, fn func(ListNodeAssembler)) + AssignNull() + AssignBool(bool) + AssignInt(int) + AssignFloat(float64) + AssignString(string) + AssignBytes([]byte) + AssignLink(ipld.Link) + AssignNode(ipld.Node) + + Style() ipld.NodeStyle +} + +// MapNodeAssembler is the same as the interface in the core package, except: +// instead of returning errors, any error will cause panic +// (and you can collect these with `fluent.Recover`); +// and all recursive operations take a function as a parameter, +// within which you will receive another {Map,List,}NodeAssembler. +type MapNodeAssembler interface { + AssembleKey() NodeAssembler + AssembleValue() NodeAssembler + + AssembleDirectly(k string) NodeAssembler + + KeyStyle() ipld.NodeStyle + ValueStyle(k string) ipld.NodeStyle +} + +// ListNodeAssembler is the same as the interface in the core package, except: +// instead of returning errors, any error will cause panic +// (and you can collect these with `fluent.Recover`); +// and all recursive operations take a function as a parameter, +// within which you will receive another {Map,List,}NodeAssembler. +type ListNodeAssembler interface { + AssembleValue() NodeAssembler + + ValueStyle() ipld.NodeStyle +} + +type nodeAssembler struct { + na ipld.NodeAssembler +} + +func (fna *nodeAssembler) CreateMap(sizeHint int, fn func(MapNodeAssembler)) { + if ma, err := fna.na.BeginMap(sizeHint); err != nil { + panic(Error{err}) + } else { + fn(&mapNodeAssembler{ma}) + if err := ma.Finish(); err != nil { + panic(Error{err}) + } + } +} +func (fna *nodeAssembler) CreateList(sizeHint int, fn func(ListNodeAssembler)) { + if la, err := fna.na.BeginList(sizeHint); err != nil { + panic(Error{err}) + } else { + fn(&listNodeAssembler{la}) + if err := la.Finish(); err != nil { + panic(Error{err}) + } + } +} +func (fna *nodeAssembler) AssignNull() { + if err := fna.na.AssignNull(); err != nil { + panic(Error{err}) + } +} +func (fna *nodeAssembler) AssignBool(v bool) { + if err := fna.na.AssignBool(v); err != nil { + panic(Error{err}) + } +} +func (fna *nodeAssembler) AssignInt(v int) { + if err := fna.na.AssignInt(v); err != nil { + panic(Error{err}) + } +} +func (fna *nodeAssembler) AssignFloat(v float64) { + if err := fna.na.AssignFloat(v); err != nil { + panic(Error{err}) + } +} +func (fna *nodeAssembler) AssignString(v string) { + if err := fna.na.AssignString(v); err != nil { + panic(Error{err}) + } +} +func (fna *nodeAssembler) AssignBytes(v []byte) { + if err := fna.na.AssignBytes(v); err != nil { + panic(Error{err}) + } +} +func (fna *nodeAssembler) AssignLink(v ipld.Link) { + if err := fna.na.AssignLink(v); err != nil { + panic(Error{err}) + } +} +func (fna *nodeAssembler) AssignNode(v ipld.Node) { + if err := fna.na.AssignNode(v); err != nil { + panic(Error{err}) + } +} +func (fna *nodeAssembler) Style() ipld.NodeStyle { + return fna.na.Style() +} + +type mapNodeAssembler struct { + ma ipld.MapNodeAssembler +} + +func (fma *mapNodeAssembler) AssembleKey() NodeAssembler { + return &nodeAssembler{fma.ma.AssembleKey()} +} +func (fma *mapNodeAssembler) AssembleValue() NodeAssembler { + return &nodeAssembler{fma.ma.AssembleValue()} +} +func (fma *mapNodeAssembler) AssembleDirectly(k string) NodeAssembler { + va, err := fma.ma.AssembleDirectly(k) + if err != nil { + panic(Error{err}) + } + return &nodeAssembler{va} +} +func (fma *mapNodeAssembler) KeyStyle() ipld.NodeStyle { + return fma.ma.KeyStyle() +} +func (fma *mapNodeAssembler) ValueStyle(k string) ipld.NodeStyle { + return fma.ma.ValueStyle(k) +} + +type listNodeAssembler struct { + la ipld.ListNodeAssembler +} + +func (fla *listNodeAssembler) AssembleValue() NodeAssembler { + return &nodeAssembler{fla.la.AssembleValue()} +} +func (fla *listNodeAssembler) ValueStyle() ipld.NodeStyle { + return fla.la.ValueStyle() +} diff --git a/fluent/fluentBuilder_test.go b/fluent/fluentBuilder_test.go new file mode 100644 index 00000000..a08e4145 --- /dev/null +++ b/fluent/fluentBuilder_test.go @@ -0,0 +1,73 @@ +package fluent_test + +import ( + "testing" + + . "github.com/warpfork/go-wish" + + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/fluent" + "github.com/ipld/go-ipld-prime/must" + basicnode "github.com/ipld/go-ipld-prime/node/basic" +) + +func TestBuild(t *testing.T) { + t.Run("scalar build should work", func(t *testing.T) { + n := fluent.MustBuild(basicnode.Style__String{}, func(fna fluent.NodeAssembler) { + fna.AssignString("fine") + }) + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_String) + v2, err := n.AsString() + Wish(t, err, ShouldEqual, nil) + Wish(t, v2, ShouldEqual, "fine") + }) + t.Run("map build should work", func(t *testing.T) { + n := fluent.MustBuild(basicnode.Style__Map{}, func(fna fluent.NodeAssembler) { + fna.CreateMap(3, func(fma fluent.MapNodeAssembler) { + fma.AssembleDirectly("k1").AssignString("fine") + fma.AssembleDirectly("k2").AssignString("super") + fma.AssembleDirectly("k3").CreateMap(3, func(fma fluent.MapNodeAssembler) { + fma.AssembleDirectly("k31").AssignString("thanks") + fma.AssembleDirectly("k32").AssignString("for") + fma.AssembleDirectly("k33").AssignString("asking") + }) + }) + }) + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) + Wish(t, n.Length(), ShouldEqual, 3) + Wish(t, must.String(must.Node(n.LookupString("k1"))), ShouldEqual, "fine") + Wish(t, must.String(must.Node(n.LookupString("k2"))), ShouldEqual, "super") + n = must.Node(n.LookupString("k3")) + Wish(t, n.Length(), ShouldEqual, 3) + Wish(t, must.String(must.Node(n.LookupString("k31"))), ShouldEqual, "thanks") + Wish(t, must.String(must.Node(n.LookupString("k32"))), ShouldEqual, "for") + Wish(t, must.String(must.Node(n.LookupString("k33"))), ShouldEqual, "asking") + }) + t.Run("list build should work", func(t *testing.T) { + n := fluent.MustBuild(basicnode.Style__List{}, func(fna fluent.NodeAssembler) { + fna.CreateList(1, func(fla fluent.ListNodeAssembler) { + fla.AssembleValue().CreateList(1, func(fla fluent.ListNodeAssembler) { + fla.AssembleValue().CreateList(1, func(fla fluent.ListNodeAssembler) { + fla.AssembleValue().CreateList(1, func(fla fluent.ListNodeAssembler) { + fla.AssembleValue().AssignInt(2) + }) + }) + }) + }) + }) + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_List) + Wish(t, n.Length(), ShouldEqual, 1) + n = must.Node(n.LookupIndex(0)) + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_List) + Wish(t, n.Length(), ShouldEqual, 1) + n = must.Node(n.LookupIndex(0)) + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_List) + Wish(t, n.Length(), ShouldEqual, 1) + n = must.Node(n.LookupIndex(0)) + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_List) + Wish(t, n.Length(), ShouldEqual, 1) + n = must.Node(n.LookupIndex(0)) + Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Int) + Wish(t, must.Int(n), ShouldEqual, 2) + }) +} diff --git a/fluent/fluentNode.go b/fluent/fluentNode.go deleted file mode 100644 index 91981fd6..00000000 --- a/fluent/fluentNode.go +++ /dev/null @@ -1,223 +0,0 @@ -package fluent - -import ( - "github.com/ipld/go-ipld-prime" -) - -// fluent.Node is an interface with all the same methods names as ipld.Node, -// but all of the methods return only the thing itself, and not error values -- -// which makes chaining calls easier. -// -// The very first error value encountered will be stored, and can be viewed later. -// After an error is encountered, all subsequent lookup methods will -// silently return the same error-storing node. -// Any of the terminal scalar-returning methods will panic if an error is stored. -// (The fluent.Recover function can be used to nicely gather these panics.) -type Node interface { - ReprKind() ipld.ReprKind - LookupString(path string) Node - Lookup(key Node) Node - LookupIndex(idx int) Node - MapIterator() MapIterator - ListIterator() ListIterator - Length() int - IsNull() bool - AsBool() bool - AsInt() int - AsFloat() float64 - AsString() string - AsBytes() []byte - AsLink() ipld.Link - GetError() error -} - -func WrapNode(n ipld.Node) Node { - return node{n, nil} -} - -type node struct { - n ipld.Node - err error -} - -type Error struct { - Err error -} - -func (e Error) Error() string { - return e.Err.Error() -} - -func (n node) GetError() error { - return n.err -} -func (n node) ReprKind() ipld.ReprKind { - if n.err != nil { - panic(Error{n.err}) - } - return n.n.ReprKind() -} -func (n node) LookupString(path string) Node { - if n.err != nil { - return n - } - v, err := n.n.LookupString(path) - if err != nil { - return node{nil, err} - } - return node{v, nil} -} -func (n node) Lookup(key Node) Node { - if n.err != nil { - return n - } - v, err := n.n.Lookup(key.(node).n) // hacky. needs fluent.Node needs unbox method. - if err != nil { - return node{nil, err} - } - return node{v, nil} -} -func (n node) LookupIndex(idx int) Node { - if n.err != nil { - return n - } - v, err := n.n.LookupIndex(idx) - if err != nil { - return node{nil, err} - } - return node{v, nil} -} -func (n node) LookupSegment(seg ipld.PathSegment) Node { - if n.err != nil { - return n - } - v, err := n.n.LookupSegment(seg) - if err != nil { - return node{nil, err} - } - return node{v, nil} -} -func (n node) MapIterator() MapIterator { - if n.err != nil { - panic(Error{n.err}) - } - return &mapIterator{n.n.MapIterator()} -} -func (n node) ListIterator() ListIterator { - if n.err != nil { - panic(Error{n.err}) - } - return &listIterator{n.n.ListIterator()} -} -func (n node) Length() int { - if n.err != nil { - panic(Error{n.err}) - } - return n.n.Length() -} -func (n node) IsNull() bool { - if n.err != nil { - panic(Error{n.err}) - } - return n.n.IsNull() -} -func (n node) AsBool() bool { - if n.err != nil { - panic(Error{n.err}) - } - v, err := n.n.AsBool() - if err != nil { - panic(Error{err}) - } - return v -} -func (n node) AsInt() int { - if n.err != nil { - panic(Error{n.err}) - } - v, err := n.n.AsInt() - if err != nil { - panic(Error{err}) - } - return v -} -func (n node) AsFloat() float64 { - if n.err != nil { - panic(Error{n.err}) - } - v, err := n.n.AsFloat() - if err != nil { - panic(Error{err}) - } - return v -} -func (n node) AsString() string { - if n.err != nil { - panic(Error{n.err}) - } - v, err := n.n.AsString() - if err != nil { - panic(Error{err}) - } - return v -} -func (n node) AsBytes() []byte { - if n.err != nil { - panic(Error{n.err}) - } - v, err := n.n.AsBytes() - if err != nil { - panic(Error{err}) - } - return v -} -func (n node) AsLink() ipld.Link { - if n.err != nil { - panic(Error{n.err}) - } - v, err := n.n.AsLink() - if err != nil { - panic(Error{err}) - } - return v -} - -type MapIterator interface { - Next() (key Node, value Node) - Done() bool -} - -type mapIterator struct { - d ipld.MapIterator -} - -func (itr *mapIterator) Next() (Node, Node) { - k, v, err := itr.d.Next() - if err != nil { - panic(Error{err}) - } - return node{k, nil}, node{v, nil} -} -func (itr *mapIterator) Done() bool { - return itr.d.Done() -} - -type ListIterator interface { - Next() (idx int, value Node) - Done() bool -} - -type listIterator struct { - d ipld.ListIterator -} - -func (itr *listIterator) Next() (int, Node) { - idx, v, err := itr.d.Next() - if err != nil { - panic(Error{err}) - } - return idx, node{v, nil} -} -func (itr *listIterator) Done() bool { - return itr.d.Done() -} diff --git a/fluent/fluentNodeBuilder.go b/fluent/fluentNodeBuilder.go deleted file mode 100644 index 95352f18..00000000 --- a/fluent/fluentNodeBuilder.go +++ /dev/null @@ -1,220 +0,0 @@ -package fluent - -import ( - "github.com/ipld/go-ipld-prime" -) - -type NodeBuilder interface { - CreateMap(MapBuildingClosure) ipld.Node - AmendMap(MapBuildingClosure) ipld.Node - CreateList(ListBuildingClosure) ipld.Node - AmendList(ListBuildingClosure) ipld.Node - CreateNull() ipld.Node - CreateBool(bool) ipld.Node - CreateInt(int) ipld.Node - CreateFloat(float64) ipld.Node - CreateString(string) ipld.Node - CreateBytes([]byte) ipld.Node - CreateLink(ipld.Link) ipld.Node -} - -type MapBuilder interface { - Insert(k, v ipld.Node) - Delete(k ipld.Node) -} - -type ListBuilder interface { - AppendAll([]ipld.Node) - Append(v ipld.Node) - Set(idx int, v ipld.Node) -} - -// MapBuildingClosure is the signiture of a function which builds a Node of kind map. -// -// The MapBuilder parameter is used to accumulate the new Node for the -// duration of the function; and when the function returns, that builder -// will be invoked. (In other words, there's no need to call `Build` within -// the closure itself -- and correspondingly, note the lack of return value.) -// -// Additional NodeBuilder handles are provided for building keys and values. -// These are used when handling typed Node implementations, since in that -// case they may contain behavior related to the type contracts. -// (For untyped nodes, this is degenerate: these builders are not -// distinct from the parent builder driving this closure.) -// -// REVIEW : whether 'knb' is needed. Not sure, and there are other pending -// discussions on this. (It's mostly a concern about having a place to do -// validation on construction; but it's possible we can solve this without -// additional Nodes and Builders by making that validation the responsibility -// of the inside of the mb.Insert method; but will this compose well, and -// will it convey the appropriate times to do the validations correctly? -// Is 'knb' relevant even if that last question is 'no'? If a concern is -// to avoid double-validations, that argues for `mb.Insert(Node, Node)` over -// `mb.Insert(string, Node)`, but if avoiding double-validations, that means -// we already have a Node and don't need 'knb' to get one. ... Design!) -type MapBuildingClosure func(mb MapBuilder, knb NodeBuilder, vnb NodeBuilder) - -// ListBuildingClosure is the signiture of a function which builds a Node of kind list. -// -// The ListBuilder parameter is used to accumulate the new Node for the -// duration of the function; and when the function returns, that builder -// will be invoked. (In other words, there's no need to call `Build` within -// the closure itself -- and correspondingly, note the lack of return value.) -// -// Additional NodeBuilder handles are provided for building the values. -// These are used when handling typed Node implementations, since in that -// case they may contain behavior related to the type contracts. -// (For untyped nodes, this is degenerate: the 'vnb' builder is not -// distinct from the parent builder driving this closure.) -type ListBuildingClosure func(lb ListBuilder, vnb NodeBuilder) - -func WrapNodeBuilder(nb ipld.NodeBuilder) NodeBuilder { - return &nodeBuilder{nb} -} - -type nodeBuilder struct { - nb ipld.NodeBuilder -} - -func (nb *nodeBuilder) CreateMap(fn MapBuildingClosure) ipld.Node { - mb, err := nb.nb.CreateMap() - if err != nil { - panic(Error{err}) - } - fn(mapBuilder{mb}, nb, nb) // FUTURE: check for schema.TypedNodeBuilder; need to specialize latter params before calling down if so. - n, err := mb.Build() - if err != nil { - panic(Error{err}) - } - return n -} -func (nb *nodeBuilder) AmendMap(fn MapBuildingClosure) ipld.Node { - mb, err := nb.nb.AmendMap() - if err != nil { - panic(Error{err}) - } - fn(mapBuilder{mb}, nb, nb) // FUTURE: check for schema.TypedNodeBuilder; need to specialize latter params before calling down if so. - n, err := mb.Build() - if err != nil { - panic(Error{err}) - } - return n -} -func (nb *nodeBuilder) CreateList(fn ListBuildingClosure) ipld.Node { - lb, err := nb.nb.CreateList() - if err != nil { - panic(Error{err}) - } - fn(listBuilder{lb}, nb) // FUTURE: check for schema.TypedNodeBuilder; need to specialize latter params before calling down if so. - n, err := lb.Build() - if err != nil { - panic(Error{err}) - } - return n -} -func (nb *nodeBuilder) AmendList(fn ListBuildingClosure) ipld.Node { - lb, err := nb.nb.AmendList() - if err != nil { - panic(Error{err}) - } - fn(listBuilder{lb}, nb) // FUTURE: check for schema.TypedNodeBuilder; need to specialize latter params before calling down if so. - n, err := lb.Build() - if err != nil { - panic(Error{err}) - } - return n -} -func (nb *nodeBuilder) CreateNull() ipld.Node { - n, err := nb.nb.CreateNull() - if err != nil { - panic(Error{err}) - } - return n -} -func (nb *nodeBuilder) CreateBool(v bool) ipld.Node { - n, err := nb.nb.CreateBool(v) - if err != nil { - panic(Error{err}) - } - return n -} -func (nb *nodeBuilder) CreateInt(v int) ipld.Node { - n, err := nb.nb.CreateInt(v) - if err != nil { - panic(Error{err}) - } - return n -} -func (nb *nodeBuilder) CreateFloat(v float64) ipld.Node { - n, err := nb.nb.CreateFloat(v) - if err != nil { - panic(Error{err}) - } - return n -} -func (nb *nodeBuilder) CreateString(v string) ipld.Node { - n, err := nb.nb.CreateString(v) - if err != nil { - panic(Error{err}) - } - return n -} -func (nb *nodeBuilder) CreateBytes(v []byte) ipld.Node { - n, err := nb.nb.CreateBytes(v) - if err != nil { - panic(Error{err}) - } - return n -} -func (nb *nodeBuilder) CreateLink(v ipld.Link) ipld.Node { - n, err := nb.nb.CreateLink(v) - if err != nil { - panic(Error{err}) - } - return n -} - -type mapBuilder struct { - ipld.MapBuilder -} - -func (mb mapBuilder) Insert(k, v ipld.Node) { - if err := mb.MapBuilder.Insert(k, v); err != nil { - if err != nil { - panic(Error{err}) - } - } -} -func (mb mapBuilder) Delete(k ipld.Node) { - if err := mb.MapBuilder.Delete(k); err != nil { - if err != nil { - panic(Error{err}) - } - } -} - -type listBuilder struct { - ipld.ListBuilder -} - -func (lb listBuilder) AppendAll(vs []ipld.Node) { - if err := lb.ListBuilder.AppendAll(vs); err != nil { - if err != nil { - panic(Error{err}) - } - } -} -func (lb listBuilder) Append(v ipld.Node) { - if err := lb.ListBuilder.Append(v); err != nil { - if err != nil { - panic(Error{err}) - } - } -} -func (lb listBuilder) Set(idx int, v ipld.Node) { - if err := lb.ListBuilder.Set(idx, v); err != nil { - if err != nil { - panic(Error{err}) - } - } -} diff --git a/fluent/fluentRecover.go b/fluent/fluentRecover.go index ebc1cf26..dec9693a 100644 --- a/fluent/fluentRecover.go +++ b/fluent/fluentRecover.go @@ -1,5 +1,13 @@ package fluent +type Error struct { + Err error +} + +func (e Error) Error() string { + return e.Err.Error() +} + // Recover invokes a function within a panic-recovering context, and returns // any raised fluent.Error values; any other values are re-panicked. // diff --git a/fluent/fluentRecover_test.go b/fluent/fluentRecover_test.go index f4fbb4d5..2ddb7266 100644 --- a/fluent/fluentRecover_test.go +++ b/fluent/fluentRecover_test.go @@ -1,4 +1,4 @@ -package fluent +package fluent_test import ( "testing" @@ -6,25 +6,29 @@ import ( . "github.com/warpfork/go-wish" ipld "github.com/ipld/go-ipld-prime" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" + "github.com/ipld/go-ipld-prime/fluent" + basicnode "github.com/ipld/go-ipld-prime/node/basic" ) func TestRecover(t *testing.T) { - t.Run("simple lookup error should capture", func(t *testing.T) { + t.Run("simple build error should capture", func(t *testing.T) { Wish(t, - Recover(func() { - WrapNode(&ipldfree.Node{}).LookupIndex(0).AsString() + fluent.Recover(func() { + fluent.MustBuild(basicnode.Style__String{}, func(fna fluent.NodeAssembler) { + fna.AssignInt(9) + }) t.Fatal("should not be reached") }), ShouldEqual, - Error{ipld.ErrWrongKind{MethodName: "LookupIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Invalid}}, + fluent.Error{ipld.ErrWrongKind{TypeName: "string", MethodName: "AssignInt", AppropriateKind: ipld.ReprKindSet_JustInt, ActualKind: ipld.ReprKind_String}}, ) }) - t.Run("correct lookup should return nil", func(t *testing.T) { + t.Run("correct build should return nil", func(t *testing.T) { Wish(t, - Recover(func() { - n, _ := ipldfree.NodeBuilder().CreateString("foo") - WrapNode(n).AsString() + fluent.Recover(func() { + fluent.MustBuild(basicnode.Style__String{}, func(fna fluent.NodeAssembler) { + fna.AssignString("fine") + }) }), ShouldEqual, nil, @@ -34,7 +38,7 @@ func TestRecover(t *testing.T) { Wish(t, func() (r interface{}) { defer func() { r = recover() }() - Recover(func() { + fluent.Recover(func() { panic("fuqawds") }) return diff --git a/fluent/fluentUtilities.go b/fluent/fluentUtilities.go deleted file mode 100644 index 8d703cbe..00000000 --- a/fluent/fluentUtilities.go +++ /dev/null @@ -1,13 +0,0 @@ -package fluent - -// AllKeyStrings is a shorthand to iterate a map node and collect all the keys -// (and convert them to strings), returning them in a slice. -func AllKeyStrings(n Node) []string { - itr := n.MapIterator() - res := make([]string, n.Length()) - for i := 0; !itr.Done(); i++ { - k, _ := itr.Next() - res[i] = k.AsString() - } - return res -} diff --git a/impl/bind/boundNode.go b/impl/bind/boundNode.go deleted file mode 100644 index 9ee4ea19..00000000 --- a/impl/bind/boundNode.go +++ /dev/null @@ -1,256 +0,0 @@ -package ipldbind - -import ( - "fmt" - "reflect" - - "github.com/ipld/go-ipld-prime" - "github.com/polydawn/refmt/obj/atlas" -) - -var ( - _ ipld.Node = Node{} -) - -/* - Node binds to some Go object in memory, using the definitions provided - by refmt's object atlasing tools. - - This binding does not provide a serialization valid for hashing; to - compute a CID, you'll have to convert to another kind of node. - If you're not sure which kind serializable node to use, try `ipldcbor.Node`. -*/ -type Node struct { - kind ipld.ReprKind // compute during bind - bound reflect.Value // should already be ptr-unwrapped - atlas atlas.Atlas -} - -/* - Bind binds any go value into being interfacable as a Node, using the provided - atlas to understand how to traverse it. -*/ -func Bind(bindme interface{}, atl atlas.Atlas) ipld.Node { - rv := reflect.ValueOf(bindme) - for rv.Kind() == reflect.Ptr { - rv = rv.Elem() - } - return Node{ - kind: determineReprKind(rv), - bound: rv, - atlas: atl, - } -} - -func determineReprKind(rv reflect.Value) ipld.ReprKind { - // TODO also handle nils - switch rv.Type().Kind() { - case reflect.Bool: - return ipld.ReprKind_Bool - case reflect.String: - return ipld.ReprKind_String - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return ipld.ReprKind_Int - case reflect.Float32, reflect.Float64: - return ipld.ReprKind_Float - case reflect.Complex64, reflect.Complex128: - panic(fmt.Errorf("invalid ipldbind.Node: ipld has no concept for complex numbers")) - case reflect.Array, reflect.Slice: - return ipld.ReprKind_List - case reflect.Map, reflect.Struct: - return ipld.ReprKind_Map - case reflect.Interface: - determineReprKind(rv.Elem()) - case reflect.Chan, reflect.Func, reflect.UnsafePointer: - panic(fmt.Errorf("invalid ipldbind.Node: cannot atlas over type %q; it's a %v", rv.Type().Name(), rv.Kind())) - case reflect.Ptr: - // might've already been traversed during bind, but interface path can find more. - determineReprKind(rv.Elem()) - } - panic("unreachable") -} - -func (n Node) ReprKind() ipld.ReprKind { - return n.kind -} - -func (Node) IsUndefined() bool { - return false -} -func (n Node) IsNull() bool { - return n.bound.IsNil() -} -func (n Node) AsBool() (v bool, _ error) { - reflect.ValueOf(&v).Elem().Set(n.bound) - return -} -func (n Node) AsInt() (v int, _ error) { - reflect.ValueOf(&v).Elem().Set(n.bound) - return -} -func (n Node) AsFloat() (v float64, _ error) { - reflect.ValueOf(&v).Elem().Set(n.bound) - return -} -func (n Node) AsString() (v string, _ error) { - reflect.ValueOf(&v).Elem().Set(n.bound) - return -} -func (n Node) AsBytes() (v []byte, _ error) { - reflect.ValueOf(&v).Elem().Set(n.bound) - return -} -func (n Node) AsLink() (v ipld.Link, _ error) { - reflect.ValueOf(&v).Elem().Set(n.bound) - return -} - -func (n Node) NodeBuilder() ipld.NodeBuilder { - panic("NYI") -} - -func (n Node) MapIterator() ipld.MapIterator { - panic("NYI") -} - -func (n Node) ListIterator() ipld.ListIterator { - panic("NYI") -} - -func (n Node) Length() int { - panic("NYI") -} - -func (n Node) LookupString(pth string) (ipld.Node, error) { - v := n.bound - - // Traverse. - // Honor any atlent overrides if present; - // Use kind-based fallbacks if necessary. - atlent, exists := n.atlas.Get(reflect.ValueOf(v.Type()).Pointer()) - if exists { - switch { - case atlent.MarshalTransformFunc != nil: - panic(fmt.Errorf("invalid ipldbind.Node: type %q atlas specifies transform, but ipld doesn't support this power level", v.Type().Name())) - case atlent.StructMap != nil: - for _, fe := range atlent.StructMap.Fields { - if fe.SerialName == pth { - v = fe.ReflectRoute.TraverseToValue(v) - break - } - } - return Node{}, fmt.Errorf("traverse failed: type %q has no field named %q", v.Type().Name(), pth) - case atlent.UnionKeyedMorphism != nil: - panic(fmt.Errorf("invalid ipldbind.Node: type %q atlas specifies union, but ipld doesn't know how to make sense of this", v.Type().Name())) - case atlent.MapMorphism != nil: - v = v.MapIndex(reflect.ValueOf(pth)) - default: - panic("unreachable") - } - } else { - switch v.Type().Kind() { - case // primitives: set them - reflect.Bool, - reflect.String, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, - reflect.Float32, reflect.Float64, - reflect.Complex64, reflect.Complex128: - panic(fmt.Errorf("invalid ipldbind.Node: atlas for type %q is union; ipld doesn't know how to make sense of this", v.Type().Name())) - case // recursives: wrap in node - reflect.Array, - reflect.Slice, - reflect.Map, - reflect.Struct, - reflect.Interface: - return Node{determineReprKind(v), v, n.atlas}, nil - case // esotera: reject with panic - reflect.Chan, - reflect.Func, - reflect.UnsafePointer: - panic(fmt.Errorf("invalid ipldbind.Node: cannot atlas over type %q; it's a %v", v.Type().Name(), v.Kind())) - case // pointers: should've already been unwrapped - reflect.Ptr: - panic("unreachable") - } - } - - // Unwrap any pointers. - for v.Kind() == reflect.Ptr { - v = v.Elem() - } - - // Assign into the result. - // Either assign the result directly (for primitives) - // Or wrap with a Node and assign that (for recursives). - // TODO decide what to do with typedef'd primitives. - switch v.Type().Kind() { - case // primitives: wrap in node - reflect.Bool, - reflect.String, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, - reflect.Float32, reflect.Float64, - reflect.Complex64, reflect.Complex128: - return Node{determineReprKind(v), v, n.atlas}, nil - case // recursives: wrap in node - reflect.Array, - reflect.Slice, - reflect.Map, - reflect.Struct, - reflect.Interface: - return Node{determineReprKind(v), v, n.atlas}, nil - case // esotera: reject with panic - reflect.Chan, - reflect.Func, - reflect.UnsafePointer: - panic(fmt.Errorf("invalid ipldbind.Node: cannot atlas over type %q; it's a %v", v.Type().Name(), v.Kind())) - case // pointers: should've already been unwrapped - reflect.Ptr: - panic("unreachable") - default: - panic("unreachable") - } -} - -func (n Node) Lookup(key ipld.Node) (ipld.Node, error) { - switch n.kind { - case ipld.ReprKind_Map: - ks, err := key.AsString() - if err != nil { - return nil, ipld.ErrInvalidKey{"got " + key.ReprKind().String() + ", need string"} - } - return n.LookupString(ks) - default: - return nil, ipld.ErrWrongKind{MethodName: "Lookup", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: n.kind} - } -} - -func (n Node) LookupIndex(idx int) (ipld.Node, error) { - panic("NYI") -} - -func (n Node) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - switch n.kind { - case ipld.ReprKind_Map: - return n.LookupString(seg.String()) - case ipld.ReprKind_List: - idx, err := seg.Index() - if err != nil { - return nil, err - } - return n.LookupIndex(idx) - case ipld.ReprKind_Invalid, - ipld.ReprKind_Null, - ipld.ReprKind_Bool, - ipld.ReprKind_String, - ipld.ReprKind_Bytes, - ipld.ReprKind_Int, - ipld.ReprKind_Float, - ipld.ReprKind_Link: - return nil, ipld.ErrWrongKind{MethodName: "LookupSegment", AppropriateKind: ipld.ReprKindSet_Recursive, ActualKind: n.kind} - default: - panic("unreachable") - } -} diff --git a/impl/bind/boundNodeTokenizing.go b/impl/bind/boundNodeTokenizing.go deleted file mode 100644 index f73bc38a..00000000 --- a/impl/bind/boundNodeTokenizing.go +++ /dev/null @@ -1,9 +0,0 @@ -package ipldbind - -import ( - "github.com/polydawn/refmt/shared" -) - -func (n Node) PushTokens(sink shared.TokenSink) error { - panic("TODO") -} diff --git a/impl/bind/boundNode_test.go b/impl/bind/boundNode_test.go deleted file mode 100644 index 5bb1ba7d..00000000 --- a/impl/bind/boundNode_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package ipldbind - -import ( - "testing" - - "github.com/polydawn/refmt/obj/atlas" -) - -// n.b. we can't use any of the standard spec tests yet because we -// need a MutableNode implementation. -// ... and even then, we're gonna have a fun ride; we need *more* tests -// for everything that's a MutableNode bound over *anything but* -// an interface wildcard. Woofdah. - -func Test(t *testing.T) { - type tFoo struct { - Bar string - Baz string - } - atl := atlas.MustBuild( - atlas.BuildEntry(tFoo{}).StructMap().Autogenerate().Complete(), - ) - n := Bind(tFoo{"one", "two"}, atl) - _ = n -} diff --git a/impl/cbor/cborNode.go b/impl/cbor/cborNode.go deleted file mode 100644 index dab5f109..00000000 --- a/impl/cbor/cborNode.go +++ /dev/null @@ -1,16 +0,0 @@ -package ipldcbor - -//import ( -// "github.com/ipld/go-ipld-prime" -//) - -var ( -//_ ipld.Node = &Node{} -//_ ipld.SerializableNode = &Node{} -) - -/* - Node in ipldcbor is implemented // ... if you want to hash it or write it: both are better streamed (especially the former) -*/ -type Node struct { -} diff --git a/impl/free/bench_test.go b/impl/free/bench_test.go deleted file mode 100644 index 1dfd540e..00000000 --- a/impl/free/bench_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package ipldfree - -import ( - "strconv" - "testing" - - ipld "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/tests" -) - -var sink interface{} - -func buildMapStrIntN3() ipld.Node { - nb := NodeBuilder() - mb, err := nb.CreateMap() - mustNotError(err) - kb := mb.BuilderForKeys() - vb := mb.BuilderForValue("") - mustNotError(mb.Insert(mustNode(kb.CreateString("whee")), mustNode(vb.CreateInt(1)))) - mustNotError(mb.Insert(mustNode(kb.CreateString("woot")), mustNode(vb.CreateInt(2)))) - mustNotError(mb.Insert(mustNode(kb.CreateString("waga")), mustNode(vb.CreateInt(3)))) - return mustNode(mb.Build()) -} - -func BenchmarkMap3nFeedGenericMapSimpleKeys(b *testing.B) { - for i := 0; i < b.N; i++ { - sink = buildMapStrIntN3() - } -} - -func BenchmarkMap3nGenericMapIterationSimpleKeys(b *testing.B) { - n := buildMapStrIntN3() - b.ResetTimer() - for i := 0; i < b.N; i++ { - itr := n.MapIterator() - for k, v, _ := itr.Next(); !itr.Done(); k, v, _ = itr.Next() { - sink = k - sink = v - } - } -} - -var tableStrInt = [25]struct { - s string - i int -}{} - -func init() { - for i := 1; i <= 25; i++ { - tableStrInt[i-1] = struct { - s string - i int - }{"k" + strconv.Itoa(i), i} - } -} - -func buildMapStrIntN25() ipld.Node { - nb := NodeBuilder() - mb, err := nb.CreateMap() - mustNotError(err) - kb := mb.BuilderForKeys() - vb := mb.BuilderForValue("") - for i := 1; i <= 25; i++ { - mustNotError(mb.Insert(mustNode(kb.CreateString(tableStrInt[i-1].s)), mustNode(vb.CreateInt(tableStrInt[i-1].i)))) - } - return mustNode(mb.Build()) -} - -func BenchmarkMap25nFeedGenericMapSimpleKeys(b *testing.B) { - for i := 0; i < b.N; i++ { - sink = buildMapStrIntN25() - } -} - -func BenchmarkMap25nGenericMapIterationSimpleKeys(b *testing.B) { - n := buildMapStrIntN25() - b.ResetTimer() - for i := 0; i < b.N; i++ { - itr := n.MapIterator() - for k, v, _ := itr.Next(); !itr.Done(); k, v, _ = itr.Next() { - sink = k - sink = v - } - } -} - -// benchmarks covering encoding --> - -func BenchmarkSpec_Marshal_Map3StrInt(b *testing.B) { - tests.BenchmarkSpec_Marshal_Map3StrInt(b, NodeBuilder()) -} -func BenchmarkSpec_Marshal_MapNStrMap3StrInt(b *testing.B) { - tests.BenchmarkSpec_Marshal_MapNStrMap3StrInt(b, NodeBuilder()) -} - -func BenchmarkSpec_Unmarshal_Map3StrInt(b *testing.B) { - tests.BenchmarkSpec_Unmarshal_Map3StrInt(b, NodeBuilder()) -} -func BenchmarkSpec_Unmarshal_MapNStrMap3StrInt(b *testing.B) { - tests.BenchmarkSpec_Unmarshal_MapNStrMap3StrInt(b, NodeBuilder()) -} - -// benchmarks covering traversal --> - -func BenchmarkSpec_Walk_Map3StrInt(b *testing.B) { - tests.BenchmarkSpec_Walk_Map3StrInt(b, NodeBuilder()) -} -func BenchmarkSpec_Walk_MapNStrMap3StrInt(b *testing.B) { - tests.BenchmarkSpec_Walk_MapNStrMap3StrInt(b, NodeBuilder()) -} - -// copy of helper functions from must package, because import cycles, sigh --> - -func mustNotError(e error) { - if e != nil { - panic(e) - } -} -func mustNode(n ipld.Node, e error) ipld.Node { - if e != nil { - panic(e) - } - return n -} diff --git a/impl/free/freeNode.go b/impl/free/freeNode.go deleted file mode 100644 index ef0ffff0..00000000 --- a/impl/free/freeNode.go +++ /dev/null @@ -1,242 +0,0 @@ -package ipldfree - -import ( - "fmt" - - ipld "github.com/ipld/go-ipld-prime" -) - -var ( - _ ipld.Node = &Node{} -) - -/* - Node is an implementatin of `ipld.Node` that can contain any content. - - This implementation is extremely simple; it is general-purpose, - but not optimized for any particular purpose. - - The "zero" value of this struct has a kind of ReprKind_Invalid. - NodeBuilder must be used to produce valid instances of Node. -*/ -type Node struct { - kind ipld.ReprKind - - _map map[string]ipld.Node // Value union. Only one of these has meaning, depending on the value of 'Type'. - _mapOrd []string // Conjugate to _map, only has meaning depending on the value of 'Type'. - _arr []ipld.Node // Value union. Only one of these has meaning, depending on the value of 'Type'. - _bool bool // Value union. Only one of these has meaning, depending on the value of 'Type'. - _int int // Value union. Only one of these has meaning, depending on the value of 'Type'. - _float float64 // Value union. Only one of these has meaning, depending on the value of 'Type'. - _str string // Value union. Only one of these has meaning, depending on the value of 'Type'. - _bytes []byte // Value union. Only one of these has meaning, depending on the value of 'Type'. - _link ipld.Link // Value union. Only one of these has meaning, depending on the value of 'Type'. -} - -func (n *Node) ReprKind() ipld.ReprKind { - return n.kind -} - -func (Node) IsUndefined() bool { - return false -} -func (n *Node) IsNull() bool { - return n.kind == ipld.ReprKind_Null -} -func (n *Node) AsBool() (v bool, _ error) { - if n.kind != ipld.ReprKind_Bool { - return false, ipld.ErrWrongKind{MethodName: "AsBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: n.kind} - } - return n._bool, nil -} -func (n *Node) AsInt() (v int, _ error) { - if n.kind != ipld.ReprKind_Int { - return 0, ipld.ErrWrongKind{MethodName: "AsInt", AppropriateKind: ipld.ReprKindSet_JustInt, ActualKind: n.kind} - } - return n._int, nil -} -func (n *Node) AsFloat() (v float64, _ error) { - if n.kind != ipld.ReprKind_Float { - return 0, ipld.ErrWrongKind{MethodName: "AsFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: n.kind} - } - return n._float, nil -} -func (n *Node) AsString() (v string, _ error) { - if n.kind != ipld.ReprKind_String { - return "", ipld.ErrWrongKind{MethodName: "AsString", AppropriateKind: ipld.ReprKindSet_JustString, ActualKind: n.kind} - } - return n._str, nil -} -func (n *Node) AsBytes() (v []byte, _ error) { - if n.kind != ipld.ReprKind_Bytes { - return nil, ipld.ErrWrongKind{MethodName: "AsBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: n.kind} - } - return n._bytes, nil -} -func (n *Node) AsLink() (v ipld.Link, _ error) { - if n.kind != ipld.ReprKind_Link { - return nil, ipld.ErrWrongKind{MethodName: "AsLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: n.kind} - } - return n._link, nil -} - -func (n *Node) NodeBuilder() ipld.NodeBuilder { - return nodeBuilder{n} -} - -func (n *Node) MapIterator() ipld.MapIterator { - if n.kind != ipld.ReprKind_Map { - return &mapIterator{n, 0, ipld.ErrWrongKind{MethodName: "MapIterator", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: n.kind}} - } - return &mapIterator{n, 0, nil} -} - -type mapIterator struct { - node *Node - idx int - err error -} - -func (itr *mapIterator) Next() (ipld.Node, ipld.Node, error) { - if itr.err != nil { - return nil, nil, itr.err - } - k := itr.node._mapOrd[itr.idx] - v := itr.node._map[k] - itr.idx++ - return &Node{kind: ipld.ReprKind_String, _str: k}, v, nil -} -func (itr *mapIterator) Done() bool { - if itr.err != nil { - return false - } - return itr.idx >= len(itr.node._mapOrd) -} - -func (n *Node) ListIterator() ipld.ListIterator { - if n.kind != ipld.ReprKind_List { - return &listIterator{n, 0, ipld.ErrWrongKind{MethodName: "ListIterator", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: n.kind}} - } - return &listIterator{n, 0, nil} -} - -type listIterator struct { - node *Node - idx int - err error -} - -func (itr *listIterator) Next() (int, ipld.Node, error) { - if itr.err != nil { - return -1, nil, itr.err - } - v := itr.node._arr[itr.idx] - idx := itr.idx - itr.idx++ - return idx, v, nil -} -func (itr *listIterator) Done() bool { - if itr.err != nil { - return false - } - return itr.idx >= len(itr.node._arr) -} - -func (n *Node) Length() int { - switch n.ReprKind() { - case ipld.ReprKind_Map: - return len(n._mapOrd) - case ipld.ReprKind_List: - return len(n._arr) - default: - return -1 - } -} - -func (n *Node) LookupString(pth string) (ipld.Node, error) { - switch n.kind { - case ipld.ReprKind_Map: - v, exists := n._map[pth] - if !exists { - return nil, ipld.ErrNotExists{ipld.PathSegmentOfString(pth)} - } - return v, nil - case ipld.ReprKind_Invalid, - ipld.ReprKind_Null, - ipld.ReprKind_List, - ipld.ReprKind_String, - ipld.ReprKind_Bytes, - ipld.ReprKind_Int, - ipld.ReprKind_Float, - ipld.ReprKind_Link: - return nil, ipld.ErrWrongKind{MethodName: "LookupString", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: n.kind} - default: - panic("unreachable") - } -} - -func (n *Node) Lookup(key ipld.Node) (ipld.Node, error) { - switch n.kind { - case ipld.ReprKind_Map: - ks, err := key.AsString() - if err != nil { - return nil, ipld.ErrInvalidKey{fmt.Sprintf("got %s, need string", key.ReprKind())} - } - v, exists := n._map[ks] - if !exists { - return nil, ipld.ErrNotExists{ipld.PathSegmentOfString(ks)} - } - return v, nil - default: - return nil, ipld.ErrWrongKind{MethodName: "Lookup", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: n.kind} - } -} - -func (n *Node) LookupIndex(idx int) (ipld.Node, error) { - switch n.kind { - case ipld.ReprKind_List: - if idx >= len(n._arr) { - return nil, ipld.ErrNotExists{ipld.PathSegmentOfInt(idx)} - } - if n._arr[idx] == nil { - return nil, ipld.ErrNotExists{ipld.PathSegmentOfInt(idx)} - } - return n._arr[idx], nil - case ipld.ReprKind_Invalid, - ipld.ReprKind_Null, - ipld.ReprKind_Map, - ipld.ReprKind_Bool, - ipld.ReprKind_String, - ipld.ReprKind_Bytes, - ipld.ReprKind_Int, - ipld.ReprKind_Float, - ipld.ReprKind_Link: - return nil, ipld.ErrWrongKind{MethodName: "LookupIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: n.kind} - default: - panic("unreachable") - } -} - -func (n *Node) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - switch n.kind { - case ipld.ReprKind_Map: - return n.LookupString(seg.String()) - case ipld.ReprKind_List: - idx, err := seg.Index() - if err != nil { - return nil, err - } - return n.LookupIndex(idx) - case ipld.ReprKind_Invalid, - ipld.ReprKind_Null, - ipld.ReprKind_Bool, - ipld.ReprKind_String, - ipld.ReprKind_Bytes, - ipld.ReprKind_Int, - ipld.ReprKind_Float, - ipld.ReprKind_Link: - return nil, ipld.ErrWrongKind{MethodName: "LookupSegment", AppropriateKind: ipld.ReprKindSet_Recursive, ActualKind: n.kind} - default: - panic("unreachable") - } -} diff --git a/impl/free/freeNodeBuilder.go b/impl/free/freeNodeBuilder.go deleted file mode 100644 index 2d18055f..00000000 --- a/impl/free/freeNodeBuilder.go +++ /dev/null @@ -1,176 +0,0 @@ -package ipldfree - -import ( - "fmt" - - ipld "github.com/ipld/go-ipld-prime" -) - -// NodeBuilder returns a new ipld.NodeBuilder implementation that will produce -// ipldfree.Node instances. -// -// There are no constraints on free nodes, so none of the create methods -// will ever return errors. -func NodeBuilder() ipld.NodeBuilder { - return nodeBuilder{} -} - -type nodeBuilder struct { - predecessor *Node // optional; only relevant for "Amend*" methods. -} - -func (nb nodeBuilder) CreateMap() (ipld.MapBuilder, error) { - return &mapBuilder{n: Node{kind: ipld.ReprKind_Map, _map: make(map[string]ipld.Node)}}, nil -} -func (nb nodeBuilder) AmendMap() (ipld.MapBuilder, error) { - if nb.predecessor == nil { - return nb.CreateMap() - } - if nb.predecessor.kind != ipld.ReprKind_List { - return nil, fmt.Errorf("AmendMap cannot be used when predecessor was not a map") - } - newMap := make(map[string]ipld.Node, len(nb.predecessor._map)) - for k, v := range nb.predecessor._map { - newMap[k] = v - } - newArr := make([]string, len(nb.predecessor._mapOrd)) - copy(newArr, nb.predecessor._mapOrd) - return &mapBuilder{n: Node{kind: ipld.ReprKind_Map, _map: newMap, _mapOrd: newArr}}, nil -} -func (nb nodeBuilder) CreateList() (ipld.ListBuilder, error) { - return &listBuilder{n: Node{kind: ipld.ReprKind_List}}, nil -} -func (nb nodeBuilder) AmendList() (ipld.ListBuilder, error) { - if nb.predecessor == nil { - return nb.CreateList() - } - if nb.predecessor.kind != ipld.ReprKind_List { - return nil, fmt.Errorf("AmendList cannot be used when predecessor was not a list") - } - newArr := make([]ipld.Node, len(nb.predecessor._arr)) - copy(newArr, nb.predecessor._arr) - return &listBuilder{n: Node{kind: ipld.ReprKind_List, _arr: newArr}}, nil -} -func (nb nodeBuilder) CreateNull() (ipld.Node, error) { - return &Node{kind: ipld.ReprKind_Null}, nil -} -func (nb nodeBuilder) CreateBool(v bool) (ipld.Node, error) { - return &Node{kind: ipld.ReprKind_Bool, _bool: v}, nil -} -func (nb nodeBuilder) CreateInt(v int) (ipld.Node, error) { - return &Node{kind: ipld.ReprKind_Int, _int: v}, nil -} -func (nb nodeBuilder) CreateFloat(v float64) (ipld.Node, error) { - return &Node{kind: ipld.ReprKind_Float, _float: v}, nil -} -func (nb nodeBuilder) CreateString(v string) (ipld.Node, error) { - return &Node{kind: ipld.ReprKind_String, _str: v}, nil -} -func (nb nodeBuilder) CreateBytes(v []byte) (ipld.Node, error) { - return &Node{kind: ipld.ReprKind_Bytes, _bytes: v}, nil -} -func (nb nodeBuilder) CreateLink(v ipld.Link) (ipld.Node, error) { - return &Node{kind: ipld.ReprKind_Link, _link: v}, nil -} - -type mapBuilder struct { - n Node // a wip node; initialized at construction. - // whole builder object nil'd after terminal `Build()` call to prevent reuse. -} - -// Insert adds a k:v pair to the map. -// -// As is usual for maps, the key must have kind==ReprKind_String. -// -// Keys not already present in the map will be appened to the end of the -// iteration order; keys already present retain their original order. -func (mb *mapBuilder) Insert(k, v ipld.Node) error { - ks, err := k.AsString() - if err != nil { - return fmt.Errorf("invalid node for map key: %s", err) - } - _, exists := mb.n._map[ks] - if exists { - return fmt.Errorf("repeated map key: %s", ks) - } - mb.n._map[ks] = v - mb.n._mapOrd = append(mb.n._mapOrd, ks) - return nil -} -func (mb *mapBuilder) Delete(k ipld.Node) error { - panic("NYI") // and see the "review: MapBuilder.Delete" comment in the interface defn file. -} -func (mb *mapBuilder) Build() (ipld.Node, error) { - v := mb.n - mb = nil - return &v, nil -} -func (mapBuilder) BuilderForKeys() ipld.NodeBuilder { - return justStringNodeBuilder{} -} -func (mapBuilder) BuilderForValue(_ string) ipld.NodeBuilder { - return nodeBuilder{} -} - -type listBuilder struct { - n Node // a wip node; initialized at construction. - // whole builder object nil'd after terminal `Build()` call to prevent reuse. -} - -func (lb *listBuilder) AppendAll(vs []ipld.Node) error { - off := len(lb.n._arr) - new := off + len(vs) - growList(&lb.n._arr, new-1) - copy(lb.n._arr[off:new], vs) - return nil -} -func (lb *listBuilder) Append(v ipld.Node) error { - lb.n._arr = append(lb.n._arr, v) - return nil -} -func (lb *listBuilder) Set(idx int, v ipld.Node) error { - growList(&lb.n._arr, idx) - lb.n._arr[idx] = v - return nil -} -func (lb *listBuilder) Build() (ipld.Node, error) { - v := lb.n - lb = nil - return &v, nil -} -func (listBuilder) BuilderForValue(_ int) ipld.NodeBuilder { - return nodeBuilder{} -} - -func growList(l *[]ipld.Node, k int) { - oldLen := len(*l) - minLen := k + 1 - if minLen > oldLen { - // Grow. - oldCap := cap(*l) - 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([]ipld.Node, minLen, newCap) - copy(newArr, *l) - *l = newArr - } else { - // Still have cap, just extend the slice. - *l = (*l)[0:minLen] - } - } -} diff --git a/impl/free/freeNode_test.go b/impl/free/freeNode_test.go deleted file mode 100644 index fba6dca4..00000000 --- a/impl/free/freeNode_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package ipldfree - -import ( - "testing" - - "github.com/ipld/go-ipld-prime/tests" -) - -func TestNodeBuilder(t *testing.T) { - tests.TestBuildingScalars(t, NodeBuilder()) - tests.TestBuildingRecursives(t, NodeBuilder()) -} - -func TestTokening(t *testing.T) { - tests.TestScalarMarshal(t, NodeBuilder()) - tests.TestRecursiveMarshal(t, NodeBuilder()) - tests.TestScalarUnmarshal(t, NodeBuilder()) - tests.TestRecursiveUnmarshal(t, NodeBuilder()) -} diff --git a/impl/free/justString.go b/impl/free/justString.go deleted file mode 100644 index be097fdb..00000000 --- a/impl/free/justString.go +++ /dev/null @@ -1,106 +0,0 @@ -package ipldfree - -import ( - ipld "github.com/ipld/go-ipld-prime" - nodeutil "github.com/ipld/go-ipld-prime/impl/util" -) - -func String(value string) ipld.Node { - return justString(value) -} - -// justString 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 justString string - -func (justString) ReprKind() ipld.ReprKind { - return ipld.ReprKind_String -} -func (justString) LookupString(string) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "LookupString", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String} -} -func (justString) Lookup(key ipld.Node) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "Lookup", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String} -} -func (justString) LookupIndex(idx int) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "LookupIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_String} -} -func (justString) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "LookupSegment", AppropriateKind: ipld.ReprKindSet_Recursive, ActualKind: ipld.ReprKind_String} -} -func (justString) MapIterator() ipld.MapIterator { - return nodeutil.MapIteratorErrorThunk(ipld.ErrWrongKind{MethodName: "MapIterator", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String}) -} -func (justString) ListIterator() ipld.ListIterator { - return nodeutil.ListIteratorErrorThunk(ipld.ErrWrongKind{MethodName: "ListIterator", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_String}) -} -func (justString) Length() int { - return -1 -} -func (justString) IsUndefined() bool { - return false -} -func (justString) IsNull() bool { - return false -} -func (justString) AsBool() (bool, error) { - return false, ipld.ErrWrongKind{MethodName: "AsBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: ipld.ReprKind_String} -} -func (justString) AsInt() (int, error) { - return 0, ipld.ErrWrongKind{MethodName: "AsInt", AppropriateKind: ipld.ReprKindSet_JustInt, ActualKind: ipld.ReprKind_String} -} -func (justString) AsFloat() (float64, error) { - return 0, ipld.ErrWrongKind{MethodName: "AsFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_String} -} -func (x justString) AsString() (string, error) { - return string(x), nil -} -func (justString) AsBytes() ([]byte, error) { - return nil, ipld.ErrWrongKind{MethodName: "AsBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: ipld.ReprKind_String} -} -func (justString) AsLink() (ipld.Link, error) { - return nil, ipld.ErrWrongKind{MethodName: "AsLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: ipld.ReprKind_String} -} -func (justString) NodeBuilder() ipld.NodeBuilder { - return justStringNodeBuilder{} -} - -type justStringNodeBuilder struct{} - -func (nb justStringNodeBuilder) CreateMap() (ipld.MapBuilder, error) { - return nil, ipld.ErrWrongKind{MethodName: "NodeBuilder.CreateMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String} -} -func (nb justStringNodeBuilder) AmendMap() (ipld.MapBuilder, error) { - return nil, ipld.ErrWrongKind{MethodName: "NodeBuilder.AmendMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String} -} -func (nb justStringNodeBuilder) CreateList() (ipld.ListBuilder, error) { - return nil, ipld.ErrWrongKind{MethodName: "NodeBuilder.CreateList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_String} -} -func (nb justStringNodeBuilder) AmendList() (ipld.ListBuilder, error) { - return nil, ipld.ErrWrongKind{MethodName: "NodeBuilder.AmendList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_String} -} -func (nb justStringNodeBuilder) CreateNull() (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "NodeBuilder.CreateNull", AppropriateKind: ipld.ReprKindSet_JustNull, ActualKind: ipld.ReprKind_String} -} -func (nb justStringNodeBuilder) CreateBool(v bool) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "NodeBuilder.CreateBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: ipld.ReprKind_String} -} -func (nb justStringNodeBuilder) CreateInt(v int) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "NodeBuilder.CreateInt", AppropriateKind: ipld.ReprKindSet_JustInt, ActualKind: ipld.ReprKind_String} -} -func (nb justStringNodeBuilder) CreateFloat(v float64) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "NodeBuilder.CreateFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_String} -} -func (nb justStringNodeBuilder) CreateString(v string) (ipld.Node, error) { - return justString(v), nil -} -func (nb justStringNodeBuilder) CreateBytes(v []byte) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "NodeBuilder.CreateBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: ipld.ReprKind_String} -} -func (nb justStringNodeBuilder) CreateLink(v ipld.Link) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{MethodName: "NodeBuilder.CreateLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: ipld.ReprKind_String} -} diff --git a/impl/free/justString_test.go b/impl/free/justString_test.go deleted file mode 100644 index 91bb2308..00000000 --- a/impl/free/justString_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package ipldfree - -import ( - "fmt" - "runtime" - "testing" - - ipld "github.com/ipld/go-ipld-prime" -) - -func BenchmarkJustString(b *testing.B) { - var node ipld.Node - for i := 0; i < b.N; i++ { - node = String("boxme") - } - _ = node -} - -func BenchmarkJustStringUse(b *testing.B) { - var node ipld.Node - for i := 0; i < b.N; i++ { - node = String("boxme") - s, err := node.AsString() - _ = s - _ = err - } -} - -func BenchmarkJustStringLogAllocs(b *testing.B) { - memUsage := func(m1, m2 *runtime.MemStats) { - fmt.Println( - "Alloc:", m2.Alloc-m1.Alloc, - "TotalAlloc:", m2.TotalAlloc-m1.TotalAlloc, - "HeapAlloc:", m2.HeapAlloc-m1.HeapAlloc, - ) - } - var m1, m2 runtime.MemStats - runtime.ReadMemStats(&m1) - var node ipld.Node = String("boxme") - runtime.ReadMemStats(&m2) - memUsage(&m1, &m2) - sinkNode = node // necessary to avoid clever elision. -} - -var sinkNode ipld.Node diff --git a/impl/typed/wrapStruct.go b/impl/typed/wrapStruct.go deleted file mode 100644 index 817b2280..00000000 --- a/impl/typed/wrapStruct.go +++ /dev/null @@ -1,224 +0,0 @@ -package typed - -import ( - "fmt" - - ipld "github.com/ipld/go-ipld-prime" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" - "github.com/ipld/go-ipld-prime/schema" -) - -var _ schema.TypedNode = wrapnodeStruct{} - -type wrapnodeStruct struct { - ipld.Node - typ schema.TypeStruct -} - -// Most of the 'nope' methods from the inner node are fine; -// we add the extra things required for schema.TypedNode; -// we decorate the getters and iterators to handle the distinct path around optionals -// and return a different error for missing fields; -// length becomes fixed to a constant; -// and we replace the builder with a complete wrapper that maintains type rules. - -// (We could override more of the Node methods to return errors with accurate type name, though.) - -func (tn wrapnodeStruct) Type() schema.Type { - return tn.typ -} - -func (tn wrapnodeStruct) LookupString(key string) (ipld.Node, error) { - for _, field := range tn.typ.Fields() { - if field.Name() != key { - continue - } - v, e1 := tn.Node.LookupString(key) - if e1 == nil { - return v, nil // null or set both flow through here - } - if _, ok := e1.(ipld.ErrNotExists); ok { - return ipld.Undef, nil // we assume the type allows this, or this node shouldn't have been possible to construct in the first place - } - return nil, e1 - } - return nil, schema.ErrNoSuchField{Type: tn.typ, FieldName: key} -} - -func (tn wrapnodeStruct) MapIterator() ipld.MapIterator { - return &wrapnodeStruct_Iterator{&tn, 0} -} - -type wrapnodeStruct_Iterator struct { - node *wrapnodeStruct - idx int -} - -func (itr *wrapnodeStruct_Iterator) Next() (k ipld.Node, v ipld.Node, _ error) { - if itr.idx >= itr.node.Length() { - return nil, nil, ipld.ErrIteratorOverread{} - } - field := itr.node.typ.Fields()[itr.idx] - k = ipldfree.String(field.Name()) - v, e1 := itr.node.LookupString(field.Name()) - if e1 != nil { - if _, ok := e1.(ipld.ErrNotExists); ok { - v = ipld.Undef // we assume the type allows this, or this node shouldn't have been possible to construct in the first place - } else { - return k, nil, e1 - } - } - itr.idx++ - return -} -func (itr *wrapnodeStruct_Iterator) Done() bool { - return itr.idx >= itr.node.Length() -} - -func (tn wrapnodeStruct) Length() int { - return len(tn.typ.Fields()) -} - -func (tn wrapnodeStruct) NodeBuilder() ipld.NodeBuilder { - return wrapnodeStruct_Builder{tn.NodeBuilder(), tn.typ} -} - -func (tn wrapnodeStruct) Representation() ipld.Node { - switch rs := tn.typ.RepresentationStrategy().(type) { - case schema.StructRepresentation_Map: - panic("todo") // TODO: add new source file for each of these. - case schema.StructRepresentation_Tuple: - panic("todo") // TODO: add new source file for each of these. - case schema.StructRepresentation_StringPairs: - panic("todo") // TODO: add new source file for each of these. - case schema.StructRepresentation_StringJoin: - panic("todo") // TODO: add new source file for each of these. - default: - _ = rs - panic("unreachable (schema.StructRepresentation sum type)") - } -} - -// The builder is a more complete straightjacket; it wouldn't be correct to -// assume that the builder we're delegating internal storage to would reject -// other kinds (e.g. CreateString) entirely, and our type requires that. - -type wrapnodeStruct_Builder struct { - utnb ipld.NodeBuilder - typ schema.TypeStruct -} - -func (nb wrapnodeStruct_Builder) CreateMap() (ipld.MapBuilder, error) { - mb, err := nb.utnb.CreateMap() - if err != nil { - return nil, err - } - needs := make(map[string]struct{}, len(nb.typ.Fields())) - for _, field := range nb.typ.Fields() { - if !field.IsOptional() { - needs[field.Name()] = struct{}{} - } - } - return &wrapnodeStruct_MapBuilder{mb, nb.typ, needs}, nil -} -func (nb wrapnodeStruct_Builder) AmendMap() (ipld.MapBuilder, error) { - panic("TODO") // TODO -} -func (nb wrapnodeStruct_Builder) CreateList() (ipld.ListBuilder, error) { - return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Map} -} -func (nb wrapnodeStruct_Builder) AmendList() (ipld.ListBuilder, error) { - return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.AmendList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Map} -} -func (nb wrapnodeStruct_Builder) CreateNull() (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateNull", AppropriateKind: ipld.ReprKindSet_JustNull, ActualKind: ipld.ReprKind_Map} -} -func (nb wrapnodeStruct_Builder) CreateBool(v bool) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateBool", AppropriateKind: ipld.ReprKindSet_JustBool, ActualKind: ipld.ReprKind_Map} -} -func (nb wrapnodeStruct_Builder) CreateInt(v int) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateInt", AppropriateKind: ipld.ReprKindSet_JustInt, ActualKind: ipld.ReprKind_Map} -} -func (nb wrapnodeStruct_Builder) CreateFloat(v float64) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateFloat", AppropriateKind: ipld.ReprKindSet_JustFloat, ActualKind: ipld.ReprKind_Map} -} -func (nb wrapnodeStruct_Builder) CreateString(v string) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateString", AppropriateKind: ipld.ReprKindSet_JustString, ActualKind: ipld.ReprKind_Map} -} -func (nb wrapnodeStruct_Builder) CreateBytes(v []byte) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateBytes", AppropriateKind: ipld.ReprKindSet_JustBytes, ActualKind: ipld.ReprKind_Map} -} -func (nb wrapnodeStruct_Builder) CreateLink(v ipld.Link) (ipld.Node, error) { - return nil, ipld.ErrWrongKind{TypeName: string(nb.typ.Name()), MethodName: "NodeBuilder.CreateLink", AppropriateKind: ipld.ReprKindSet_JustLink, ActualKind: ipld.ReprKind_Map} -} - -type wrapnodeStruct_MapBuilder struct { - utmb ipld.MapBuilder - typ schema.TypeStruct - needs map[string]struct{} - // We have to remember if anything was intentionally unset so we can check at the end. - // Or, initialize with the set of things that need to be set, and decrement it; easier. -} - -func (mb *wrapnodeStruct_MapBuilder) Insert(k, v ipld.Node) error { - ks, err := k.AsString() - if err != nil { - return ipld.ErrInvalidKey{"not a string: " + err.Error()} - } - // Check that the field exists at all. - field := mb.typ.Field(ks) - if field == nil { - return schema.ErrNoSuchField{Type: mb.typ, FieldName: ks} - } - // Check that the value is assignable to this field, or return error. - vt, ok := v.(schema.TypedNode) - switch { - case v.IsNull(): - if !field.IsNullable() { - return fmt.Errorf("type mismatch on struct field assignment: cannot assign null to non-nullable field") - } - // if null and nullable: carry on. - case ok: - if mb.typ.Field(ks).Type() != vt.Type() { - return fmt.Errorf("type mismatch on struct field assignment") - } - // if typed node, and it matches: carry on. - default: - return fmt.Errorf("need schema.TypedNode for insertion into struct") // FUTURE: maybe if it's a basic enough thing we sholud attempt coerce? - } - // Insert the value, and note it's now been set. - if err := mb.utmb.Insert(k, v); err != nil { - return err - } - delete(mb.needs, ks) - return nil -} -func (mb *wrapnodeStruct_MapBuilder) Delete(k ipld.Node) error { - panic("delete not supported on this type") // I have serious questions about whether the delete method deserves to exist. -} -func (mb *wrapnodeStruct_MapBuilder) Build() (ipld.Node, error) { - if len(mb.needs) > 0 { - return nil, fmt.Errorf("missing required fields") // TODO say which - } - n, err := mb.Build() - if err != nil { - return nil, err - } - return wrapnodeStruct{n, mb.typ}, nil -} - -func (mb *wrapnodeStruct_MapBuilder) BuilderForKeys() ipld.NodeBuilder { - // Struct fields are always plain strings, so this is easy. - // FUTURE: we might want to have the builder immediately reject a string not in the field names enum instead of leaving that until insert? - // unclear if this is necessary. if so: one impl sufficies, just looks at TypeStruct.Field (fortunately this already computes a map internally). - return ipldfree.NodeBuilder() // FIXME: justStringNodeBuilder{} would be preferable but isn't exported atm. -} -func (mb *wrapnodeStruct_MapBuilder) BuilderForValue(k string) ipld.NodeBuilder { - // TODO we'll need other kinds of "wrapnode{Foo}_Builder" to finish fleshing this out. - _ = mb.typ.Field(k).Type().Kind() // putting together a nodebuilder from this info should be easy. - panic("todo: need more wrapnode builders") -} - -// TODO much of this all again, but now for representations. -// (e.g. `wrapnodeStruct_ReprMap_MapBuilder.BuilderForValue` method will do almost the same things, -// but will need to look up a nodebuilder from a slightly different table.) diff --git a/impl/util/iteratorThunks.go b/impl/util/iteratorThunks.go deleted file mode 100644 index 2444b306..00000000 --- a/impl/util/iteratorThunks.go +++ /dev/null @@ -1,21 +0,0 @@ -package nodeutil - -import ( - ipld "github.com/ipld/go-ipld-prime" -) - -func MapIteratorErrorThunk(err error) ipld.MapIterator { - return mapIteratorErrorThunk{err} -} -func ListIteratorErrorThunk(err error) ipld.ListIterator { - return listIteratorErrorThunk{err} -} - -type mapIteratorErrorThunk struct{ err error } -type listIteratorErrorThunk struct{ err error } - -func (itr mapIteratorErrorThunk) Next() (ipld.Node, ipld.Node, error) { return nil, nil, itr.err } -func (itr mapIteratorErrorThunk) Done() bool { return false } - -func (itr listIteratorErrorThunk) Next() (int, ipld.Node, error) { return -1, nil, itr.err } -func (itr listIteratorErrorThunk) Done() bool { return false } diff --git a/linking.go b/linking.go index 3e4cc405..8ccc40e9 100644 --- a/linking.go +++ b/linking.go @@ -31,12 +31,16 @@ import ( // using it purely for json/cbor via the encoding packages and // foregoing the advanced traversal features around transparent link loading. type Link interface { - // Load returns a Node identified by the Link. + // Load consumes serial data from a Loader and funnels the parsed + // data into a NodeAssembler. // // The provided Loader function is used to get a reader for the raw // serialized content; the Link contains an understanding of how to - // select a decoder (and hasher for verification, etc). - Load(context.Context, LinkContext, NodeBuilder, Loader) (Node, error) + // select a decoder (and hasher for verification, etc); and the + // NodeAssembler accumulates the final results (which you can + // presumably access from elsewhere; Load is designed not to know + // about this). + Load(context.Context, LinkContext, NodeAssembler, Loader) error // LinkBuilder returns a handle to any parameters of the Link which // are needed to create a new Link of the same style but with new content. diff --git a/linking/cid/cidLink.go b/linking/cid/cidLink.go index 21189682..e8561a4d 100644 --- a/linking/cid/cidLink.go +++ b/linking/cid/cidLink.go @@ -19,19 +19,19 @@ type Link struct { cid.Cid } -func (lnk Link) Load(ctx context.Context, lnkCtx ipld.LinkContext, nb ipld.NodeBuilder, loader ipld.Loader) (ipld.Node, error) { +func (lnk Link) Load(ctx context.Context, lnkCtx ipld.LinkContext, na ipld.NodeAssembler, loader ipld.Loader) error { // Open the byte reader. r, err := loader(lnk, lnkCtx) if err != nil { - return nil, err + return err } // Tee into hash checking and unmarshalling. mcDecoder, exists := multicodecDecodeTable[lnk.Prefix().Codec] if !exists { - return nil, fmt.Errorf("no decoder registered for multicodec %d", lnk.Prefix().Codec) + return fmt.Errorf("no decoder registered for multicodec %d", lnk.Prefix().Codec) } var hasher bytes.Buffer // multihash only exports bulk use, which is... really inefficient and should be fixed. - node, decodeErr := mcDecoder(nb, io.TeeReader(r, &hasher)) + decodeErr := mcDecoder(na, io.TeeReader(r, &hasher)) // Error checking order here is tricky. // If decoding errored out, we should still run the reader to the end, to check the hash. // (We still don't implement this by running the hash to the end first, because that would increase the high-water memory requirement.) @@ -40,20 +40,20 @@ func (lnk Link) Load(ctx context.Context, lnkCtx ipld.LinkContext, nb ipld.NodeB if decodeErr != nil { _, err := io.Copy(&hasher, r) if err != nil { - return nil, err + return err } } cid, err := lnk.Prefix().Sum(hasher.Bytes()) if err != nil { - return nil, err + return err } if cid != lnk.Cid { - return nil, fmt.Errorf("hash mismatch! %q (actual) != %q (expected)", cid, lnk.Cid) + return fmt.Errorf("hash mismatch! %q (actual) != %q (expected)", cid, lnk.Cid) } if decodeErr != nil { - return nil, decodeErr + return decodeErr } - return node, nil + return nil } func (lnk Link) LinkBuilder() ipld.LinkBuilder { return LinkBuilder{lnk.Cid.Prefix()} diff --git a/linking/cid/multicodec.go b/linking/cid/multicodec.go index 9d8c5d8c..510e0411 100644 --- a/linking/cid/multicodec.go +++ b/linking/cid/multicodec.go @@ -10,8 +10,10 @@ type MulticodecDecodeTable map[uint64]MulticodecDecoder type MulticodecEncodeTable map[uint64]MulticodecEncoder -// MulticodecDecoder builds an ipld.Node by unmarshalling bytes and applying -// an ipld.NodeBuilder. +// MulticodecDecoder builds an ipld.Node by unmarshalling bytes and funnelling +// the data tree into an ipld.NodeAssembler. The resulting Node is not +// returned; typically you call this function with an ipld.NodeBuilder, +// and you can extract the result from there. // // MulticodecDecoder are used by registering them in a MulticodecDecoderTable, // which makes them available to be used internally by cidlink.Link.Load. @@ -22,7 +24,7 @@ type MulticodecEncodeTable map[uint64]MulticodecEncoder // that know how to fastpath their work *if* we're doing a cbor decode; // if possible, detect and use that; if not, fall back to general generic // NodeBuilder usage. -type MulticodecDecoder func(ipld.NodeBuilder, io.Reader) (ipld.Node, error) +type MulticodecDecoder func(ipld.NodeAssembler, io.Reader) error // MulticodecEncoder marshals and ipld.Node into bytes and sends them to // an io.Writer. diff --git a/must/must.go b/must/must.go index 9559c6d3..cd08c17b 100644 --- a/must/must.go +++ b/must/must.go @@ -67,3 +67,25 @@ func True(v bool) { panic("must.True") } } + +// must.String unboxes the given Node via AsString, +// panicking in the case that the Node isn't of string kind, +// and otherwise returning the bare native string. +func String(n ipld.Node) string { + if v, err := n.AsString(); err != nil { + panic(err) + } else { + return v + } +} + +// must.Int unboxes the given Node via AsInt, +// panicking in the case that the Node isn't of int kind, +// and otherwise returning the bare native int. +func Int(n ipld.Node) int { + if v, err := n.AsInt(); err != nil { + panic(err) + } else { + return v + } +} diff --git a/node.go b/node.go index 7260f436..45834d12 100644 --- a/node.go +++ b/node.go @@ -104,8 +104,7 @@ type Node interface { // MapIterator returns an iterator which yields key-value pairs // traversing the node. - // If the node kind is anything other than a map, the iterator will - // yield error values. + // If the node kind is anything other than a map, nil will be returned. // // The iterator will yield every entry in the map; that is, it // can be expected that itr.Next will be called node.Length times @@ -114,8 +113,7 @@ type Node interface { // ListIterator returns an iterator which yields key-value pairs // traversing the node. - // If the node kind is anything other than a list, the iterator will - // yield error values. + // If the node kind is anything other than a list, nil will be returned. // // The iterator will yield every entry in the list; that is, it // can be expected that itr.Next will be called node.Length times @@ -142,49 +140,63 @@ type Node interface { AsBytes() ([]byte, error) AsLink() (Link, error) - // NodeBuilder returns a NodeBuilder which can be used to build - // new nodes of the same implementation type as this one. + // Style returns a NodeStyle which can describe some properties of this node's implementation, + // and also be used to get a NodeBuilder, + // which can be use to create new nodes with the same implementation as this one. // - // For map and list nodes, the NodeBuilder's append-oriented methods - // will work using this node's values as a base. - // If this is a typed node, the NodeBuilder will carry the same - // typesystem constraints as this Node. + // For typed nodes, the NodeStyle will also implement schema.Type. // - // (This feature is used by the traversal package, especially in - // e.g. traversal.Transform, for doing tree updates while keeping the - // existing implementation preferences and doing as many operations - // in copy-on-write fashions as possible.) + // For Advanced Data Layouts, the NodeStyle will encapsulate any additional + // parameters and configuration of the ADL, and will also (usually) + // implement NodeStyleSupportingAmend. // - // --- - // - // More specifically, the contract of a NodeBuilder returned by this method - // is that it should be able to "replace" this node with a new one of - // similar properties. - // E.g., for a string, the builder must be able to build a new string. - // For a map, the builder must be able to build a new map. - // For a *struct* (when using typed nodes), the builder must be able to - // build new structs of the name type. - // Note that the promise doesn't extend further: there's no requirement - // that the builder be able to build maps if this node's kind is "string" - // (you can see why this lack-of-contract is important when considering - // typed nodes: if this node has a struct type, then should the builder - // be able to build other structs of different types? Of course not; - // there'd be no way to define which other types to build!). - // For nulls, this means the builder doesn't have to do much at all! - // - // (Some Nodes may return a NodeBuilder that can be used for much more - // than replacing their own kind: for example, Node implementations from - // the ipldfree package tend to return a NodeBuilder than can build any - // other ipldfree.Node (e.g. even the builder obtained from a string node - // will be able to build maps). This is not required by the contract; - // such packages only do so out of internal implementation convenience.) + // Calling this method should not cause an allocation. + Style() NodeStyle +} + +// NodeStyle describes a node implementation (all Node have a NodeStyle), +// and a NodeStyle can always be used to get a NodeBuilder. +// +// A NodeStyle may also provide other information about implementation; +// such information is specific to this library ("style" isn't a concept +// you'll find in the IPLD Specifications), and is usually provided through +// feature-detection interfaces (for example, see NodeStyleSupportingAmend). +// +// Generic algorithms for working with IPLD Nodes make use of NodeStyle +// to get builders for new nodes when creating data, and can also use the +// feature-detection interfaces to help decide what kind of operations +// will be optimal to use on a given node implementation. +// +// Note that NodeStyle is not the same as schema.Type. +// NodeStyle is a (golang-specific!) way to reflect upon the implementation +// and in-memory layout of some IPLD data. +// schema.Type is information about how a group of nodes is related in a schema +// (if they have one!) and the rules that the type mandates the node must follow. +// (Every node must have a style; but schema types are an optional feature.) +type NodeStyle interface { + // NewBuilder returns a NodeBuilder that can be used to create a new Node. // - // This "able to replace" behavior also has a specific application regarding - // nodes implementing Advanced Data Layouts: it means that the NodeBuilder - // returned by this method must produce a new Node using that same ADL. - // For example, if a Node is a map implemented by some sort of HAMT, its - // NodeBuilder must also produce a new HAMT. - NodeBuilder() NodeBuilder + // Note that calling NewBuilder often performs an allocation + // (while in contrast, getting a NodeStyle typically does not!) -- + // this may be consequential when writing high performance code. + NewBuilder() NodeBuilder +} + +// NodeStyleSupportingAmend is a feature-detection interface that can be +// used on a NodeStyle to see if it's possible to build new nodes of this style +// while sharing some internal data in a copy-on-write way. +// +// For example, Nodes using an Advanced Data Layout will typically +// support this behavior, and since ADLs are often used for handling large +// volumes of data, detecting and using this feature can result in significant +// performance savings. +type NodeStyleSupportingAmend interface { + AmendingBuilder(base Node) NodeBuilder + // FUTURE: probably also needs a `AmendingWithout(base Node, filter func(k,v) bool) NodeBuilder`, or similar. + // ("deletion" based APIs are also possible but both more complicated in interfaces added, and prone to accidentally quadratic usage.) + // FUTURE: there should be some stdlib `Copy` (?) methods that automatically look for this feature, and fallback if absent. + // Might include a wide range of point `Transform`, etc, methods. + // FUTURE: consider putting this (and others like it) in a `feature` package, if there begin to be enough of them and docs get crowded. } // MapIterator is an interface for traversing map nodes. diff --git a/_rsrch/nodesolution/node/basic/HACKME.md b/node/basic/HACKME.md similarity index 88% rename from _rsrch/nodesolution/node/basic/HACKME.md rename to node/basic/HACKME.md index fdf87e17..538abe29 100644 --- a/_rsrch/nodesolution/node/basic/HACKME.md +++ b/node/basic/HACKME.md @@ -18,20 +18,6 @@ This casting is not a concern for ipldfree types, because A) we don't have any kind of validation rules to make such casting worrying; and B) since our types are unexported, casting is still blocked by this anyway. -### pointer-vs-value inhabitant consistency - -Builders always return interfaces inhabited by pointers. -Some constructor functions (e.g. `String()`) return an interface inhabited by -the bare typedef and no pointer. - -This means you can get `*plainString` and `plainString` inhabitants of `Node`. - -(In contrast, codegen systems usually make a stance of consistently using -only pointer inhabitants -- so you can do type cast checks more easily.) - -This hasn't been a problem yet, but it's something to keep an eye on. -If this causes the slightest irritation, we'll standardize to pointer inhabitants. - ### about builders for scalars The assembler types for scalars (string, int, etc) are pretty funny-looking. diff --git a/_rsrch/nodesolution/node/basic/any.go b/node/basic/any.go similarity index 98% rename from _rsrch/nodesolution/node/basic/any.go rename to node/basic/any.go index 959b35fb..d42f8948 100644 --- a/_rsrch/nodesolution/node/basic/any.go +++ b/node/basic/any.go @@ -1,7 +1,7 @@ package basicnode import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) var ( diff --git a/_rsrch/nodesolution/node/basic/any_test.go b/node/basic/any_test.go similarity index 81% rename from _rsrch/nodesolution/node/basic/any_test.go rename to node/basic/any_test.go index 7a451d2d..43119b9b 100644 --- a/_rsrch/nodesolution/node/basic/any_test.go +++ b/node/basic/any_test.go @@ -3,7 +3,7 @@ package basicnode import ( "testing" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins/tests" + "github.com/ipld/go-ipld-prime/node/tests" ) func TestAnyBeingString(t *testing.T) { diff --git a/node/basic/bench_test.go b/node/basic/bench_test.go new file mode 100644 index 00000000..86b874d8 --- /dev/null +++ b/node/basic/bench_test.go @@ -0,0 +1,15 @@ +package basicnode + +import ( + "testing" + + "github.com/ipld/go-ipld-prime/node/tests" +) + +func BenchmarkSpec_Walk_Map3StrInt(b *testing.B) { + tests.BenchmarkSpec_Walk_Map3StrInt(b, Style__Any{}) +} + +func BenchmarkSpec_Walk_MapNStrMap3StrInt(b *testing.B) { + tests.BenchmarkSpec_Walk_MapNStrMap3StrInt(b, Style__Any{}) +} diff --git a/_rsrch/nodesolution/node/basic/bool.go b/node/basic/bool.go similarity index 95% rename from _rsrch/nodesolution/node/basic/bool.go rename to node/basic/bool.go index 86cafd2d..ba55355e 100644 --- a/_rsrch/nodesolution/node/basic/bool.go +++ b/node/basic/bool.go @@ -1,8 +1,8 @@ package basicnode import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" ) var ( @@ -13,7 +13,8 @@ var ( ) func NewBool(value bool) ipld.Node { - return plainBool(value) + v := plainBool(value) + return &v } // plainBool is a simple boxed boolean that complies with ipld.Node. diff --git a/_rsrch/nodesolution/node/basic/bytes.go b/node/basic/bytes.go similarity index 96% rename from _rsrch/nodesolution/node/basic/bytes.go rename to node/basic/bytes.go index c8fcb57c..e63ba0f4 100644 --- a/_rsrch/nodesolution/node/basic/bytes.go +++ b/node/basic/bytes.go @@ -1,8 +1,8 @@ package basicnode import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" ) var ( @@ -13,7 +13,8 @@ var ( ) func NewBytes(value []byte) ipld.Node { - return plainBytes(value) + v := plainBytes(value) + return &v } // plainBytes is a simple boxed byte slice that complies with ipld.Node. diff --git a/_rsrch/nodesolution/node/basic/float.go b/node/basic/float.go similarity index 96% rename from _rsrch/nodesolution/node/basic/float.go rename to node/basic/float.go index 9ccb3302..98f90583 100644 --- a/_rsrch/nodesolution/node/basic/float.go +++ b/node/basic/float.go @@ -1,8 +1,8 @@ package basicnode import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" ) var ( @@ -13,7 +13,8 @@ var ( ) func NewFloat(value float64) ipld.Node { - return plainFloat(value) + v := plainFloat(value) + return &v } // plainFloat is a simple boxed float that complies with ipld.Node. diff --git a/_rsrch/nodesolution/node/basic/int.go b/node/basic/int.go similarity index 95% rename from _rsrch/nodesolution/node/basic/int.go rename to node/basic/int.go index db1b134f..b00893ae 100644 --- a/_rsrch/nodesolution/node/basic/int.go +++ b/node/basic/int.go @@ -1,8 +1,8 @@ package basicnode import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" ) var ( @@ -13,7 +13,8 @@ var ( ) func NewInt(value int) ipld.Node { - return plainInt(value) + v := plainInt(value) + return &v } // plainInt is a simple boxed int that complies with ipld.Node. diff --git a/_rsrch/nodesolution/node/basic/link.go b/node/basic/link.go similarity index 96% rename from _rsrch/nodesolution/node/basic/link.go rename to node/basic/link.go index ae0563f7..d607cddd 100644 --- a/_rsrch/nodesolution/node/basic/link.go +++ b/node/basic/link.go @@ -1,8 +1,8 @@ package basicnode import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" ) var ( diff --git a/_rsrch/nodesolution/node/basic/list.go b/node/basic/list.go similarity index 98% rename from _rsrch/nodesolution/node/basic/list.go rename to node/basic/list.go index 14b0b34e..ee2e3101 100644 --- a/_rsrch/nodesolution/node/basic/list.go +++ b/node/basic/list.go @@ -1,8 +1,8 @@ package basicnode import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" ) var ( @@ -151,6 +151,9 @@ func (plainList__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error return mixins.ListAssembler{"list"}.BeginMap(0) } func (na *plainList__Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { + if sizeHint < 0 { + sizeHint = 0 + } // Allocate storage space. na.w.x = make([]ipld.Node, 0, sizeHint) // That's it; return self as the ListNodeAssembler. We already have all the right methods on this structure. diff --git a/_rsrch/nodesolution/node/basic/map.go b/node/basic/map.go similarity index 99% rename from _rsrch/nodesolution/node/basic/map.go rename to node/basic/map.go index c37e5c34..dcc3059c 100644 --- a/_rsrch/nodesolution/node/basic/map.go +++ b/node/basic/map.go @@ -3,8 +3,8 @@ package basicnode import ( "fmt" - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" ) var ( @@ -163,6 +163,9 @@ const ( ) func (na *plainMap__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { + if sizeHint < 0 { + sizeHint = 0 + } // Allocate storage space. na.w.t = make([]plainMap__Entry, 0, sizeHint) na.w.m = make(map[string]ipld.Node, sizeHint) diff --git a/_rsrch/nodesolution/node/basic/map_test.go b/node/basic/map_test.go similarity index 95% rename from _rsrch/nodesolution/node/basic/map_test.go rename to node/basic/map_test.go index 0ad67b12..3778a09a 100644 --- a/_rsrch/nodesolution/node/basic/map_test.go +++ b/node/basic/map_test.go @@ -3,7 +3,7 @@ package basicnode import ( "testing" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins/tests" + "github.com/ipld/go-ipld-prime/node/tests" ) func TestMap(t *testing.T) { diff --git a/_rsrch/nodesolution/node/basic/string.go b/node/basic/string.go similarity index 96% rename from _rsrch/nodesolution/node/basic/string.go rename to node/basic/string.go index 1691fcfd..d8850f81 100644 --- a/_rsrch/nodesolution/node/basic/string.go +++ b/node/basic/string.go @@ -1,8 +1,8 @@ package basicnode import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" ) var ( @@ -13,7 +13,8 @@ var ( ) func NewString(value string) ipld.Node { - return plainString(value) + v := plainString(value) + return &v } // plainString is a simple boxed string that complies with ipld.Node. diff --git a/_rsrch/nodesolution/node/basic/string_test.go b/node/basic/string_test.go similarity index 62% rename from _rsrch/nodesolution/node/basic/string_test.go rename to node/basic/string_test.go index d4ed153b..e9233047 100644 --- a/_rsrch/nodesolution/node/basic/string_test.go +++ b/node/basic/string_test.go @@ -3,7 +3,7 @@ package basicnode import ( "testing" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins/tests" + "github.com/ipld/go-ipld-prime/node/tests" ) func TestString(t *testing.T) { diff --git a/_rsrch/nodesolution/node/doc.go b/node/doc.go similarity index 100% rename from _rsrch/nodesolution/node/doc.go rename to node/doc.go diff --git a/_rsrch/nodesolution/node/gendemo/HACKME.md b/node/gendemo/HACKME.md similarity index 100% rename from _rsrch/nodesolution/node/gendemo/HACKME.md rename to node/gendemo/HACKME.md diff --git a/_rsrch/nodesolution/node/gendemo/HACKME_abbrevs.md b/node/gendemo/HACKME_abbrevs.md similarity index 100% rename from _rsrch/nodesolution/node/gendemo/HACKME_abbrevs.md rename to node/gendemo/HACKME_abbrevs.md diff --git a/_rsrch/nodesolution/node/gendemo/HACKME_scalars.md b/node/gendemo/HACKME_scalars.md similarity index 100% rename from _rsrch/nodesolution/node/gendemo/HACKME_scalars.md rename to node/gendemo/HACKME_scalars.md diff --git a/_rsrch/nodesolution/node/gendemo/HACKME_tradeoffs.md b/node/gendemo/HACKME_tradeoffs.md similarity index 100% rename from _rsrch/nodesolution/node/gendemo/HACKME_tradeoffs.md rename to node/gendemo/HACKME_tradeoffs.md diff --git a/_rsrch/nodesolution/node/gendemo/int.go b/node/gendemo/int.go similarity index 96% rename from _rsrch/nodesolution/node/gendemo/int.go rename to node/gendemo/int.go index 04be94fd..2dbdfa4d 100644 --- a/_rsrch/nodesolution/node/gendemo/int.go +++ b/node/gendemo/int.go @@ -1,8 +1,8 @@ package gendemo import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" ) var ( diff --git a/_rsrch/nodesolution/node/gendemo/map_K2_T2.go b/node/gendemo/map_K2_T2.go similarity index 99% rename from _rsrch/nodesolution/node/gendemo/map_K2_T2.go rename to node/gendemo/map_K2_T2.go index 3fd35be6..f9e53bda 100644 --- a/_rsrch/nodesolution/node/gendemo/map_K2_T2.go +++ b/node/gendemo/map_K2_T2.go @@ -6,7 +6,7 @@ package gendemo import ( "fmt" - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) // --- we need some types to use for keys and values: ---> diff --git a/_rsrch/nodesolution/node/gendemo/map_K_T.go b/node/gendemo/map_K_T.go similarity index 99% rename from _rsrch/nodesolution/node/gendemo/map_K_T.go rename to node/gendemo/map_K_T.go index d0ede3aa..0da037e6 100644 --- a/_rsrch/nodesolution/node/gendemo/map_K_T.go +++ b/node/gendemo/map_K_T.go @@ -5,7 +5,7 @@ package gendemo import ( "fmt" - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) // --- we need some types to use for keys and values: ---> diff --git a/_rsrch/nodesolution/node/gendemo/map_K_T_test.go b/node/gendemo/map_K_T_test.go similarity index 92% rename from _rsrch/nodesolution/node/gendemo/map_K_T_test.go rename to node/gendemo/map_K_T_test.go index bf24156a..ae8d775b 100644 --- a/_rsrch/nodesolution/node/gendemo/map_K_T_test.go +++ b/node/gendemo/map_K_T_test.go @@ -3,7 +3,7 @@ package gendemo import ( "testing" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins/tests" + "github.com/ipld/go-ipld-prime/node/tests" ) func TestGennedMapStrInt(t *testing.T) { diff --git a/_rsrch/nodesolution/node/gendemo/string.go b/node/gendemo/string.go similarity index 97% rename from _rsrch/nodesolution/node/gendemo/string.go rename to node/gendemo/string.go index ee367a16..8a86a489 100644 --- a/_rsrch/nodesolution/node/gendemo/string.go +++ b/node/gendemo/string.go @@ -1,8 +1,8 @@ package gendemo import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/node/mixins" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/node/mixins" ) var ( diff --git a/_rsrch/nodesolution/node/mixins/HACKME.md b/node/mixins/HACKME.md similarity index 100% rename from _rsrch/nodesolution/node/mixins/HACKME.md rename to node/mixins/HACKME.md diff --git a/_rsrch/nodesolution/node/mixins/boolMixin.go b/node/mixins/boolMixin.go similarity index 98% rename from _rsrch/nodesolution/node/mixins/boolMixin.go rename to node/mixins/boolMixin.go index 6a2dee1e..4c0ff3e5 100644 --- a/_rsrch/nodesolution/node/mixins/boolMixin.go +++ b/node/mixins/boolMixin.go @@ -1,7 +1,7 @@ package mixins import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) // Bool can be embedded in a struct to provide all the methods that diff --git a/_rsrch/nodesolution/node/mixins/bytesMixin.go b/node/mixins/bytesMixin.go similarity index 98% rename from _rsrch/nodesolution/node/mixins/bytesMixin.go rename to node/mixins/bytesMixin.go index 9a0917d9..15dcd041 100644 --- a/_rsrch/nodesolution/node/mixins/bytesMixin.go +++ b/node/mixins/bytesMixin.go @@ -1,7 +1,7 @@ package mixins import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) // Bytes can be embedded in a struct to provide all the methods that diff --git a/_rsrch/nodesolution/node/mixins/floatMixin.go b/node/mixins/floatMixin.go similarity index 98% rename from _rsrch/nodesolution/node/mixins/floatMixin.go rename to node/mixins/floatMixin.go index f58d024e..2c602b87 100644 --- a/_rsrch/nodesolution/node/mixins/floatMixin.go +++ b/node/mixins/floatMixin.go @@ -1,7 +1,7 @@ package mixins import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) // Float can be embedded in a struct to provide all the methods that diff --git a/_rsrch/nodesolution/node/mixins/intMixin.go b/node/mixins/intMixin.go similarity index 98% rename from _rsrch/nodesolution/node/mixins/intMixin.go rename to node/mixins/intMixin.go index 4bb9eee7..cbc7f18c 100644 --- a/_rsrch/nodesolution/node/mixins/intMixin.go +++ b/node/mixins/intMixin.go @@ -1,7 +1,7 @@ package mixins import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) // Int can be embedded in a struct to provide all the methods that diff --git a/_rsrch/nodesolution/node/mixins/linkMixin.go b/node/mixins/linkMixin.go similarity index 98% rename from _rsrch/nodesolution/node/mixins/linkMixin.go rename to node/mixins/linkMixin.go index ae095afb..28448eb0 100644 --- a/_rsrch/nodesolution/node/mixins/linkMixin.go +++ b/node/mixins/linkMixin.go @@ -1,7 +1,7 @@ package mixins import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) // Link can be embedded in a struct to provide all the methods that diff --git a/_rsrch/nodesolution/node/mixins/listMixin.go b/node/mixins/listMixin.go similarity index 98% rename from _rsrch/nodesolution/node/mixins/listMixin.go rename to node/mixins/listMixin.go index 5cab466f..f42e9e5d 100644 --- a/_rsrch/nodesolution/node/mixins/listMixin.go +++ b/node/mixins/listMixin.go @@ -1,7 +1,7 @@ package mixins import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) // List can be embedded in a struct to provide all the methods that diff --git a/_rsrch/nodesolution/node/mixins/mapMixin.go b/node/mixins/mapMixin.go similarity index 98% rename from _rsrch/nodesolution/node/mixins/mapMixin.go rename to node/mixins/mapMixin.go index c01a9e13..0dab58be 100644 --- a/_rsrch/nodesolution/node/mixins/mapMixin.go +++ b/node/mixins/mapMixin.go @@ -1,7 +1,7 @@ package mixins import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) // Map can be embedded in a struct to provide all the methods that diff --git a/_rsrch/nodesolution/node/mixins/stringMixin.go b/node/mixins/stringMixin.go similarity index 98% rename from _rsrch/nodesolution/node/mixins/stringMixin.go rename to node/mixins/stringMixin.go index 1a6871c2..3cb5f270 100644 --- a/_rsrch/nodesolution/node/mixins/stringMixin.go +++ b/node/mixins/stringMixin.go @@ -1,7 +1,7 @@ package mixins import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) // String can be embedded in a struct to provide all the methods that diff --git a/_rsrch/nodesolution/node/mixins/tmplMixin.txt b/node/mixins/tmplMixin.txt similarity index 98% rename from _rsrch/nodesolution/node/mixins/tmplMixin.txt rename to node/mixins/tmplMixin.txt index 55f8626e..46822e54 100644 --- a/_rsrch/nodesolution/node/mixins/tmplMixin.txt +++ b/node/mixins/tmplMixin.txt @@ -5,7 +5,7 @@ package mixins import ( - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) // @Kind@ can be embedded in a struct to provide all the methods that diff --git a/tests/HACKME.md b/node/tests/HACKME.md similarity index 100% rename from tests/HACKME.md rename to node/tests/HACKME.md diff --git a/tests/corpus/corpus.go b/node/tests/corpus/corpus.go similarity index 100% rename from tests/corpus/corpus.go rename to node/tests/corpus/corpus.go diff --git a/tests/corpus/corpus_test.go b/node/tests/corpus/corpus_test.go similarity index 100% rename from tests/corpus/corpus_test.go rename to node/tests/corpus/corpus_test.go diff --git a/tests/corpus/util.go b/node/tests/corpus/util.go similarity index 100% rename from tests/corpus/util.go rename to node/tests/corpus/util.go diff --git a/_rsrch/nodesolution/node/mixins/tests/mapBenchmarks.go b/node/tests/mapBenchmarks.go similarity index 97% rename from _rsrch/nodesolution/node/mixins/tests/mapBenchmarks.go rename to node/tests/mapBenchmarks.go index c89778b6..4846b85b 100644 --- a/_rsrch/nodesolution/node/mixins/tests/mapBenchmarks.go +++ b/node/tests/mapBenchmarks.go @@ -3,7 +3,7 @@ package tests import ( "testing" - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/must" ) diff --git a/_rsrch/nodesolution/node/mixins/tests/mapBenchmarks_test.go b/node/tests/mapBenchmarks_test.go similarity index 100% rename from _rsrch/nodesolution/node/mixins/tests/mapBenchmarks_test.go rename to node/tests/mapBenchmarks_test.go diff --git a/_rsrch/nodesolution/node/mixins/tests/mapFixtures.go b/node/tests/mapFixtures.go similarity index 95% rename from _rsrch/nodesolution/node/mixins/tests/mapFixtures.go rename to node/tests/mapFixtures.go index bb61919a..cb1ba9b8 100644 --- a/_rsrch/nodesolution/node/mixins/tests/mapFixtures.go +++ b/node/tests/mapFixtures.go @@ -3,7 +3,7 @@ package tests import ( "strconv" - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/must" ) diff --git a/_rsrch/nodesolution/node/mixins/tests/mapSpecs.go b/node/tests/mapSpecs.go similarity index 99% rename from _rsrch/nodesolution/node/mixins/tests/mapSpecs.go rename to node/tests/mapSpecs.go index 5e8c49b8..decc8c98 100644 --- a/_rsrch/nodesolution/node/mixins/tests/mapSpecs.go +++ b/node/tests/mapSpecs.go @@ -5,7 +5,7 @@ import ( . "github.com/warpfork/go-wish" - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/must" ) diff --git a/_rsrch/nodesolution/node/mixins/tests/marshalBenchmarks.go b/node/tests/marshalBenchmarks.go similarity index 91% rename from _rsrch/nodesolution/node/mixins/tests/marshalBenchmarks.go rename to node/tests/marshalBenchmarks.go index 3044ef01..bfb9ba50 100644 --- a/_rsrch/nodesolution/node/mixins/tests/marshalBenchmarks.go +++ b/node/tests/marshalBenchmarks.go @@ -8,10 +8,10 @@ import ( refmtjson "github.com/polydawn/refmt/json" "github.com/polydawn/refmt/tok" - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/codec" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec" "github.com/ipld/go-ipld-prime/must" - "github.com/ipld/go-ipld-prime/tests/corpus" + "github.com/ipld/go-ipld-prime/node/tests/corpus" ) // All of the marshalling and unmarshalling benchmark specs use JSON. @@ -66,7 +66,7 @@ func BenchmarkSpec_Marshal_MapNStrMap3StrInt(b *testing.B, ns ipld.NodeStyle) { for _, n := range []int{0, 1, 2, 4, 8, 16, 32} { b.Run(fmt.Sprintf("n=%d", n), func(b *testing.B) { msg := corpus.MapNStrMap3StrInt(n) - node := mustNodeFromJsonString(ns.NewBuilder(), msg) + node := mustNodeFromJsonString(ns, msg) b.ResetTimer() var buf bytes.Buffer diff --git a/_rsrch/nodesolution/node/mixins/tests/stringSpecs.go b/node/tests/stringSpecs.go similarity index 89% rename from _rsrch/nodesolution/node/mixins/tests/stringSpecs.go rename to node/tests/stringSpecs.go index 6cfbd83c..ba62ef67 100644 --- a/_rsrch/nodesolution/node/mixins/tests/stringSpecs.go +++ b/node/tests/stringSpecs.go @@ -5,7 +5,7 @@ import ( . "github.com/warpfork/go-wish" - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" + ipld "github.com/ipld/go-ipld-prime" ) func SpecTestString(t *testing.T, ns ipld.NodeStyle) { diff --git a/tests/traversalBenchmarks.go b/node/tests/traversalBenchmarks.go similarity index 74% rename from tests/traversalBenchmarks.go rename to node/tests/traversalBenchmarks.go index 3a1d03bc..bb55f0f4 100644 --- a/tests/traversalBenchmarks.go +++ b/node/tests/traversalBenchmarks.go @@ -5,13 +5,13 @@ import ( "testing" ipld "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/tests/corpus" + "github.com/ipld/go-ipld-prime/node/tests/corpus" "github.com/ipld/go-ipld-prime/traversal" ) -func BenchmarkSpec_Walk_Map3StrInt(b *testing.B, nb ipld.NodeBuilder) { - node := mustNodeFromJsonString(nb, corpus.Map3StrInt()) - sel := mustSelectorFromJsonString(nb, `{"a":{">":{".":{}}}}`) +func BenchmarkSpec_Walk_Map3StrInt(b *testing.B, ns ipld.NodeStyle) { + node := mustNodeFromJsonString(ns, corpus.Map3StrInt()) + sel := mustSelectorFromJsonString(ns, `{"a":{">":{".":{}}}}`) b.ResetTimer() var visitCountSanityCheck int @@ -27,12 +27,12 @@ func BenchmarkSpec_Walk_Map3StrInt(b *testing.B, nb ipld.NodeBuilder) { } } -func BenchmarkSpec_Walk_MapNStrMap3StrInt(b *testing.B, nb ipld.NodeBuilder) { - sel := mustSelectorFromJsonString(nb, `{"a":{">":{"a":{">":{".":{}}}}}}`) +func BenchmarkSpec_Walk_MapNStrMap3StrInt(b *testing.B, ns ipld.NodeStyle) { + sel := mustSelectorFromJsonString(ns, `{"a":{">":{"a":{">":{".":{}}}}}}`) for _, n := range []int{0, 1, 2, 4, 8, 16, 32} { b.Run(fmt.Sprintf("n=%d", n), func(b *testing.B) { - node := mustNodeFromJsonString(nb, corpus.MapNStrMap3StrInt(n)) + node := mustNodeFromJsonString(ns, corpus.MapNStrMap3StrInt(n)) b.ResetTimer() var visitCountSanityCheck int diff --git a/_rsrch/nodesolution/node/mixins/tests/unmarshalBenchmarks.go b/node/tests/unmarshalBenchmarks.go similarity index 91% rename from _rsrch/nodesolution/node/mixins/tests/unmarshalBenchmarks.go rename to node/tests/unmarshalBenchmarks.go index 42f4e24b..153e9bf4 100644 --- a/_rsrch/nodesolution/node/mixins/tests/unmarshalBenchmarks.go +++ b/node/tests/unmarshalBenchmarks.go @@ -7,9 +7,9 @@ import ( refmtjson "github.com/polydawn/refmt/json" - ipld "github.com/ipld/go-ipld-prime/_rsrch/nodesolution" - "github.com/ipld/go-ipld-prime/_rsrch/nodesolution/codec" - "github.com/ipld/go-ipld-prime/tests/corpus" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec" + "github.com/ipld/go-ipld-prime/node/tests/corpus" ) // All of the marshalling and unmarshalling benchmark specs use JSON. diff --git a/tests/util.go b/node/tests/util.go similarity index 52% rename from tests/util.go rename to node/tests/util.go index b8715ecc..5bf1b051 100644 --- a/tests/util.go +++ b/node/tests/util.go @@ -6,20 +6,27 @@ import ( refmtjson "github.com/polydawn/refmt/json" ipld "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/encoding" + "github.com/ipld/go-ipld-prime/codec" "github.com/ipld/go-ipld-prime/must" "github.com/ipld/go-ipld-prime/traversal/selector" ) -func mustNodeFromJsonString(nb ipld.NodeBuilder, str string) ipld.Node { - return must.Node(encoding.Unmarshal(nb, refmtjson.NewDecoder(bytes.NewBufferString(str)))) +// various benchmarks assign their final result here, +// in order to defuse the possibility of their work being elided. +var sink interface{} + +func mustNodeFromJsonString(ns ipld.NodeStyle, str string) ipld.Node { + nb := ns.NewBuilder() + must.NotError(codec.Unmarshal(nb, refmtjson.NewDecoder(bytes.NewBufferString(str)))) + return nb.Build() } -func mustSelectorFromJsonString(nb ipld.NodeBuilder, str string) selector.Selector { - // Needing an 'nb' parameter here is sort of off-topic, to be honest. + +func mustSelectorFromJsonString(ns ipld.NodeStyle, str string) selector.Selector { + // Needing an 'ns' parameter here is sort of off-topic, to be honest. // Someday the selector package will probably contain codegen'd nodes of its own schema, and we'll use those unconditionally. // For now... we'll just use whatever node you're already testing, because it oughta work // (and because it avoids hardcoding any other implementation that might cause import cycle funtimes.). - seldefn := mustNodeFromJsonString(nb, str) + seldefn := mustNodeFromJsonString(ns, str) sel, err := selector.ParseSelector(seldefn) must.NotError(err) return sel diff --git a/nodeBuilder.go b/nodeBuilder.go index 3add5e51..513b4f4b 100644 --- a/nodeBuilder.go +++ b/nodeBuilder.go @@ -1,161 +1,129 @@ package ipld -// NodeBuilder is an interface that describes creating new Node instances. -// -// The Node interface is entirely read-only methods; a Node is immutable. -// Thus, we need a NodeBuilder system for creating new ones; the builder -// is mutable, and when we're done accumulating mutations, we take the -// accumulated data and produce an immutable Node out of it. -// -// Separating mutation into NodeBuilder and keeping Node immutable makes -// it possible to perform caching (or rather, memoization, since there's no -// such thing as cache invalidation for immutable systems) of computed -// properties of Node; use copy-on-write algorithms for memory efficiency; -// and to generally build pleasant APIs. -// -// Each package in `go-ipld-prime//impl/*` that implements ipld.Node also -// has a NodeBuilder implementation that produces new nodes of that same -// package's type. -// -// The Node interface includes a method which returns a NodeBuilder; -// this builder must be able to produce a new node of the same concrete -// implementation as the original node. -// This is useful for algorithms that work on trees of nodes: this NodeBuilder -// getter will be used when an update deep in the tree causes a need to -// create several new nodes to propagate the change up through parent nodes. -// -// NodeBuilder instances obtained from `Node.NodeBuilder()` may carry some -// additional logic or constraints with them to the new Node they produce. -// For example, a Node which is implemented using reflection to bind to a -// natively-typed struct will yield a NodeBuilder which contains a -// `reflect.Type` handle it can use to create a new value of that native type; -// similarly, schema-typed Nodes will yield a NodeBuilder that keeps the schema -// info and type constraints from that Node! -// (Continuing the schema.TypedNode example: if you have a schema.TypedNode that is -// constrained to be of some `type Foo = {Bar:Baz}` type, then any new Node -// produced from its NodeBuilder will still answer -// `n.(schema.TypedNode).Type().Name()` as `Foo`; and if -// `n.NodeBuilder().AmendMap().Insert(...)` is called with nodes of unmatching -// type given to the insertion, the builder will error!) -// -// The NodeBuilder retrieved from a Node can also be used to do *updates*: -// consider the AmendMap and AmendList methods. These methods are useful -// not just for programmer convenience, but also because they can reuse memory, -// sharing any common segments of memory with the earlier Node. -// (In the NodeBuilder exposed by the `go-ipld-prime//impl/*` packages, these -// methods are equivalent to their Create* counterparts. As there's no -// "existing" node for them to refer to, it's treated the same as amending -// an empty node.) -type NodeBuilder interface { - CreateMap() (MapBuilder, error) - AmendMap() (MapBuilder, error) - CreateList() (ListBuilder, error) - AmendList() (ListBuilder, error) - CreateNull() (Node, error) - CreateBool(bool) (Node, error) - CreateInt(int) (Node, error) - CreateFloat(float64) (Node, error) - CreateString(string) (Node, error) - CreateBytes([]byte) (Node, error) - CreateLink(Link) (Node, error) +// NodeAssembler is the interface that describes all the ways we can set values +// in a node that's under construction. +// +// To create a Node, you should start with a NodeBuilder (which contains a +// superset of the NodeAssembler methods, and can return the finished Node +// from its `Build` method). +// +// Why do both this and the NodeBuilder interface exist? +// When creating trees of nodes, recursion works over the NodeAssembler interface. +// This is important to efficient library internals, because avoiding the +// requirement to be able to return a Node at any random point in the process +// relieves internals from needing to implement 'freeze' features. +// (This is useful in turn because implementing those 'freeze' features in a +// language without first-class/compile-time support for them (as golang is) +// would tend to push complexity and costs to execution time; we'd rather not.) +type NodeAssembler interface { + BeginMap(sizeHint int) (MapNodeAssembler, error) + BeginList(sizeHint int) (ListNodeAssembler, error) + AssignNull() error + AssignBool(bool) error + AssignInt(int) error + AssignFloat(float64) error + AssignString(string) error + AssignBytes([]byte) error + AssignLink(Link) error + + AssignNode(Node) error // if you already have a completely constructed subtree, this method puts the whole thing in place at once. + + // Style returns a NodeStyle describing what kind of value we're assembling. + // + // You often don't need this (because you should be able to + // just feed data and check errors), but it's here. + // + // Using `this.Style().NewBuilder()` to produce a new `Node`, + // then giving that node to `this.AssignNode(n)` should always work. + // (Note that this is not necessarily an _exclusive_ statement on what + // sort of values will be accepted by `this.AssignNode(n)`.) + Style() NodeStyle } -// MapBuilder is an interface for creating new Node instances of kind map. -// -// A MapBuilder is generally obtained by getting a NodeBuilder first, -// and then using CreateMap or AmendMap to begin. -// -// Methods mutate the builder's internal state; when done, call Build to -// produce a new immutable Node from the internal state. -// (After calling Build, future mutations may be rejected.) -// -// Insertion methods error if the key already exists. -// -// The BuilderForKeys and BuilderForValue functions return NodeBuilders -// that can be used to produce values for insertion. -// If you already have the data you're inserting, you can use those Nodes; -// if you don't, use these builders. -// (This is particularly relevant for typed nodes and bind nodes, since those -// have internal specializations, and not all NodeBuilders for them are equal.) -// Note that BuilderForValue requires a key as a parameter! -// This is because typed nodes which are structs may return different builders -// per field, specific to the field's type. -// -// You may be interested in the fluent package's fluent.MapBuilder equivalent -// for common usage with less error-handling boilerplate requirements. -type MapBuilder interface { - Insert(k, v Node) error - Delete(k Node) error - Build() (Node, error) +// MapNodeAssembler assembles a map node! (You guessed it.) +// +// Methods on MapNodeAssembler must be called in a valid order: +// assemble a key, then assemble a value, then loop as long as desired; +// when finished, call 'Finish'. +// +// Incorrect order invocations will panic. +// Calling AssembleKey twice in a row will panic; +// calling AssembleValue before finishing using the NodeAssembler from AssembleKey will panic; +// calling AssembleValue twice in a row will panic; +// etc. +// +// Note that the NodeAssembler yielded from AssembleKey has additional behavior: +// if the node assembled there matches a key already present in the map, +// that assembler will emit the error! +type MapNodeAssembler interface { + AssembleKey() NodeAssembler // must be followed by call to AssembleValue. + AssembleValue() NodeAssembler // must be called immediately after AssembleKey. + + AssembleDirectly(k string) (NodeAssembler, error) // shortcut combining AssembleKey and AssembleValue into one step; valid when the key is a string kind. - BuilderForKeys() NodeBuilder - BuilderForValue(k string) NodeBuilder + Finish() error - // FIXME we might need a way to reject invalid 'k' for BuilderForValue. - // We certainly can't wait until the insert, because we need the value builder before that (duh!). - // However, you probably should've applied the BuilderForKeys to the key value already, - // and that should've told you about most errors...? - // Hrm. Not sure if we wanna rely on this. - // Panic? or return an error, and be sad about breaking chaining of calls? or return a curried-error thunk? - // Or can we shift all the responsibility to BuilderForKeys after all (with panic as 'unreachable' fallback)? + // KeyStyle returns a NodeStyle that knows how to build keys of a type this map uses. // - // - for maps with typed keys that have constraints, the rejection should've come from the key builder. fine. - // - builderForValue also doesn't need to vary at all in this case; you could've given an empty 'k' and cached that one. - // - though note we haven't exposed a way to *detect* that yet (and it's questioned whether we should until more profiling/optimization info comes in). - // - for structs, the rejection *could* come from the key builder, but we haven't decided if that's required or not. - // - requiring a builder for keys that whitelists the valid keys but still returns plain string nodes is viable... - // - but a little odd looking, since the returned thing is going to be a plain untyped string kind node. - // - in other words, the type wouldn't carry info about the constraints it has passed through; not wrong, but perhaps a design smell. - // - for codegen'd impls, this would be compiled into a string switch and be pretty cheap. viable. - // - for runtime-wrapper typed.Node impls... still viable, but a little heavier. + // You often don't need this (because you should be able to + // just feed data and check errors), but it's here. + // + // For all Data Model maps, this will answer with a basic concept of "string". + // For Schema typed maps, this may answer with a more complex type (potentially even a struct type). + KeyStyle() NodeStyle + + // ValueStyle returns a NodeStyle that knows how to build values this map can contain. + // + // You often don't need this (because you should be able to + // just feed data and check errors), but it's here. + // + // ValueStyle requires a parameter describing the key in order to say what + // NodeStyle will be acceptable as a value for that key, because when using + // struct types (or union types) from the Schemas system, they behave as maps + // but have different acceptable types for each field (or member, for unions). + // For plain maps (that is, not structs or unions masquerading as maps), + // the empty string can be used as a parameter, and the returned NodeStyle + // can be assumed applicable for all values. + // Using an empty string for a struct or union will return a nil NodeStyle. + // (Design note: a string is sufficient for the parameter here rather than + // a full Node, because the only cases where the value types vary are also + // cases where the keys may not be complex.) + ValueStyle(k string) NodeStyle } -// ListBuilder is an interface for creating new Node instances of kind list. -// -// A ListBuilder is generally obtained by getting a NodeBuilder first, -// and then using CreateList or AmendList to begin. -// -// Methods mutate the builder's internal state; when done, call Build to -// produce a new immutable Node from the internal state. -// (After calling Build, future mutations may be rejected.) -// -// Methods may error when handling typed lists if non-matching types are inserted. -// -// The BuilderForValue function returns a NodeBuilder -// that can be used to produce values for insertion. -// If you already have the data you're inserting, you can use those Nodes; -// if you don't, use these builders. -// (This is particularly relevant for typed nodes and bind nodes, since those -// have internal specializations, and not all NodeBuilders for them are equal.) -// Note that BuilderForValue requires an index as a parameter! -// In most cases, this is not relevant and the method returns a constant NodeBuilder; -// however, typed nodes which are structs and have list representations may -// return different builders per index, corresponding to the types of its fields. -// -// You may be interested in the fluent package's fluent.ListBuilder equivalent -// for common usage with less error-handling boilerplate requirements. -type ListBuilder interface { - AppendAll([]Node) error - Append(v Node) error - Set(idx int, v Node) error - Build() (Node, error) +type ListNodeAssembler interface { + AssembleValue() NodeAssembler - BuilderForValue(idx int) NodeBuilder + Finish() error - // FIXME the question about rejection of invalid idx applies here as well, - // for all the same reasons it came up for BuilderForValue on maps: - // structs with tuple representation provoke all the exact same issues. + // ValueStyle returns a NodeStyle that knows how to build values this map can contain. + // + // You often don't need this (because you should be able to + // just feed data and check errors), but it's here. + // + // In contrast to the `MapNodeAssembler.ValueStyle(key)` function, + // to determine the ValueStyle for lists we need no parameters; + // lists always contain one value type (even if it's "any"). + ValueStyle() NodeStyle } -// future: add AppendIterator() methods (when we've implemented iterators!) - -// future: add InsertConverting(map[string]interface{}) and similar methods. -// (some open questions about how useful that is, given ipldbind should likely be more efficient, depending on use case.) +type NodeBuilder interface { + NodeAssembler -// future: define key ordering semantics during map insertion. -// methods for re-ordering will probably be wanted someday. + // Build returns the new value after all other assembly has been completed. + // + // A method on the NodeAssembler that finishes assembly of the data must + // be called first (e.g., any of the "Assign*" methods, or "Finish" if + // the assembly was for a map or a list); that finishing method still has + // all responsibility for validating the assembled data and returning + // any errors from that process. + // (Correspondingly, there is no error return from this method.) + Build() Node -// review: MapBuilder.Delete as an API is dangerously prone to usage which is accidentally quadratic. -// https://accidentallyquadratic.tumblr.com/post/157496054437/ruby-reject describes a similar issue. -// an internal implementation which accumulates oplogs is one fix, but not a joy either (not alloc free). -// a totally different API -- say, `NodeBuilder.AmendMapWithout(...)` -- might be the best approach. + // Resets the builder. It can hereafter be used again. + // Reusing a NodeBuilder can reduce allocations and improve performance. + // + // Only call this if you're going to reuse the builder. + // (Otherwise, it's unnecessary, and may cause an unwanted allocation). + Reset() +} diff --git a/schema/tests/testsForStructs.go b/schema/tests/testsForStructs.go index e2695218..615f19fc 100644 --- a/schema/tests/testsForStructs.go +++ b/schema/tests/testsForStructs.go @@ -6,7 +6,6 @@ import ( . "github.com/warpfork/go-wish" ipld "github.com/ipld/go-ipld-prime" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" "github.com/ipld/go-ipld-prime/schema" ) @@ -21,24 +20,27 @@ func TestFoo(t *testing.T, newNb func(typ schema.Type) ipld.NodeBuilder) { }, schema.StructRepresentation_Map{}, ) - nbString := newNb(tString) - nbStroct := newNb(tStroct) var n1 ipld.Node t.Run("test building", func(t *testing.T) { t.Run("all values valid", func(t *testing.T) { - mb, err := nbStroct.CreateMap() + nb := newNb(tStroct) + ma, err := nb.BeginMap(3) Wish(t, err, ShouldEqual, nil) - // Set 'f1' to a valid, typed string. - v, _ := nbString.CreateString("asdf") - Wish(t, mb.Insert(ipldfree.String("f1"), v), ShouldEqual, nil) + // Set 'f1' to a valid string. + va, err := ma.AssembleDirectly("f1") + Wish(t, err, ShouldEqual, nil) + Wish(t, va.AssignString("asdf"), ShouldEqual, nil) // Skip setting 'f2' -- it's optional. // Set 'f3' to null. Nulls aren't typed. - Wish(t, mb.Insert(ipldfree.String("f3"), ipld.Null), ShouldEqual, nil) - // Set 'f4' to a valid, typed string. - v, _ = nbString.CreateString("qwer") - Wish(t, mb.Insert(ipldfree.String("f4"), v), ShouldEqual, nil) - n1, err = mb.Build() + va, err = ma.AssembleDirectly("f3") + Wish(t, err, ShouldEqual, nil) + Wish(t, va.AssignNull(), ShouldEqual, nil) + // Set 'f4' to a valid string. + va, err = ma.AssembleDirectly("f4") Wish(t, err, ShouldEqual, nil) + Wish(t, va.AssignString("qwer"), ShouldEqual, nil) + Wish(t, ma.Finish(), ShouldEqual, nil) + n1 = nb.Build() }) t.Run("wrong type rejected", func(t *testing.T) { @@ -57,7 +59,9 @@ func TestFoo(t *testing.T, newNb func(typ schema.Type) ipld.NodeBuilder) { t.Run("regular fields", func(t *testing.T) { v, err := n1.LookupString("f1") Wish(t, err, ShouldEqual, nil) - v2, _ := nbString.CreateString("asdf") + nb := newNb(tString) + Require(t, nb.AssignString("asdf"), ShouldEqual, nil) + v2 := nb.Build() Wish(t, v, ShouldEqual, v2) }) t.Run("optional absent fields", func(t *testing.T) { diff --git a/schema/typedNode.go b/schema/typedNode.go index a595953d..2e608c83 100644 --- a/schema/typedNode.go +++ b/schema/typedNode.go @@ -61,13 +61,13 @@ type TypedNode interface { // with a code-gen'd node builder while utilizing the automatic loading facilities // of the traversal package, you could write a LinkNodeBuilderChooser as follows: // -// func LinkNodeBuilderChooser(lnk ipld.Link, lnkCtx ipld.LinkContext) ipld.NodeBuilder { +// func LinkNodeBuilderChooser(lnk ipld.Link, lnkCtx ipld.LinkContext) ipld.NodeStyle { // if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { -// return tlnkNd.LinkTargetNodeBuilder() +// return tlnkNd.LinkTargetNodeStyle() // } -// return ipldfree.NodeBuilder() +// return basicnode.Style__Any{} // } // type TypedLinkNode interface { - LinkTargetNodeBuilder() ipld.NodeBuilder + LinkTargetNodeStyle() ipld.NodeStyle } diff --git a/tests/building.go b/tests/building.go deleted file mode 100644 index 16d46baa..00000000 --- a/tests/building.go +++ /dev/null @@ -1,90 +0,0 @@ -package tests - -import ( - "testing" - - "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/fluent" - - . "github.com/warpfork/go-wish" -) - -func TestBuildingScalars(t *testing.T, nb ipld.NodeBuilder) { - t.Run("null node", func(t *testing.T) { - n := fluent.WrapNodeBuilder(nb).CreateNull() - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Null) - Wish(t, n.IsNull(), ShouldEqual, true) - }) - t.Run("bool node", func(t *testing.T) { - n := fluent.WrapNodeBuilder(nb).CreateBool(true) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Bool) - Wish(t, n.IsNull(), ShouldEqual, false) - Wish(t, fluent.WrapNode(n).AsBool(), ShouldEqual, true) - }) - t.Run("int node", func(t *testing.T) { - n := fluent.WrapNodeBuilder(nb).CreateInt(17) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Int) - Wish(t, n.IsNull(), ShouldEqual, false) - Wish(t, fluent.WrapNode(n).AsInt(), ShouldEqual, 17) - }) - t.Run("float node", func(t *testing.T) { - n := fluent.WrapNodeBuilder(nb).CreateFloat(0.122) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Float) - Wish(t, n.IsNull(), ShouldEqual, false) - Wish(t, fluent.WrapNode(n).AsFloat(), ShouldEqual, 0.122) - }) - t.Run("string node", func(t *testing.T) { - n := fluent.WrapNodeBuilder(nb).CreateString("asdf") - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_String) - Wish(t, n.IsNull(), ShouldEqual, false) - Wish(t, fluent.WrapNode(n).AsString(), ShouldEqual, "asdf") - }) - t.Run("bytes node", func(t *testing.T) { - n := fluent.WrapNodeBuilder(nb).CreateBytes([]byte{65, 66}) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Bytes) - Wish(t, n.IsNull(), ShouldEqual, false) - Wish(t, fluent.WrapNode(n).AsBytes(), ShouldEqual, []byte{65, 66}) - }) -} - -func TestBuildingRecursives(t *testing.T, nb ipld.NodeBuilder) { - t.Run("short list node", func(t *testing.T) { - nb := fluent.WrapNodeBuilder(nb) - n := nb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateString("asdf")) - }) - Wish(t, fluent.WrapNode(n).LookupIndex(0).AsString(), ShouldEqual, "asdf") - }) - t.Run("nested list node", func(t *testing.T) { - nb := fluent.WrapNodeBuilder(nb) - n := nb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateString("asdf")) - })) - lb.Append(vnb.CreateString("quux")) - }) - nf := fluent.WrapNode(n) - Wish(t, nf.ReprKind(), ShouldEqual, ipld.ReprKind_List) - Wish(t, nf.LookupIndex(0).LookupIndex(0).AsString(), ShouldEqual, "asdf") - Wish(t, nf.LookupIndex(1).AsString(), ShouldEqual, "quux") - }) - t.Run("long list node", func(t *testing.T) { - nb := fluent.WrapNodeBuilder(nb) - n := nb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Set(9, vnb.CreateString("quux")) - lb.Set(19, vnb.CreateString("quuux")) - }) - nf := fluent.WrapNode(n) - Wish(t, nf.ReprKind(), ShouldEqual, ipld.ReprKind_List) - Wish(t, nf.Length(), ShouldEqual, 20) - Wish(t, nf.LookupIndex(9).AsString(), ShouldEqual, "quux") - Wish(t, nf.LookupIndex(19).AsString(), ShouldEqual, "quuux") - }) - - // todo map tests - - // todo list append tests - // (appends will require putting the GetNodeBuilder on Node iface!) - - // todo list append at odd sizes tests -} diff --git a/tests/marshalBenchmarks.go b/tests/marshalBenchmarks.go deleted file mode 100644 index 493b2720..00000000 --- a/tests/marshalBenchmarks.go +++ /dev/null @@ -1,68 +0,0 @@ -package tests - -import ( - "bytes" - "fmt" - "testing" - - refmtjson "github.com/polydawn/refmt/json" - - ipld "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/encoding" - "github.com/ipld/go-ipld-prime/tests/corpus" -) - -// All of the marshalling and unmarshalling benchmark specs use JSON. -// This does mean we're measuring a bunch of stuff that has nothing to do -// with the core operations of the Node/NodeBuilder interface. -// We do this so that: -// - we get a reasonable picture of how much time is spent in the IPLD Data Model -// versus how much time is spent in the serialization efforts; -// - we can make direct comparisons to the standard library json marshalling -// and unmarshalling, thus having a back-of-the-envelope baseline to compare. - -func BenchmarkSpec_Marshal_Map3StrInt(b *testing.B, nb ipld.NodeBuilder) { - msg := corpus.Map3StrInt() - node := mustNodeFromJsonString(nb, msg) - b.ResetTimer() - - var buf bytes.Buffer - var err error - for i := 0; i < b.N; i++ { - buf = bytes.Buffer{} - err = encoding.Marshal(node, refmtjson.NewEncoder(&buf, refmtjson.EncodeOptions{})) - } - - b.StopTimer() - if err != nil { - b.Fatalf("marshal errored: %s", err) - } - if buf.String() != msg { - b.Fatalf("marshal didn't match corpus") - } -} - -func BenchmarkSpec_Marshal_MapNStrMap3StrInt(b *testing.B, nb ipld.NodeBuilder) { - for _, n := range []int{0, 1, 2, 4, 8, 16, 32} { - b.Run(fmt.Sprintf("n=%d", n), func(b *testing.B) { - msg := corpus.MapNStrMap3StrInt(n) - node := mustNodeFromJsonString(nb, msg) - b.ResetTimer() - - var buf bytes.Buffer - var err error - for i := 0; i < b.N; i++ { - buf = bytes.Buffer{} - err = encoding.Marshal(node, refmtjson.NewEncoder(&buf, refmtjson.EncodeOptions{})) - } - - b.StopTimer() - if err != nil { - b.Fatalf("marshal errored: %s", err) - } - if buf.String() != msg { - b.Fatalf("marshal didn't match corpus") - } - }) - } -} diff --git a/tests/marshalling.go b/tests/marshalling.go deleted file mode 100644 index 4d084121..00000000 --- a/tests/marshalling.go +++ /dev/null @@ -1,102 +0,0 @@ -package tests - -import ( - "testing" - - "github.com/polydawn/refmt/tok" - . "github.com/warpfork/go-wish" - - "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/encoding" - "github.com/ipld/go-ipld-prime/fluent" -) - -// TokenBucket acts as a TokenSink; you can dump data into it and then -// do test assertions on it with go-wish. -type TokenBucket []tok.Token - -func (tb *TokenBucket) Step(consume *tok.Token) (done bool, err error) { - if tb == nil { - *tb = make(TokenBucket, 0, 10) - } - *tb = append(*tb, *consume) - return false, nil -} - -// This should really be a utility func in refmt tok. -.- -func (tb TokenBucket) Minimalize() TokenBucket { - for i, v := range tb { - switch v.Type { - case tok.TMapOpen: - tb[i] = tok.Token{Type: v.Type, Length: v.Length, Tagged: v.Tagged, Tag: v.Tag} - case tok.TMapClose: - tb[i] = tok.Token{Type: v.Type} - case tok.TArrOpen: - tb[i] = tok.Token{Type: v.Type, Length: v.Length, Tagged: v.Tagged, Tag: v.Tag} - case tok.TArrClose: - tb[i] = tok.Token{Type: v.Type} - case tok.TNull: - tb[i] = tok.Token{Type: v.Type, Tagged: v.Tagged, Tag: v.Tag} - case tok.TString: - tb[i] = tok.Token{Type: v.Type, Str: v.Str, Tagged: v.Tagged, Tag: v.Tag} - case tok.TBytes: - tb[i] = tok.Token{Type: v.Type, Bytes: v.Bytes, Tagged: v.Tagged, Tag: v.Tag} - case tok.TBool: - tb[i] = tok.Token{Type: v.Type, Bool: v.Bool, Tagged: v.Tagged, Tag: v.Tag} - case tok.TInt: - tb[i] = tok.Token{Type: v.Type, Int: v.Int, Tagged: v.Tagged, Tag: v.Tag} - case tok.TUint: - tb[i] = tok.Token{Type: v.Type, Uint: v.Uint, Tagged: v.Tagged, Tag: v.Tag} - case tok.TFloat64: - tb[i] = tok.Token{Type: v.Type, Float64: v.Float64, Tagged: v.Tagged, Tag: v.Tag} - } - } - return tb -} - -func TestScalarMarshal(t *testing.T, nb ipld.NodeBuilder) { - t.Run("null node", func(t *testing.T) { - n, _ := nb.CreateNull() - var tb TokenBucket - err := encoding.Marshal(n, &tb) - Wish(t, err, ShouldEqual, nil) - Wish(t, tb, ShouldEqual, TokenBucket{ - {Type: tok.TNull}, - }) - }) -} - -func TestRecursiveMarshal(t *testing.T, nb ipld.NodeBuilder) { - t.Run("short list node", func(t *testing.T) { - n := fluent.WrapNodeBuilder(nb).CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateString("asdf")) - }) - var tb TokenBucket - err := encoding.Marshal(n, &tb) - Wish(t, err, ShouldEqual, nil) - Wish(t, tb.Minimalize(), ShouldEqual, TokenBucket{ - {Type: tok.TArrOpen, Length: 1}, - {Type: tok.TString, Str: "asdf"}, - {Type: tok.TArrClose}, - }) - }) - t.Run("nested list node", func(t *testing.T) { - n := fluent.WrapNodeBuilder(nb).CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateString("asdf")) - })) - lb.Append(vnb.CreateString("quux")) - }) - var tb TokenBucket - err := encoding.Marshal(n, &tb) - Wish(t, err, ShouldEqual, nil) - Wish(t, tb.Minimalize(), ShouldEqual, TokenBucket{ - {Type: tok.TArrOpen, Length: 2}, - {Type: tok.TArrOpen, Length: 1}, - {Type: tok.TString, Str: "asdf"}, - {Type: tok.TArrClose}, - {Type: tok.TString, Str: "quux"}, - {Type: tok.TArrClose}, - }) - }) -} diff --git a/tests/unmarshalBenchmarks.go b/tests/unmarshalBenchmarks.go deleted file mode 100644 index 870ad704..00000000 --- a/tests/unmarshalBenchmarks.go +++ /dev/null @@ -1,68 +0,0 @@ -package tests - -import ( - "bytes" - "fmt" - "testing" - - refmtjson "github.com/polydawn/refmt/json" - - ipld "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/encoding" - "github.com/ipld/go-ipld-prime/tests/corpus" -) - -// All of the marshalling and unmarshalling benchmark specs use JSON. -// This does mean we're measuring a bunch of stuff that has nothing to do -// with the core operations of the Node/NodeBuilder interface. -// We do this so that: -// - we get a reasonable picture of how much time is spent in the IPLD Data Model -// versus how much time is spent in the serialization efforts; -// - we can make direct comparisons to the standard library json marshalling -// and unmarshalling, thus having a back-of-the-envelope baseline to compare. - -func BenchmarkSpec_Unmarshal_Map3StrInt(b *testing.B, nb ipld.NodeBuilder) { - msg := corpus.Map3StrInt() - b.ResetTimer() - - var node ipld.Node - var err error - for i := 0; i < b.N; i++ { - node, err = encoding.Unmarshal(nb, refmtjson.NewDecoder(bytes.NewBufferString(msg))) - } - - b.StopTimer() - if err != nil { - b.Fatalf("unmarshal errored: %s", err) - } - var buf bytes.Buffer - encoding.Marshal(node, refmtjson.NewEncoder(&buf, refmtjson.EncodeOptions{})) - if buf.String() != msg { - b.Fatalf("remarshal didn't match corpus") - } -} - -func BenchmarkSpec_Unmarshal_MapNStrMap3StrInt(b *testing.B, nb ipld.NodeBuilder) { - for _, n := range []int{0, 1, 2, 4, 8, 16, 32} { - b.Run(fmt.Sprintf("n=%d", n), func(b *testing.B) { - msg := corpus.MapNStrMap3StrInt(n) - b.ResetTimer() - - var node ipld.Node - var err error - for i := 0; i < b.N; i++ { - node, err = encoding.Unmarshal(nb, refmtjson.NewDecoder(bytes.NewBufferString(msg))) - } - - b.StopTimer() - if err != nil { - b.Fatalf("unmarshal errored: %s", err) - } - var buf bytes.Buffer - encoding.Marshal(node, refmtjson.NewEncoder(&buf, refmtjson.EncodeOptions{})) - if buf.String() != msg { - b.Fatalf("remarshal didn't match corpus") - } - }) - } -} diff --git a/tests/unmarshalling.go b/tests/unmarshalling.go deleted file mode 100644 index b2b57386..00000000 --- a/tests/unmarshalling.go +++ /dev/null @@ -1,131 +0,0 @@ -package tests - -import ( - "testing" - - "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" -) - -// 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 TestScalarUnmarshal(t *testing.T, nb ipld.NodeBuilder) { - t.Run("null node", func(t *testing.T) { - tb := &TokenSourceBucket{tokens: []tok.Token{ - {Type: tok.TNull}, - }} - n, err := encoding.Unmarshal(nb, tb) - Wish(t, err, ShouldEqual, nil) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Null) - Wish(t, tb.read, ShouldEqual, 1) - }) - t.Run("int node", func(t *testing.T) { - tb := &TokenSourceBucket{tokens: []tok.Token{ - {Type: tok.TInt, Int: 1400}, - }} - n, err := encoding.Unmarshal(nb, tb) - Wish(t, err, ShouldEqual, nil) - Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Int) - Wish(t, fluent.WrapNode(n).AsInt(), ShouldEqual, 1400) - Wish(t, tb.read, ShouldEqual, 1) - }) - t.Run("string node", func(t *testing.T) { - tb := &TokenSourceBucket{tokens: []tok.Token{ - {Type: tok.TString, Str: "zooooom"}, - }} - 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) - }) -} - -func TestRecursiveUnmarshal(t *testing.T, nb ipld.NodeBuilder) { - t.Run("short list node", func(t *testing.T) { - tb := &TokenSourceBucket{tokens: []tok.Token{ - {Type: tok.TArrOpen, Length: 1}, - {Type: tok.TString, Str: "asdf"}, - {Type: tok.TArrClose}, - }} - n, err := encoding.Unmarshal(nb, tb) - Require(t, err, ShouldEqual, nil) - Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_List) - Require(t, n.Length(), ShouldEqual, 1) - Wish(t, fluent.WrapNode(n).LookupIndex(0).ReprKind(), ShouldEqual, ipld.ReprKind_String) - Wish(t, fluent.WrapNode(n).LookupIndex(0).AsString(), ShouldEqual, "asdf") - Wish(t, tb.read, ShouldEqual, 3) - }) - t.Run("nested list node", func(t *testing.T) { - tb := &TokenSourceBucket{tokens: []tok.Token{ - {Type: tok.TArrOpen, Length: 2}, - {Type: tok.TArrOpen, Length: 1}, - {Type: tok.TString, Str: "asdf"}, - {Type: tok.TArrClose}, - {Type: tok.TString, Str: "quux"}, - {Type: tok.TArrClose}, - }} - n, err := encoding.Unmarshal(nb, tb) - Require(t, err, ShouldEqual, nil) - Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_List) - Wish(t, n.Length(), ShouldEqual, 2) - Require(t, fluent.WrapNode(n).LookupIndex(0).ReprKind(), ShouldEqual, ipld.ReprKind_List) - Wish(t, fluent.WrapNode(n).LookupIndex(0).Length(), ShouldEqual, 1) - Wish(t, fluent.WrapNode(n).LookupIndex(0).LookupIndex(0).ReprKind(), ShouldEqual, ipld.ReprKind_String) - Wish(t, fluent.WrapNode(n).LookupIndex(0).LookupIndex(0).AsString(), ShouldEqual, "asdf") - Wish(t, tb.read, ShouldEqual, 6) - }) - t.Run("short map node", func(t *testing.T) { - tb := &TokenSourceBucket{tokens: []tok.Token{ - {Type: tok.TMapOpen, Length: 1}, - {Type: tok.TString, Str: "asdf"}, - {Type: tok.TString, Str: "zomzom"}, - {Type: tok.TMapClose}, - }} - n, err := encoding.Unmarshal(nb, tb) - Require(t, err, ShouldEqual, nil) - Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Require(t, n.Length(), ShouldEqual, 1) - Require(t, fluent.AllKeyStrings(fluent.WrapNode(n)), ShouldEqual, []string{"asdf"}) - Wish(t, fluent.WrapNode(n).LookupString("asdf").ReprKind(), ShouldEqual, ipld.ReprKind_String) - Wish(t, fluent.WrapNode(n).LookupString("asdf").AsString(), ShouldEqual, "zomzom") - Wish(t, tb.read, ShouldEqual, 4) - }) - t.Run("nested map node", func(t *testing.T) { - tb := &TokenSourceBucket{tokens: []tok.Token{ - {Type: tok.TMapOpen, Length: 2}, - {Type: tok.TString, Str: "asdf"}, - {Type: tok.TMapOpen, Length: 1}, - {Type: tok.TString, Str: "awoo"}, - {Type: tok.TString, Str: "gah"}, - {Type: tok.TMapClose}, - {Type: tok.TString, Str: "zyzzy"}, - {Type: tok.TInt, Int: 9}, - {Type: tok.TMapClose}, - }} - n, err := encoding.Unmarshal(nb, tb) - Require(t, err, ShouldEqual, nil) - Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Require(t, n.Length(), ShouldEqual, 2) - Require(t, fluent.AllKeyStrings(fluent.WrapNode(n)), ShouldEqual, []string{"asdf", "zyzzy"}) - Wish(t, fluent.WrapNode(n).LookupString("asdf").ReprKind(), ShouldEqual, ipld.ReprKind_Map) - Wish(t, fluent.WrapNode(n).LookupString("asdf").LookupString("awoo").AsString(), ShouldEqual, "gah") - Wish(t, fluent.WrapNode(n).LookupString("zyzzy").AsInt(), ShouldEqual, 9) - Wish(t, tb.read, ShouldEqual, 9) - }) -} diff --git a/thunks.go b/thunks.go deleted file mode 100644 index 54df166b..00000000 --- a/thunks.go +++ /dev/null @@ -1,76 +0,0 @@ -package ipld - -var Null Node = nullNode{} -var Undef Node = undefNode{} - -type nullNode struct{} - -func (nullNode) ReprKind() ReprKind { - return ReprKind_Null -} -func (nullNode) LookupString(key string) (Node, error) { - return nil, ErrWrongKind{MethodName: "LookupString", AppropriateKind: ReprKindSet_JustMap, ActualKind: ReprKind_Null} -} -func (nullNode) Lookup(key Node) (Node, error) { - return nil, ErrWrongKind{MethodName: "Lookup", AppropriateKind: ReprKindSet_JustMap, ActualKind: ReprKind_Null} -} -func (nullNode) LookupIndex(idx int) (Node, error) { - return nil, ErrWrongKind{MethodName: "LookupIndex", AppropriateKind: ReprKindSet_JustList, ActualKind: ReprKind_Null} -} -func (nullNode) LookupSegment(seg PathSegment) (Node, error) { - return nil, ErrWrongKind{MethodName: "LookupSegment", AppropriateKind: ReprKindSet_Recursive, ActualKind: ReprKind_Null} -} -func (nullNode) MapIterator() MapIterator { - return mapIteratorReject{ErrWrongKind{MethodName: "MapIterator", AppropriateKind: ReprKindSet_JustMap, ActualKind: ReprKind_Null}} -} -func (nullNode) ListIterator() ListIterator { - return listIteratorReject{ErrWrongKind{MethodName: "ListIterator", AppropriateKind: ReprKindSet_JustList, ActualKind: ReprKind_Null}} -} -func (nullNode) Length() int { - return -1 -} -func (nullNode) IsUndefined() bool { - return false -} -func (nullNode) IsNull() bool { - return true -} -func (nullNode) AsBool() (bool, error) { - return false, ErrWrongKind{MethodName: "AsBool", AppropriateKind: ReprKindSet_JustBool, ActualKind: ReprKind_Null} -} -func (nullNode) AsInt() (int, error) { - return 0, ErrWrongKind{MethodName: "AsInt", AppropriateKind: ReprKindSet_JustInt, ActualKind: ReprKind_Null} -} -func (nullNode) AsFloat() (float64, error) { - return 0, ErrWrongKind{MethodName: "AsFloat", AppropriateKind: ReprKindSet_JustFloat, ActualKind: ReprKind_Null} -} -func (nullNode) AsString() (string, error) { - return "", ErrWrongKind{MethodName: "AsString", AppropriateKind: ReprKindSet_JustString, ActualKind: ReprKind_Null} -} -func (nullNode) AsBytes() ([]byte, error) { - return nil, ErrWrongKind{MethodName: "AsBytes", AppropriateKind: ReprKindSet_JustBytes, ActualKind: ReprKind_Null} -} -func (nullNode) AsLink() (Link, error) { - return nil, ErrWrongKind{MethodName: "AsLink", AppropriateKind: ReprKindSet_JustLink, ActualKind: ReprKind_Null} -} -func (nullNode) NodeBuilder() NodeBuilder { - panic("cannot build null nodes") -} - -type undefNode struct{ nullNode } - -func (undefNode) IsUndefined() bool { - return true -} -func (undefNode) IsNull() bool { - return false -} - -type mapIteratorReject struct{ err error } -type listIteratorReject struct{ err error } - -func (itr mapIteratorReject) Next() (Node, Node, error) { return nil, nil, itr.err } -func (itr mapIteratorReject) Done() bool { return false } - -func (itr listIteratorReject) Next() (int, Node, error) { return -1, nil, itr.err } -func (itr listIteratorReject) Done() bool { return false } diff --git a/traversal/common.go b/traversal/common.go index 6cd58bf1..5f5e4725 100644 --- a/traversal/common.go +++ b/traversal/common.go @@ -24,12 +24,12 @@ func (tc *Config) init() { return nil, fmt.Errorf("no link loader configured") } } - if tc.LinkNodeBuilderChooser == nil { - tc.LinkNodeBuilderChooser = func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodeBuilder, error) { + if tc.LinkTargetNodeStyleChooser == nil { + tc.LinkTargetNodeStyleChooser = func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodeStyle, error) { if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { - return tlnkNd.LinkTargetNodeBuilder(), nil + return tlnkNd.LinkTargetNodeStyle(), nil } - return nil, fmt.Errorf("no LinkNodeBuilderChooser configured") + return nil, fmt.Errorf("no LinkTargetNodeStyleChooser configured") } } if tc.LinkStorer == nil { diff --git a/traversal/fns.go b/traversal/fns.go index 98979024..5c5ea844 100644 --- a/traversal/fns.go +++ b/traversal/fns.go @@ -38,19 +38,19 @@ type Progress struct { } type Config struct { - Ctx context.Context // Context carried through a traversal. Optional; use it if you need cancellation. - LinkLoader ipld.Loader // Loader used for automatic link traversal. - LinkNodeBuilderChooser NodeBuilderChooser // Chooser for Node implementations to produce during automatic link traversal. - LinkStorer ipld.Storer // Storer used if any mutation features (e.g. traversal.Transform) are used. + Ctx context.Context // Context carried through a traversal. Optional; use it if you need cancellation. + LinkLoader ipld.Loader // Loader used for automatic link traversal. + LinkTargetNodeStyleChooser LinkTargetNodeStyleChooser // Chooser for Node implementations to produce during automatic link traversal. + LinkStorer ipld.Storer // Storer used if any mutation features (e.g. traversal.Transform) are used. } -// NodeBuilderChooser is a function that returns a NodeBuilder based on -// the information in a Link its LinkContext. +// LinkTargetNodeStyleChooser is a function that returns a NodeStyle based on +// the information in a Link and/or its LinkContext. // -// A NodeBuilderChooser can be used in a traversal.Config to be clear about +// A LinkTargetNodeStyleChooser can be used in a traversal.Config to be clear about // what kind of Node implementation to use when loading a Link. -// In a simple example, it could constantly return an `ipldfree.NodeBuilder`. +// In a simple example, it could constantly return a `basicnode.Style__Any{}`. // In a more complex example, a program using `bind` over native Go types // could decide what kind of native type is expected, and return a // `bind.NodeBuilder` for that specific concrete native type. -type NodeBuilderChooser func(ipld.Link, ipld.LinkContext) (ipld.NodeBuilder, error) +type LinkTargetNodeStyleChooser func(ipld.Link, ipld.LinkContext) (ipld.NodeStyle, error) diff --git a/traversal/focus.go b/traversal/focus.go index 49c82d97..6bdb7cfa 100644 --- a/traversal/focus.go +++ b/traversal/focus.go @@ -68,12 +68,13 @@ func (prog Progress) Focus(n ipld.Node, p ipld.Path, fn VisitFn) error { ParentNode: prev, } // Pick what in-memory format we will build. - nb, err := prog.Cfg.LinkNodeBuilderChooser(lnk, lnkCtx) + ns, err := prog.Cfg.LinkTargetNodeStyleChooser(lnk, lnkCtx) if err != nil { return fmt.Errorf("error traversing node at %q: could not load link %q: %s", p.Truncate(i+1), lnk, err) } + nb := ns.NewBuilder() // Load link! - next, err := lnk.Load( + err = lnk.Load( prog.Cfg.Ctx, lnkCtx, nb, @@ -84,7 +85,7 @@ func (prog Progress) Focus(n ipld.Node, p ipld.Path, fn VisitFn) error { } prog.LastBlock.Path = p.Truncate(i + 1) prog.LastBlock.Link = lnk - prev, n = n, next + prev, n = n, nb.Build() } } prog.Path = prog.Path.Join(p) diff --git a/traversal/focus_test.go b/traversal/focus_test.go index 4960c8cb..d62a04c0 100644 --- a/traversal/focus_test.go +++ b/traversal/focus_test.go @@ -13,10 +13,11 @@ import ( cid "github.com/ipfs/go-cid" ipld "github.com/ipld/go-ipld-prime" - _ "github.com/ipld/go-ipld-prime/encoding/dagjson" + + _ "github.com/ipld/go-ipld-prime/codec/dagjson" "github.com/ipld/go-ipld-prime/fluent" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/ipld/go-ipld-prime/traversal" ) @@ -24,29 +25,28 @@ import ( // We assume all the builders and serialization must Just Work here. var storage = make(map[ipld.Link][]byte) -var fnb = fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building var ( - leafAlpha, leafAlphaLnk = encode(fnb.CreateString("alpha")) - leafBeta, leafBetaLnk = encode(fnb.CreateString("beta")) - middleMapNode, middleMapNodeLnk = encode(fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("foo"), vnb.CreateBool(true)) - mb.Insert(knb.CreateString("bar"), vnb.CreateBool(false)) - mb.Insert(knb.CreateString("nested"), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("alink"), vnb.CreateLink(leafAlphaLnk)) - mb.Insert(knb.CreateString("nonlink"), vnb.CreateString("zoo")) - })) + leafAlpha, leafAlphaLnk = encode(basicnode.NewString("alpha")) + leafBeta, leafBetaLnk = encode(basicnode.NewString("beta")) + middleMapNode, middleMapNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("foo").AssignBool(true) + na.AssembleDirectly("bar").AssignBool(false) + na.AssembleDirectly("nested").CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("alink").AssignLink(leafAlphaLnk) + na.AssembleDirectly("nonlink").AssignString("zoo") + }) })) - middleListNode, middleListNodeLnk = encode(fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateLink(leafAlphaLnk)) - lb.Append(vnb.CreateLink(leafAlphaLnk)) - lb.Append(vnb.CreateLink(leafBetaLnk)) - lb.Append(vnb.CreateLink(leafAlphaLnk)) + middleListNode, middleListNodeLnk = encode(fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListNodeAssembler) { + na.AssembleValue().AssignLink(leafAlphaLnk) + na.AssembleValue().AssignLink(leafAlphaLnk) + na.AssembleValue().AssignLink(leafBetaLnk) + na.AssembleValue().AssignLink(leafAlphaLnk) })) - rootNode, rootNodeLnk = encode(fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("plain"), vnb.CreateString("olde string")) - mb.Insert(knb.CreateString("linkedString"), vnb.CreateLink(leafAlphaLnk)) - mb.Insert(knb.CreateString("linkedMap"), vnb.CreateLink(middleMapNodeLnk)) - mb.Insert(knb.CreateString("linkedList"), vnb.CreateLink(middleListNodeLnk)) + rootNode, rootNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("plain").AssignString("olde string") + na.AssembleDirectly("linkedString").AssignLink(leafAlphaLnk) + na.AssembleDirectly("linkedMap").AssignLink(middleMapNodeLnk) + na.AssembleDirectly("linkedList").AssignLink(middleListNodeLnk) })) ) @@ -98,8 +98,8 @@ func init() { // covers Focus used on one already-loaded Node; no link-loading exercised. func TestFocusSingleTree(t *testing.T) { t.Run("empty path on scalar node returns start node", func(t *testing.T) { - err := traversal.Focus(fnb.CreateString("x"), ipld.Path{}, func(prog traversal.Progress, n ipld.Node) error { - Wish(t, n, ShouldEqual, fnb.CreateString("x")) + err := traversal.Focus(basicnode.NewString("x"), ipld.Path{}, func(prog traversal.Progress, n ipld.Node) error { + Wish(t, n, ShouldEqual, basicnode.NewString("x")) Wish(t, prog.Path.String(), ShouldEqual, ipld.Path{}.String()) return nil }) @@ -107,7 +107,7 @@ func TestFocusSingleTree(t *testing.T) { }) t.Run("one step path on map node works", func(t *testing.T) { err := traversal.Focus(middleMapNode, ipld.ParsePath("foo"), func(prog traversal.Progress, n ipld.Node) error { - Wish(t, n, ShouldEqual, fnb.CreateBool(true)) + Wish(t, n, ShouldEqual, basicnode.NewBool(true)) Wish(t, prog.Path, ShouldEqual, ipld.ParsePath("foo")) return nil }) @@ -115,7 +115,7 @@ func TestFocusSingleTree(t *testing.T) { }) t.Run("two step path on map node works", func(t *testing.T) { err := traversal.Focus(middleMapNode, ipld.ParsePath("nested/nonlink"), func(prog traversal.Progress, n ipld.Node) error { - Wish(t, n, ShouldEqual, fnb.CreateString("zoo")) + Wish(t, n, ShouldEqual, basicnode.NewString("zoo")) Wish(t, prog.Path, ShouldEqual, ipld.ParsePath("nested/nonlink")) return nil }) @@ -130,14 +130,14 @@ func TestFocusWithLinkLoading(t *testing.T) { t.Errorf("should not be reached; no way to load this path") return nil }) - Wish(t, err.Error(), ShouldEqual, `error traversing node at "nested/alink": could not load link "`+leafAlphaLnk.String()+`": no LinkNodeBuilderChooser configured`) + Wish(t, err.Error(), ShouldEqual, `error traversing node at "nested/alink": could not load link "`+leafAlphaLnk.String()+`": no LinkTargetNodeStyleChooser configured`) }) t.Run("mid-path link should fail", func(t *testing.T) { err := traversal.Focus(rootNode, ipld.ParsePath("linkedMap/nested/nonlink"), func(prog traversal.Progress, n ipld.Node) error { t.Errorf("should not be reached; no way to load this path") return nil }) - Wish(t, err.Error(), ShouldEqual, `error traversing node at "linkedMap": could not load link "`+middleMapNodeLnk.String()+`": no LinkNodeBuilderChooser configured`) + Wish(t, err.Error(), ShouldEqual, `error traversing node at "linkedMap": could not load link "`+middleMapNodeLnk.String()+`": no LinkTargetNodeStyleChooser configured`) }) }) t.Run("link traversal with loader should work", func(t *testing.T) { @@ -146,12 +146,12 @@ func TestFocusWithLinkLoading(t *testing.T) { LinkLoader: func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) { return bytes.NewBuffer(storage[lnk]), nil }, - LinkNodeBuilderChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodeBuilder, error) { - return ipldfree.NodeBuilder(), nil + LinkTargetNodeStyleChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodeStyle, error) { + return basicnode.Style__Any{}, nil }, }, }.Focus(rootNode, ipld.ParsePath("linkedMap/nested/nonlink"), func(prog traversal.Progress, n ipld.Node) error { - Wish(t, n, ShouldEqual, fnb.CreateString("zoo")) + Wish(t, n, ShouldEqual, basicnode.NewString("zoo")) Wish(t, prog.Path, ShouldEqual, ipld.ParsePath("linkedMap/nested/nonlink")) Wish(t, prog.LastBlock.Link, ShouldEqual, middleMapNodeLnk) Wish(t, prog.LastBlock.Path, ShouldEqual, ipld.ParsePath("linkedMap")) diff --git a/traversal/selector/builder/builder.go b/traversal/selector/builder/builder.go index 1d4b245f..7ff34415 100644 --- a/traversal/selector/builder/builder.go +++ b/traversal/selector/builder/builder.go @@ -42,11 +42,10 @@ type ExploreFieldsSpecBuildingClosure func(ExploreFieldsSpecBuilder) // selectors in ExploreFields type ExploreFieldsSpecBuilder interface { Insert(k string, v SelectorSpec) - Delete(k string) } type selectorSpecBuilder struct { - fnb fluent.NodeBuilder + ns ipld.NodeStyle } type selectorSpec struct { @@ -61,113 +60,108 @@ func (ss selectorSpec) Selector() (selector.Selector, error) { return selector.ParseSelector(ss.n) } -// NewSelectorSpecBuilder creates a SelectorSpecBuilder from an underlying ipld NodeBuilder -func NewSelectorSpecBuilder(nb ipld.NodeBuilder) SelectorSpecBuilder { - fnb := fluent.WrapNodeBuilder(nb) - return &selectorSpecBuilder{fnb} +// NewSelectorSpecBuilder creates a SelectorSpecBuilder which will store +// data in the format determined by the given ipld.NodeStyle. +func NewSelectorSpecBuilder(ns ipld.NodeStyle) SelectorSpecBuilder { + return &selectorSpecBuilder{ns} } func (ssb *selectorSpecBuilder) ExploreRecursiveEdge() SelectorSpec { return selectorSpec{ - ssb.fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreRecursiveEdge), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapNodeAssembler) {}) }), } } func (ssb *selectorSpecBuilder) ExploreRecursive(limit selector.RecursionLimit, sequence SelectorSpec) SelectorSpec { return selectorSpec{ - ssb.fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreRecursive), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Limit), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { switch limit.Mode() { case selector.RecursionLimit_Depth: - mb.Insert(knb.CreateString(selector.SelectorKey_LimitDepth), vnb.CreateInt(limit.Depth())) + na.AssembleDirectly(selector.SelectorKey_LimitDepth).AssignInt(limit.Depth()) case selector.RecursionLimit_None: - mb.Insert(knb.CreateString(selector.SelectorKey_LimitNone), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) + na.AssembleDirectly(selector.SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapNodeAssembler) {}) default: panic("Unsupported recursion limit type") } - })) - mb.Insert(knb.CreateString(selector.SelectorKey_Sequence), sequence.Node()) - })) + }) + na.AssembleDirectly(selector.SelectorKey_Sequence).AssignNode(sequence.Node()) + }) }), } } func (ssb *selectorSpecBuilder) ExploreAll(next SelectorSpec) SelectorSpec { return selectorSpec{ - ssb.fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreAll), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Next), next.Node()) - })) + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Next).AssignNode(next.Node()) + }) }), } } func (ssb *selectorSpecBuilder) ExploreIndex(index int, next SelectorSpec) SelectorSpec { return selectorSpec{ - ssb.fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreIndex), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Index), vnb.CreateInt(index)) - mb.Insert(knb.CreateString(selector.SelectorKey_Next), next.Node()) - })) + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Index).AssignInt(index) + na.AssembleDirectly(selector.SelectorKey_Next).AssignNode(next.Node()) + }) }), } } func (ssb *selectorSpecBuilder) ExploreRange(start int, end int, next SelectorSpec) SelectorSpec { return selectorSpec{ - ssb.fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreRange), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Start), vnb.CreateInt(start)) - mb.Insert(knb.CreateString(selector.SelectorKey_End), vnb.CreateInt(end)) - mb.Insert(knb.CreateString(selector.SelectorKey_Next), next.Node()) - })) + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRange).CreateMap(3, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Start).AssignInt(start) + na.AssembleDirectly(selector.SelectorKey_End).AssignInt(end) + na.AssembleDirectly(selector.SelectorKey_Next).AssignNode(next.Node()) + }) }), } } func (ssb *selectorSpecBuilder) ExploreUnion(members ...SelectorSpec) SelectorSpec { return selectorSpec{ - ssb.fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreUnion), vnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreUnion).CreateList(len(members), func(na fluent.ListNodeAssembler) { for _, member := range members { - lb.Append(member.Node()) + na.AssembleValue().AssignNode(member.Node()) } - })) + }) }), } } func (ssb *selectorSpecBuilder) ExploreFields(specBuilder ExploreFieldsSpecBuildingClosure) SelectorSpec { return selectorSpec{ - ssb.fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreFields), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Fields), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - specBuilder(exploreFieldsSpecBuilder{mb, knb}) - })) - })) + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreFields).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Fields).CreateMap(-1, func(na fluent.MapNodeAssembler) { + specBuilder(exploreFieldsSpecBuilder{na}) + }) + }) }), } } func (ssb *selectorSpecBuilder) Matcher() SelectorSpec { return selectorSpec{ - ssb.fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) }), } } type exploreFieldsSpecBuilder struct { - mb fluent.MapBuilder - knb fluent.NodeBuilder + na fluent.MapNodeAssembler } func (efsb exploreFieldsSpecBuilder) Insert(field string, s SelectorSpec) { - efsb.mb.Insert(efsb.knb.CreateString(field), s.Node()) -} - -func (efsb exploreFieldsSpecBuilder) Delete(field string) { - efsb.mb.Delete(efsb.knb.CreateString(field)) + efsb.na.AssembleDirectly(field).AssignNode(s.Node()) } diff --git a/traversal/selector/builder/builder_test.go b/traversal/selector/builder/builder_test.go index e3f1bd99..cf543a10 100644 --- a/traversal/selector/builder/builder_test.go +++ b/traversal/selector/builder/builder_test.go @@ -4,128 +4,127 @@ import ( "testing" "github.com/ipld/go-ipld-prime/fluent" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" + basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/ipld/go-ipld-prime/traversal/selector" . "github.com/warpfork/go-wish" ) func TestBuildingSelectors(t *testing.T) { - nb := ipldfree.NodeBuilder() - fnb := fluent.WrapNodeBuilder(nb) - ssb := NewSelectorSpecBuilder(nb) + ns := basicnode.Style__Any{} + ssb := NewSelectorSpecBuilder(ns) t.Run("Matcher builds matcher nodes", func(t *testing.T) { sn := ssb.Matcher().Node() - esn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) }) Wish(t, sn, ShouldEqual, esn) }) t.Run("ExploreRecursiveEdge builds ExploreRecursiveEdge nodes", func(t *testing.T) { sn := ssb.ExploreRecursiveEdge().Node() - esn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreRecursiveEdge), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapNodeAssembler) {}) }) Wish(t, sn, ShouldEqual, esn) }) t.Run("ExploreAll builds ExploreAll nodes", func(t *testing.T) { sn := ssb.ExploreAll(ssb.Matcher()).Node() - esn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreAll), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - })) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + }) }) Wish(t, sn, ShouldEqual, esn) }) t.Run("ExploreIndex builds ExploreIndex nodes", func(t *testing.T) { sn := ssb.ExploreIndex(2, ssb.Matcher()).Node() - esn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreIndex), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Index), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(selector.SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - })) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Index).AssignInt(2) + na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + }) }) Wish(t, sn, ShouldEqual, esn) }) t.Run("ExploreRange builds ExploreRange nodes", func(t *testing.T) { sn := ssb.ExploreRange(2, 3, ssb.Matcher()).Node() - esn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreRange), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Start), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(selector.SelectorKey_End), vnb.CreateInt(3)) - mb.Insert(knb.CreateString(selector.SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - })) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRange).CreateMap(3, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Start).AssignInt(2) + na.AssembleDirectly(selector.SelectorKey_End).AssignInt(3) + na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + }) }) Wish(t, sn, ShouldEqual, esn) }) t.Run("ExploreRecursive builds ExploreRecursive nodes", func(t *testing.T) { sn := ssb.ExploreRecursive(selector.RecursionLimitDepth(2), ssb.ExploreAll(ssb.ExploreRecursiveEdge())).Node() - esn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreRecursive), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Limit), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_LimitDepth), vnb.CreateInt(2)) - })) - mb.Insert(knb.CreateString(selector.SelectorKey_Sequence), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreAll), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreRecursiveEdge), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - })) - })) - })) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_LimitDepth).AssignInt(2) + }) + na.AssembleDirectly(selector.SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + }) + }) + }) }) Wish(t, sn, ShouldEqual, esn) sn = ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge())).Node() - esn = fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreRecursive), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Limit), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_LimitNone), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - mb.Insert(knb.CreateString(selector.SelectorKey_Sequence), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreAll), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreRecursiveEdge), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - })) - })) - })) + esn = fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + na.AssembleDirectly(selector.SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + }) + }) + }) }) Wish(t, sn, ShouldEqual, esn) }) t.Run("ExploreUnion builds ExploreUnion nodes", func(t *testing.T) { sn := ssb.ExploreUnion(ssb.Matcher(), ssb.ExploreIndex(2, ssb.Matcher())).Node() - esn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreUnion), vnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - lb.Append(vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreIndex), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Index), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(selector.SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - })) - })) - })) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreUnion).CreateList(2, func(na fluent.ListNodeAssembler) { + na.AssembleValue().CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + na.AssembleValue().CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Index).AssignInt(2) + na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + }) + }) + }) }) Wish(t, sn, ShouldEqual, esn) }) t.Run("ExploreFields builds ExploreFields nodes", func(t *testing.T) { sn := ssb.ExploreFields(func(efsb ExploreFieldsSpecBuilder) { efsb.Insert("applesauce", ssb.Matcher()) }).Node() - esn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_ExploreFields), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Fields), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("applesauce"), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(selector.SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - })) - })) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreFields).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Fields).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("applesauce").CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + }) + }) }) Wish(t, sn, ShouldEqual, esn) }) diff --git a/traversal/selector/exploreAll_test.go b/traversal/selector/exploreAll_test.go index 698428d6..9d85c1b2 100644 --- a/traversal/selector/exploreAll_test.go +++ b/traversal/selector/exploreAll_test.go @@ -4,36 +4,36 @@ import ( "fmt" "testing" - "github.com/ipld/go-ipld-prime/fluent" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" . "github.com/warpfork/go-wish" + + "github.com/ipld/go-ipld-prime/fluent" + basicnode "github.com/ipld/go-ipld-prime/node/basic" ) func TestParseExploreAll(t *testing.T) { - fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building t.Run("parsing non map node should error", func(t *testing.T) { - sn := fnb.CreateInt(0) + sn := basicnode.NewInt(0) _, err := ParseContext{}.ParseExploreAll(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without next field should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapNodeAssembler) {}) _, err := ParseContext{}.ParseExploreAll(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreAll selector")) }) t.Run("parsing map node without next field with invalid selector node should return child's error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateInt(0)) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Next).AssignInt(0) }) _, err := ParseContext{}.ParseExploreAll(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) s, err := ParseContext{}.ParseExploreAll(sn) Wish(t, err, ShouldEqual, nil) diff --git a/traversal/selector/exploreFields_test.go b/traversal/selector/exploreFields_test.go index da900521..4b026cfd 100644 --- a/traversal/selector/exploreFields_test.go +++ b/traversal/selector/exploreFields_test.go @@ -4,47 +4,47 @@ import ( "fmt" "testing" + . "github.com/warpfork/go-wish" + ipld "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/fluent" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" - . "github.com/warpfork/go-wish" + basicnode "github.com/ipld/go-ipld-prime/node/basic" ) func TestParseExploreFields(t *testing.T) { - fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building t.Run("parsing non map node should error", func(t *testing.T) { - sn := fnb.CreateInt(0) + sn := basicnode.NewInt(0) _, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without fields value should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapNodeAssembler) {}) _, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: fields in ExploreFields selector must be present")) }) t.Run("parsing map node with fields value that is not a map should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Fields), vnb.CreateString("cheese")) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Fields).AssignString("cheese") }) _, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: fields in ExploreFields selector must be a map")) }) t.Run("parsing map node with selector node in fields that is invalid should return child's error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Fields), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("applesauce"), vnb.CreateInt(0)) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Fields).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("applesauce").AssignInt(0) + }) }) _, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with fields value that is map of only valid selector node should parse", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Fields), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("applesauce"), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Fields).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("applesauce").CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + }) }) s, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, nil) diff --git a/traversal/selector/exploreIndex_test.go b/traversal/selector/exploreIndex_test.go index 929925a7..454139cc 100644 --- a/traversal/selector/exploreIndex_test.go +++ b/traversal/selector/exploreIndex_test.go @@ -4,59 +4,59 @@ import ( "fmt" "testing" + . "github.com/warpfork/go-wish" + ipld "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/fluent" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" - . "github.com/warpfork/go-wish" + basicnode "github.com/ipld/go-ipld-prime/node/basic" ) func TestParseExploreIndex(t *testing.T) { - fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building t.Run("parsing non map node should error", func(t *testing.T) { - sn := fnb.CreateInt(0) + sn := basicnode.NewInt(0) _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without next field should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Index), vnb.CreateInt(2)) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Index).AssignInt(2) }) _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreIndex selector")) }) t.Run("parsing map node without index field should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: index field must be present in ExploreIndex selector")) }) t.Run("parsing map node with index field that is not an int should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Index), vnb.CreateString("cheese")) - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Index).AssignString("cheese") + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: index field must be a number in ExploreIndex selector")) }) t.Run("parsing map node with next field with invalid selector node should return child's error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Index), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateInt(0)) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Index).AssignInt(2) + na.AssembleDirectly(SelectorKey_Next).AssignInt(0) }) _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Index), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Index).AssignInt(2) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) s, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, nil) @@ -65,31 +65,27 @@ func TestParseExploreIndex(t *testing.T) { } func TestExploreIndexExplore(t *testing.T) { - fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building s := ExploreIndex{Matcher{}, [1]ipld.PathSegment{ipld.PathSegmentOfInt(3)}} t.Run("exploring should return nil unless node is a list", func(t *testing.T) { - n := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {}) + n := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapNodeAssembler) {}) returnedSelector := s.Explore(n, ipld.PathSegmentOfInt(3)) Wish(t, returnedSelector, ShouldEqual, nil) }) + n := fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListNodeAssembler) { + na.AssembleValue().AssignInt(0) + na.AssembleValue().AssignInt(1) + na.AssembleValue().AssignInt(2) + na.AssembleValue().AssignInt(3) + }) t.Run("exploring should return nil when given a path segment with a different index", func(t *testing.T) { - n := fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.AppendAll([]ipld.Node{fnb.CreateInt(0), fnb.CreateInt(1), fnb.CreateInt(2), fnb.CreateInt(3)}) - }) returnedSelector := s.Explore(n, ipld.PathSegmentOfInt(2)) Wish(t, returnedSelector, ShouldEqual, nil) }) t.Run("exploring should return nil when given a path segment that isn't an index", func(t *testing.T) { - n := fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.AppendAll([]ipld.Node{fnb.CreateInt(0), fnb.CreateInt(1), fnb.CreateInt(2), fnb.CreateInt(3)}) - }) returnedSelector := s.Explore(n, ipld.PathSegmentOfString("cheese")) Wish(t, returnedSelector, ShouldEqual, nil) }) t.Run("exploring should return the next selector when given a path segment with the right index", func(t *testing.T) { - n := fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.AppendAll([]ipld.Node{fnb.CreateInt(0), fnb.CreateInt(1), fnb.CreateInt(2), fnb.CreateInt(3)}) - }) returnedSelector := s.Explore(n, ipld.PathSegmentOfInt(3)) Wish(t, returnedSelector, ShouldEqual, Matcher{}) }) diff --git a/traversal/selector/exploreRange_test.go b/traversal/selector/exploreRange_test.go index c50e0a0a..6e9fec9d 100644 --- a/traversal/selector/exploreRange_test.go +++ b/traversal/selector/exploreRange_test.go @@ -4,97 +4,97 @@ import ( "fmt" "testing" + . "github.com/warpfork/go-wish" + ipld "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/fluent" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" - . "github.com/warpfork/go-wish" + basicnode "github.com/ipld/go-ipld-prime/node/basic" ) func TestParseExploreRange(t *testing.T) { - fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building t.Run("parsing non map node should error", func(t *testing.T) { - sn := fnb.CreateInt(0) + sn := basicnode.NewInt(0) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without next field should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Start), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(SelectorKey_End), vnb.CreateInt(3)) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Start).AssignInt(2) + na.AssembleDirectly(SelectorKey_End).AssignInt(3) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreRange selector")) }) t.Run("parsing map node without start field should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_End), vnb.CreateInt(3)) - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_End).AssignInt(3) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: start field must be present in ExploreRange selector")) }) t.Run("parsing map node with start field that is not an int should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Start), vnb.CreateString("cheese")) - mb.Insert(knb.CreateString(SelectorKey_End), vnb.CreateInt(3)) - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Start).AssignString("cheese") + na.AssembleDirectly(SelectorKey_End).AssignInt(3) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: start field must be a number in ExploreRange selector")) }) t.Run("parsing map node without end field should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Start), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Start).AssignInt(2) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: end field must be present in ExploreRange selector")) }) t.Run("parsing map node with end field that is not an int should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Start), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(SelectorKey_End), vnb.CreateString("cheese")) - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Start).AssignInt(2) + na.AssembleDirectly(SelectorKey_End).AssignString("cheese") + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: end field must be a number in ExploreRange selector")) }) t.Run("parsing map node where end is not greater than start should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Start), vnb.CreateInt(3)) - mb.Insert(knb.CreateString(SelectorKey_End), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Start).AssignInt(3) + na.AssembleDirectly(SelectorKey_End).AssignInt(2) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: end field must be greater than start field in ExploreRange selector")) }) t.Run("parsing map node with next field with invalid selector node should return child's error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Start), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(SelectorKey_End), vnb.CreateInt(3)) - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateInt(0)) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Start).AssignInt(2) + na.AssembleDirectly(SelectorKey_End).AssignInt(3) + na.AssembleDirectly(SelectorKey_Next).AssignInt(0) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Start), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(SelectorKey_End), vnb.CreateInt(3)) - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Start).AssignInt(2) + na.AssembleDirectly(SelectorKey_End).AssignInt(3) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) s, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, nil) @@ -103,31 +103,27 @@ func TestParseExploreRange(t *testing.T) { } func TestExploreRangeExplore(t *testing.T) { - fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building s := ExploreRange{Matcher{}, 3, 4, []ipld.PathSegment{ipld.PathSegmentOfInt(3)}} t.Run("exploring should return nil unless node is a list", func(t *testing.T) { - n := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {}) + n := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapNodeAssembler) {}) returnedSelector := s.Explore(n, ipld.PathSegmentOfInt(3)) Wish(t, returnedSelector, ShouldEqual, nil) }) + n := fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListNodeAssembler) { + na.AssembleValue().AssignInt(0) + na.AssembleValue().AssignInt(1) + na.AssembleValue().AssignInt(2) + na.AssembleValue().AssignInt(3) + }) t.Run("exploring should return nil when given a path segment out of range", func(t *testing.T) { - n := fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.AppendAll([]ipld.Node{fnb.CreateInt(0), fnb.CreateInt(1), fnb.CreateInt(2), fnb.CreateInt(3)}) - }) returnedSelector := s.Explore(n, ipld.PathSegmentOfInt(2)) Wish(t, returnedSelector, ShouldEqual, nil) }) t.Run("exploring should return nil when given a path segment that isn't an index", func(t *testing.T) { - n := fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.AppendAll([]ipld.Node{fnb.CreateInt(0), fnb.CreateInt(1), fnb.CreateInt(2), fnb.CreateInt(3)}) - }) returnedSelector := s.Explore(n, ipld.PathSegmentOfString("cheese")) Wish(t, returnedSelector, ShouldEqual, nil) }) t.Run("exploring should return the next selector when given a path segment with index in range", func(t *testing.T) { - n := fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.AppendAll([]ipld.Node{fnb.CreateInt(0), fnb.CreateInt(1), fnb.CreateInt(2), fnb.CreateInt(3)}) - }) returnedSelector := s.Explore(n, ipld.PathSegmentOfInt(3)) Wish(t, returnedSelector, ShouldEqual, Matcher{}) }) diff --git a/traversal/selector/exploreRecursive_test.go b/traversal/selector/exploreRecursive_test.go index 13ee95a7..5fe1e0bd 100644 --- a/traversal/selector/exploreRecursive_test.go +++ b/traversal/selector/exploreRecursive_test.go @@ -5,128 +5,128 @@ import ( "fmt" "testing" + . "github.com/warpfork/go-wish" + ipld "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/encoding/dagjson" + "github.com/ipld/go-ipld-prime/codec/dagjson" "github.com/ipld/go-ipld-prime/fluent" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" - . "github.com/warpfork/go-wish" + basicnode "github.com/ipld/go-ipld-prime/node/basic" ) func TestParseExploreRecursive(t *testing.T) { - fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building t.Run("parsing non map node should error", func(t *testing.T) { - sn := fnb.CreateInt(0) + sn := basicnode.NewInt(0) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without sequence field should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Limit), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_LimitDepth), vnb.CreateInt(2)) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) + }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: sequence field must be present in ExploreRecursive selector")) }) t.Run("parsing map node without limit field should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Sequence), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: limit field must be present in ExploreRecursive selector")) }) t.Run("parsing map node with limit field that is not a map should fail", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Limit), vnb.CreateString("cheese")) - mb.Insert(knb.CreateString(SelectorKey_Sequence), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Limit).AssignString("cheese") + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: limit in ExploreRecursive is a keyed union and thus must be a map")) }) t.Run("parsing map node with limit field that is not a single entry map should fail", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Limit), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_LimitDepth), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(SelectorKey_LimitNone), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - mb.Insert(knb.CreateString(SelectorKey_Sequence), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) + na.AssembleDirectly(SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: limit in ExploreRecursive is a keyed union and thus must be a single-entry map")) }) t.Run("parsing map node with limit field that does not have a known key should fail", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Limit), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("applesauce"), vnb.CreateInt(2)) - })) - mb.Insert(knb.CreateString(SelectorKey_Sequence), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("applesauce").AssignInt(2) + }) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: \"applesauce\" is not a known member of the limit union in ExploreRecursive")) }) t.Run("parsing map node with limit field of type depth that is not an int should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Limit), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_LimitDepth), vnb.CreateString("cheese")) - })) - mb.Insert(knb.CreateString(SelectorKey_Sequence), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_LimitDepth).AssignString("cheese") + }) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: limit field of type depth must be a number in ExploreRecursive selector")) }) t.Run("parsing map node with sequence field with invalid selector node should return child's error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Limit), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_LimitDepth), vnb.CreateInt(2)) - })) - mb.Insert(knb.CreateString(SelectorKey_Sequence), vnb.CreateInt(0)) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) + }) + na.AssembleDirectly(SelectorKey_Sequence).AssignInt(0) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with sequence field with valid selector w/o ExploreRecursiveEdge should not parse", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Limit), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_LimitDepth), vnb.CreateInt(2)) - })) - mb.Insert(knb.CreateString(SelectorKey_Sequence), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_ExploreAll), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - })) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) + }) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + }) + }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: ExploreRecursive must have at least one ExploreRecursiveEdge")) }) t.Run("parsing map node that is ExploreRecursiveEdge without ExploreRecursive parent should not parse", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapNodeAssembler) {}) _, err := ParseContext{}.ParseExploreRecursiveEdge(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: ExploreRecursiveEdge must be beneath ExploreRecursive")) }) t.Run("parsing map node with sequence field with valid selector node should parse", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Limit), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_LimitDepth), vnb.CreateInt(2)) - })) - mb.Insert(knb.CreateString(SelectorKey_Sequence), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_ExploreAll), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_ExploreRecursiveEdge), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - })) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) + }) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + }) + }) }) s, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, nil) @@ -134,17 +134,17 @@ func TestParseExploreRecursive(t *testing.T) { }) t.Run("parsing map node with sequence field with valid selector node and limit type none should parse", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Limit), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_LimitNone), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - mb.Insert(knb.CreateString(SelectorKey_Sequence), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_ExploreAll), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_ExploreRecursiveEdge), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - })) - })) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + }) + }) }) s, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, nil) @@ -198,8 +198,10 @@ func TestExploreRecursiveExplore(t *testing.T) { ] } ` - rn, err := dagjson.Decoder(ipldfree.NodeBuilder(), bytes.NewBufferString(nodeString)) + nb := basicnode.Style__Any{}.NewBuilder() + err := dagjson.Decoder(nb, bytes.NewBufferString(nodeString)) Wish(t, err, ShouldEqual, nil) + rn := nb.Build() rs = rs.Explore(rn, ipld.PathSegmentOfString("Parents")) rn, err = rn.LookupString("Parents") Wish(t, rs, ShouldEqual, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_Depth, maxDepth}}) @@ -247,8 +249,10 @@ func TestExploreRecursiveExplore(t *testing.T) { ] } ` - rn, err := dagjson.Decoder(ipldfree.NodeBuilder(), bytes.NewBufferString(nodeString)) + nb := basicnode.Style__Any{}.NewBuilder() + err := dagjson.Decoder(nb, bytes.NewBufferString(nodeString)) Wish(t, err, ShouldEqual, nil) + rn := nb.Build() rs = rs.Explore(rn, ipld.PathSegmentOfString("Parents")) rn, err = rn.LookupString("Parents") Wish(t, rs, ShouldEqual, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_None, 0}}) @@ -288,8 +292,10 @@ func TestExploreRecursiveExplore(t *testing.T) { } } ` - rn, err := dagjson.Decoder(ipldfree.NodeBuilder(), bytes.NewBufferString(nodeString)) + nb := basicnode.Style__Any{}.NewBuilder() + err := dagjson.Decoder(nb, bytes.NewBufferString(nodeString)) Wish(t, err, ShouldEqual, nil) + rn := nb.Build() rs = rs.Explore(rn, ipld.PathSegmentOfString("Parents")) rn, err = rn.LookupString("Parents") Wish(t, rs, ShouldEqual, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_Depth, maxDepth}}) @@ -331,8 +337,10 @@ func TestExploreRecursiveExplore(t *testing.T) { } } ` - n, err := dagjson.Decoder(ipldfree.NodeBuilder(), bytes.NewBufferString(nodeString)) + nb := basicnode.Style__Any{}.NewBuilder() + err := dagjson.Decoder(nb, bytes.NewBufferString(nodeString)) Wish(t, err, ShouldEqual, nil) + n := nb.Build() // traverse down Parent nodes rn := n @@ -414,8 +422,10 @@ func TestExploreRecursiveExplore(t *testing.T) { ] } ` - rn, err := dagjson.Decoder(ipldfree.NodeBuilder(), bytes.NewBufferString(nodeString)) + nb := basicnode.Style__Any{}.NewBuilder() + err := dagjson.Decoder(nb, bytes.NewBufferString(nodeString)) Wish(t, err, ShouldEqual, nil) + rn := nb.Build() rs = rs.Explore(rn, ipld.PathSegmentOfString("Parents")) rn, err = rn.LookupString("Parents") Wish(t, rs, ShouldEqual, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_Depth, maxDepth}}) diff --git a/traversal/selector/exploreUnion_test.go b/traversal/selector/exploreUnion_test.go index 56262629..1cc5d97e 100644 --- a/traversal/selector/exploreUnion_test.go +++ b/traversal/selector/exploreUnion_test.go @@ -4,43 +4,43 @@ import ( "fmt" "testing" + . "github.com/warpfork/go-wish" + ipld "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/fluent" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" - . "github.com/warpfork/go-wish" + basicnode "github.com/ipld/go-ipld-prime/node/basic" ) func TestParseExploreUnion(t *testing.T) { - fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building t.Run("parsing non list node should error", func(t *testing.T) { - sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapNodeAssembler) {}) _, err := ParseContext{}.ParseExploreUnion(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: explore union selector must be a list")) }) t.Run("parsing list node where one node is invalid should return child's error", func(t *testing.T) { - sn := fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - lb.Append(vnb.CreateInt(2)) + sn := fluent.MustBuildList(basicnode.Style__List{}, 2, func(na fluent.ListNodeAssembler) { + na.AssembleValue().CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + na.AssembleValue().AssignInt(2) }) _, err := ParseContext{}.ParseExploreUnion(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { - sn := fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - lb.Append(vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_ExploreIndex), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Index), vnb.CreateInt(2)) - mb.Insert(knb.CreateString(SelectorKey_Next), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString(SelectorKey_Matcher), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) - })) - })) - })) + sn := fluent.MustBuildList(basicnode.Style__List{}, 2, func(na fluent.ListNodeAssembler) { + na.AssembleValue().CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + na.AssembleValue().CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Index).AssignInt(2) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + }) + }) + }) }) s, err := ParseContext{}.ParseExploreUnion(sn) Wish(t, err, ShouldEqual, nil) @@ -49,9 +49,11 @@ func TestParseExploreUnion(t *testing.T) { } func TestExploreUnionExplore(t *testing.T) { - fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building - n := fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.AppendAll([]ipld.Node{fnb.CreateInt(0), fnb.CreateInt(1), fnb.CreateInt(2), fnb.CreateInt(3)}) + n := fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListNodeAssembler) { + na.AssembleValue().AssignInt(0) + na.AssembleValue().AssignInt(1) + na.AssembleValue().AssignInt(2) + na.AssembleValue().AssignInt(3) }) t.Run("exploring should return nil if all member selectors return nil when explored", func(t *testing.T) { s := ExploreUnion{[]Selector{Matcher{}, ExploreIndex{Matcher{}, [1]ipld.PathSegment{ipld.PathSegmentOfInt(2)}}}} @@ -98,8 +100,7 @@ func TestExploreUnionInterests(t *testing.T) { } func TestExploreUnionDecide(t *testing.T) { - fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building - n := fnb.CreateInt(2) + n := basicnode.NewInt(2) t.Run("if any member selector returns true, decide should be true", func(t *testing.T) { s := ExploreUnion{[]Selector{ ExploreAll{Matcher{}}, diff --git a/traversal/walk.go b/traversal/walk.go index 26956a45..ed843c51 100644 --- a/traversal/walk.go +++ b/traversal/walk.go @@ -128,12 +128,13 @@ func (prog Progress) loadLink(v ipld.Node, parent ipld.Node) (ipld.Node, error) ParentNode: parent, } // Pick what in-memory format we will build. - nb, err := prog.Cfg.LinkNodeBuilderChooser(lnk, lnkCtx) + ns, err := prog.Cfg.LinkTargetNodeStyleChooser(lnk, lnkCtx) if err != nil { return nil, fmt.Errorf("error traversing node at %q: could not load link %q: %s", prog.Path, lnk, err) } + nb := ns.NewBuilder() // Load link! - v, err = lnk.Load( + err = lnk.Load( prog.Cfg.Ctx, lnkCtx, nb, @@ -142,7 +143,7 @@ func (prog Progress) loadLink(v ipld.Node, parent ipld.Node) (ipld.Node, error) if err != nil { return nil, fmt.Errorf("error traversing node at %q: could not load link %q: %s", prog.Path, lnk, err) } - return v, nil + return nb.Build(), nil } func (prog Progress) WalkTransforming(n ipld.Node, s selector.Selector, fn TransformFn) (ipld.Node, error) { diff --git a/traversal/walk_test.go b/traversal/walk_test.go index 09082f5c..87429ca4 100644 --- a/traversal/walk_test.go +++ b/traversal/walk_test.go @@ -8,9 +8,9 @@ import ( . "github.com/warpfork/go-wish" ipld "github.com/ipld/go-ipld-prime" - _ "github.com/ipld/go-ipld-prime/encoding/dagjson" + _ "github.com/ipld/go-ipld-prime/codec/dagjson" "github.com/ipld/go-ipld-prime/fluent" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" + basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/ipld/go-ipld-prime/traversal" "github.com/ipld/go-ipld-prime/traversal/selector" "github.com/ipld/go-ipld-prime/traversal/selector/builder" @@ -18,27 +18,27 @@ import ( /* Remember, we've got the following fixtures in scope: var ( - leafAlpha, leafAlphaLnk = encode(fnb.CreateString("alpha")) - leafBeta, leafBetaLnk = encode(fnb.CreateString("beta")) - middleMapNode, middleMapNodeLnk = encode(fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("foo"), vnb.CreateBool(true)) - mb.Insert(knb.CreateString("bar"), vnb.CreateBool(false)) - mb.Insert(knb.CreateString("nested"), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("alink"), vnb.CreateLink(leafAlphaLnk)) - mb.Insert(knb.CreateString("nonlink"), vnb.CreateString("zoo")) - })) + leafAlpha, leafAlphaLnk = encode(basicnode.NewString("alpha")) + leafBeta, leafBetaLnk = encode(basicnode.NewString("beta")) + middleMapNode, middleMapNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("foo").AssignBool(true) + na.AssembleDirectly("bar").AssignBool(false) + na.AssembleDirectly("nested").CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("alink").AssignLink(leafAlphaLnk) + na.AssembleDirectly("nonlink").AssignString("zoo") + }) })) - middleListNode, middleListNodeLnk = encode(fnb.CreateList(func(lb fluent.ListBuilder, vnb fluent.NodeBuilder) { - lb.Append(vnb.CreateLink(leafAlphaLnk)) - lb.Append(vnb.CreateLink(leafAlphaLnk)) - lb.Append(vnb.CreateLink(leafBetaLnk)) - lb.Append(vnb.CreateLink(leafAlphaLnk)) + middleListNode, middleListNodeLnk = encode(fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListNodeAssembler) { + na.AssembleValue().AssignLink(leafAlphaLnk) + na.AssembleValue().AssignLink(leafAlphaLnk) + na.AssembleValue().AssignLink(leafBetaLnk) + na.AssembleValue().AssignLink(leafAlphaLnk) })) - rootNode, rootNodeLnk = encode(fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("plain"), vnb.CreateString("olde string")) - mb.Insert(knb.CreateString("linkedString"), vnb.CreateLink(leafAlphaLnk)) - mb.Insert(knb.CreateString("linkedMap"), vnb.CreateLink(middleMapNodeLnk)) - mb.Insert(knb.CreateString("linkedList"), vnb.CreateLink(middleListNodeLnk)) + rootNode, rootNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("plain").AssignString("olde string") + na.AssembleDirectly("linkedString").AssignLink(leafAlphaLnk) + na.AssembleDirectly("linkedMap").AssignLink(middleMapNodeLnk) + na.AssembleDirectly("linkedList").AssignLink(middleListNodeLnk) })) ) */ @@ -47,10 +47,10 @@ var ( // all cases here use one already-loaded Node; no link-loading exercised. func TestWalkMatching(t *testing.T) { - ssb := builder.NewSelectorSpecBuilder(ipldfree.NodeBuilder()) + ssb := builder.NewSelectorSpecBuilder(basicnode.Style__Any{}) t.Run("traverse selecting true should visit the root", func(t *testing.T) { - err := traversal.WalkMatching(fnb.CreateString("x"), selector.Matcher{}, func(prog traversal.Progress, n ipld.Node) error { - Wish(t, n, ShouldEqual, fnb.CreateString("x")) + err := traversal.WalkMatching(basicnode.NewString("x"), selector.Matcher{}, func(prog traversal.Progress, n ipld.Node) error { + Wish(t, n, ShouldEqual, basicnode.NewString("x")) Wish(t, prog.Path.String(), ShouldEqual, ipld.Path{}.String()) return nil }) @@ -75,10 +75,10 @@ func TestWalkMatching(t *testing.T) { err = traversal.WalkMatching(middleMapNode, s, func(prog traversal.Progress, n ipld.Node) error { switch order { case 0: - Wish(t, n, ShouldEqual, fnb.CreateBool(true)) + Wish(t, n, ShouldEqual, basicnode.NewBool(true)) Wish(t, prog.Path.String(), ShouldEqual, "foo") case 1: - Wish(t, n, ShouldEqual, fnb.CreateBool(false)) + Wish(t, n, ShouldEqual, basicnode.NewBool(false)) Wish(t, prog.Path.String(), ShouldEqual, "bar") } order++ @@ -100,10 +100,10 @@ func TestWalkMatching(t *testing.T) { err = traversal.WalkMatching(middleMapNode, s, func(prog traversal.Progress, n ipld.Node) error { switch order { case 0: - Wish(t, n, ShouldEqual, fnb.CreateBool(true)) + Wish(t, n, ShouldEqual, basicnode.NewBool(true)) Wish(t, prog.Path.String(), ShouldEqual, "foo") case 1: - Wish(t, n, ShouldEqual, fnb.CreateString("zoo")) + Wish(t, n, ShouldEqual, basicnode.NewString("zoo")) Wish(t, prog.Path.String(), ShouldEqual, "nested/nonlink") } order++ @@ -124,8 +124,8 @@ func TestWalkMatching(t *testing.T) { LinkLoader: func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) { return bytes.NewBuffer(storage[lnk]), nil }, - LinkNodeBuilderChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodeBuilder, error) { - return ipldfree.NodeBuilder(), nil + LinkTargetNodeStyleChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodeStyle, error) { + return basicnode.Style__Any{}, nil }, }, }.WalkMatching(middleMapNode, s, func(prog traversal.Progress, n ipld.Node) error { @@ -134,25 +134,25 @@ func TestWalkMatching(t *testing.T) { Wish(t, n, ShouldEqual, middleMapNode) Wish(t, prog.Path.String(), ShouldEqual, "") case 1: - Wish(t, n, ShouldEqual, fnb.CreateBool(true)) + Wish(t, n, ShouldEqual, basicnode.NewBool(true)) Wish(t, prog.Path.String(), ShouldEqual, "foo") case 2: - Wish(t, n, ShouldEqual, fnb.CreateBool(false)) + Wish(t, n, ShouldEqual, basicnode.NewBool(false)) Wish(t, prog.Path.String(), ShouldEqual, "bar") case 3: - Wish(t, n, ShouldEqual, fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { - mb.Insert(knb.CreateString("alink"), vnb.CreateLink(leafAlphaLnk)) - mb.Insert(knb.CreateString("nonlink"), vnb.CreateString("zoo")) + Wish(t, n, ShouldEqual, fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("alink").AssignLink(leafAlphaLnk) + na.AssembleDirectly("nonlink").AssignString("zoo") })) Wish(t, prog.Path.String(), ShouldEqual, "nested") case 4: - Wish(t, n, ShouldEqual, fnb.CreateString("alpha")) + Wish(t, n, ShouldEqual, basicnode.NewString("alpha")) Wish(t, prog.Path.String(), ShouldEqual, "nested/alink") Wish(t, prog.LastBlock.Path.String(), ShouldEqual, "nested/alink") Wish(t, prog.LastBlock.Link.String(), ShouldEqual, leafAlphaLnk.String()) case 5: - Wish(t, n, ShouldEqual, fnb.CreateString("zoo")) + Wish(t, n, ShouldEqual, basicnode.NewString("zoo")) Wish(t, prog.Path.String(), ShouldEqual, "nested/nonlink") } order++ @@ -170,24 +170,24 @@ func TestWalkMatching(t *testing.T) { LinkLoader: func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) { return bytes.NewBuffer(storage[lnk]), nil }, - LinkNodeBuilderChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodeBuilder, error) { - return ipldfree.NodeBuilder(), nil + LinkTargetNodeStyleChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodeStyle, error) { + return basicnode.Style__Any{}, nil }, }, }.WalkMatching(middleListNode, s, func(prog traversal.Progress, n ipld.Node) error { switch order { case 0: - Wish(t, n, ShouldEqual, fnb.CreateString("alpha")) + Wish(t, n, ShouldEqual, basicnode.NewString("alpha")) Wish(t, prog.Path.String(), ShouldEqual, "0") Wish(t, prog.LastBlock.Path.String(), ShouldEqual, "0") Wish(t, prog.LastBlock.Link.String(), ShouldEqual, leafAlphaLnk.String()) case 1: - Wish(t, n, ShouldEqual, fnb.CreateString("alpha")) + Wish(t, n, ShouldEqual, basicnode.NewString("alpha")) Wish(t, prog.Path.String(), ShouldEqual, "1") Wish(t, prog.LastBlock.Path.String(), ShouldEqual, "1") Wish(t, prog.LastBlock.Link.String(), ShouldEqual, leafAlphaLnk.String()) case 2: - Wish(t, n, ShouldEqual, fnb.CreateString("beta")) + Wish(t, n, ShouldEqual, basicnode.NewString("beta")) Wish(t, prog.Path.String(), ShouldEqual, "2") Wish(t, prog.LastBlock.Path.String(), ShouldEqual, "2") Wish(t, prog.LastBlock.Link.String(), ShouldEqual, leafBetaLnk.String()) @@ -215,44 +215,44 @@ func TestWalkMatching(t *testing.T) { LinkLoader: func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) { return bytes.NewBuffer(storage[lnk]), nil }, - LinkNodeBuilderChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodeBuilder, error) { - return ipldfree.NodeBuilder(), nil + LinkTargetNodeStyleChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodeStyle, error) { + return basicnode.Style__Any{}, nil }, }, }.WalkMatching(rootNode, s, func(prog traversal.Progress, n ipld.Node) error { switch order { case 0: - Wish(t, n, ShouldEqual, fnb.CreateString("alpha")) + Wish(t, n, ShouldEqual, basicnode.NewString("alpha")) Wish(t, prog.Path.String(), ShouldEqual, "linkedList/0") Wish(t, prog.LastBlock.Path.String(), ShouldEqual, "linkedList/0") Wish(t, prog.LastBlock.Link.String(), ShouldEqual, leafAlphaLnk.String()) case 1: - Wish(t, n, ShouldEqual, fnb.CreateString("alpha")) + Wish(t, n, ShouldEqual, basicnode.NewString("alpha")) Wish(t, prog.Path.String(), ShouldEqual, "linkedList/1") Wish(t, prog.LastBlock.Path.String(), ShouldEqual, "linkedList/1") Wish(t, prog.LastBlock.Link.String(), ShouldEqual, leafAlphaLnk.String()) case 2: - Wish(t, n, ShouldEqual, fnb.CreateString("beta")) + Wish(t, n, ShouldEqual, basicnode.NewString("beta")) Wish(t, prog.Path.String(), ShouldEqual, "linkedList/2") Wish(t, prog.LastBlock.Path.String(), ShouldEqual, "linkedList/2") Wish(t, prog.LastBlock.Link.String(), ShouldEqual, leafBetaLnk.String()) case 3: - Wish(t, n, ShouldEqual, fnb.CreateString("alpha")) + Wish(t, n, ShouldEqual, basicnode.NewString("alpha")) Wish(t, prog.Path.String(), ShouldEqual, "linkedList/3") Wish(t, prog.LastBlock.Path.String(), ShouldEqual, "linkedList/3") Wish(t, prog.LastBlock.Link.String(), ShouldEqual, leafAlphaLnk.String()) case 4: - Wish(t, n, ShouldEqual, fnb.CreateBool(true)) + Wish(t, n, ShouldEqual, basicnode.NewBool(true)) Wish(t, prog.Path.String(), ShouldEqual, "linkedMap/foo") Wish(t, prog.LastBlock.Path.String(), ShouldEqual, "linkedMap") Wish(t, prog.LastBlock.Link.String(), ShouldEqual, middleMapNodeLnk.String()) case 5: - Wish(t, n, ShouldEqual, fnb.CreateString("zoo")) + Wish(t, n, ShouldEqual, basicnode.NewString("zoo")) Wish(t, prog.Path.String(), ShouldEqual, "linkedMap/nested/nonlink") Wish(t, prog.LastBlock.Path.String(), ShouldEqual, "linkedMap") Wish(t, prog.LastBlock.Link.String(), ShouldEqual, middleMapNodeLnk.String()) case 6: - Wish(t, n, ShouldEqual, fnb.CreateString("alpha")) + Wish(t, n, ShouldEqual, basicnode.NewString("alpha")) Wish(t, prog.Path.String(), ShouldEqual, "linkedMap/nested/alink") Wish(t, prog.LastBlock.Path.String(), ShouldEqual, "linkedMap/nested/alink") Wish(t, prog.LastBlock.Link.String(), ShouldEqual, leafAlphaLnk.String()) diff --git a/_rsrch/nodesolution/unit.go b/unit.go similarity index 100% rename from _rsrch/nodesolution/unit.go rename to unit.go From 1da3519feebec9343dd15adcf19c31954fe8a343 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Wed, 11 Mar 2020 11:28:46 +0100 Subject: [PATCH 02/11] Review comment addressed. Votes for no in https://github.com/ipld/go-ipld-prime/pull/49#discussion_r389603606 and https://github.com/ipld/go-ipld-prime/pull/49#discussion_r389611401 . --- errors.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/errors.go b/errors.go index b63e14f8..2f7bcbc3 100644 --- a/errors.go +++ b/errors.go @@ -10,8 +10,6 @@ import ( // // For example, calling AsString on a map will return ErrWrongKind. // Calling Lookup on an int will similarly return ErrWrongKind. -// -// REVIEW: would it improve clarity to call this 'ErrWrongKindForNodeStyle'? type ErrWrongKind struct { // TypeName may optionally indicate the named type of a node the function // was called on (if the node was typed!), or, may be the empty string. From 38cefe7a9ea002c08315ce6a89c8b9be7818648c Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Wed, 11 Mar 2020 11:29:36 +0100 Subject: [PATCH 03/11] Move package docs to own file for discoverablity. Per suggestion in https://github.com/ipld/go-ipld-prime/pull/49#discussion_r389606762 . --- fluent/doc.go | 15 +++++++++++++++ fluent/fluentBuilder.go | 14 -------------- 2 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 fluent/doc.go diff --git a/fluent/doc.go b/fluent/doc.go new file mode 100644 index 00000000..86c9768c --- /dev/null +++ b/fluent/doc.go @@ -0,0 +1,15 @@ +/* + The fluent package offers helper utilities for using NodeAssembler + more tersely by providing an interface that handles all errors for you, + and allows use of closures for any recursive assembly + so that creating trees of data results in indentation for legibility. + + Note that the fluent package creates wrapper objects in order to provide + the API conveniences that it does, and this comes at some cost to performance. + If you're optimizing for performance, using the fluent interfaces may be inadvisable. + However, as with any performance questions, benchmark before making decisions; + its entirely possible that your performance bottlenecks will be elsewhere + and there's no reason to deny yourself syntactic sugar if the costs don't + detectably affect the bottom line. +*/ +package fluent diff --git a/fluent/fluentBuilder.go b/fluent/fluentBuilder.go index 97c560bc..fff9ade9 100644 --- a/fluent/fluentBuilder.go +++ b/fluent/fluentBuilder.go @@ -1,17 +1,3 @@ -/* - The fluent package offers helper utilities for using NodeAssembler - more tersely by providing an interface that handles all errors for you, - and allows use of closures for any recursive assembly - so that creating trees of data results in indentation for legibility. - - Note that the fluent package creates wrapper objects in order to provide - the API conveniences that it does, and this comes at some cost to performance. - If you're optimizing for performance, using the fluent interfaces may be inadvisable. - However, as with any performance questions, benchmark before making decisions; - its entirely possible that your performance bottlenecks will be elsewhere - and there's no reason to deny yourself syntactic sugar if the costs don't - detectably affect the bottom line. -*/ package fluent import ( From c3818a007a1d7bbb250ae7acb60e22c1d45590d3 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Wed, 11 Mar 2020 11:30:25 +0100 Subject: [PATCH 04/11] Add missing error type docs. Called out in https://github.com/ipld/go-ipld-prime/pull/49#discussion_r389604191 . --- errors.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/errors.go b/errors.go index 2f7bcbc3..24511dfd 100644 --- a/errors.go +++ b/errors.go @@ -56,6 +56,12 @@ func (e ErrNotExists) Error() string { return fmt.Sprintf("key not found: %q", e.Segment) } +// ErrRepeatedMapKey is an error indicating that a key was inserted +// into a map that already contains that key. +// +// This error may be returned by any methods that add data to a map -- +// any of the methods on a NodeAssembler that was yielded by MapAssembler.AssignKey(), +// or from the MapAssembler.AssignDirectly() method. type ErrRepeatedMapKey struct { Key Node } From 4d73ecca12b07eb6329a536cf2f6c897f8d34452 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Wed, 11 Mar 2020 11:31:58 +0100 Subject: [PATCH 05/11] Fix hilarious typo. With continuing thanks to @rvagg's good eyes: https://github.com/ipld/go-ipld-prime/pull/49#discussion_r389603173 --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index 70386703..f304558a 100644 --- a/doc.go +++ b/doc.go @@ -38,7 +38,7 @@ // - schema -- interfaces for working with IPLD Schemas and Nodes // which use Schema types and constraints // -// Note that since packages in this interface are the core of the library, +// Note that since interfaces in this package are the core of the library, // choices made here maximize correctness and performance -- these choices // are *not* always the choices that would maximize ergonomics. // (Ergonomics can come on top; performance generally can't.) From 31cbbf82548c56e9a6f70d8709db606350dec4ab Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Wed, 11 Mar 2020 11:42:04 +0100 Subject: [PATCH 06/11] Shorten: s/{Map,List}NodeAssembler/{Map,List}Assembler/g Saying "Node" again in the middle is just purely redundant and doesn't improve clarity in any meaningful way. Begone with it. --- HACKME.md | 12 +-- HACKME_builderBehaviors.md | 6 +- _rsrch/nodeassembler/nodeAssembler.go | 14 ++-- codec/dagcbor/roundtrip_test.go | 10 +-- codec/dagjson/roundtrip_test.go | 10 +-- fluent/fluentBuilder.go | 24 +++--- fluent/fluentBuilder_test.go | 12 +-- node/basic/any.go | 4 +- node/basic/bool.go | 4 +- node/basic/bytes.go | 4 +- node/basic/float.go | 4 +- node/basic/int.go | 4 +- node/basic/link.go | 4 +- node/basic/list.go | 18 ++--- node/basic/map.go | 26 +++--- node/basic/string.go | 4 +- node/gendemo/int.go | 4 +- node/gendemo/map_K2_T2.go | 8 +- node/gendemo/map_K_T.go | 22 +++--- node/gendemo/string.go | 4 +- node/mixins/boolMixin.go | 4 +- node/mixins/bytesMixin.go | 4 +- node/mixins/floatMixin.go | 4 +- node/mixins/intMixin.go | 4 +- node/mixins/linkMixin.go | 4 +- node/mixins/listMixin.go | 2 +- node/mixins/mapMixin.go | 2 +- node/mixins/stringMixin.go | 4 +- node/mixins/tmplMixin.txt | 4 +- node/tests/mapSpecs.go | 6 +- nodeBuilder.go | 14 ++-- traversal/focus_test.go | 8 +- traversal/selector/builder/builder.go | 40 +++++----- traversal/selector/builder/builder_test.go | 88 ++++++++++----------- traversal/selector/exploreAll_test.go | 10 +-- traversal/selector/exploreFields_test.go | 16 ++-- traversal/selector/exploreIndex_test.go | 26 +++--- traversal/selector/exploreRange_test.go | 44 +++++------ traversal/selector/exploreRecursive_test.go | 86 ++++++++++---------- traversal/selector/exploreUnion_test.go | 24 +++--- traversal/walk_test.go | 10 +-- 41 files changed, 301 insertions(+), 301 deletions(-) diff --git a/HACKME.md b/HACKME.md index 29c65f14..c952fa26 100644 --- a/HACKME.md +++ b/HACKME.md @@ -108,12 +108,12 @@ appropriately converted.) Any of these paths counts as "recursive assembly process": -- `MapNodeAssembler.KeyStyle()` -- `MapNodeAssembler.ValueStyle(string)` -- `MapNodeAssembler.AssembleKey().Style()` -- `MapNodeAssembler.AssembleValue().Style()` -- `ListNodeAssembler.ValueStyle()` -- `ListNodeAssembler.AssembleValue().Style()` +- `MapAssembler.KeyStyle()` +- `MapAssembler.ValueStyle(string)` +- `MapAssembler.AssembleKey().Style()` +- `MapAssembler.AssembleValue().Style()` +- `ListAssembler.ValueStyle()` +- `ListAssembler.AssembleValue().Style()` ### NodeStyle for carrying ADL configuration diff --git a/HACKME_builderBehaviors.md b/HACKME_builderBehaviors.md index 1a9006ab..c4c977b7 100644 --- a/HACKME_builderBehaviors.md +++ b/HACKME_builderBehaviors.md @@ -7,7 +7,7 @@ high level rules of builders and assemblers - Errors should be returned as soon as possible. - That means an error like "repeated key in map" should be returned by the key assembler! - Either 'NodeAssembler.AssignString' should return this (for simple keys on untyped maps, or on structs, etc)... - - ... or 'MapNodeAssembler.Finish' (in the case of complex keys in a typed map). + - ... or 'MapAssembler.Finish' (in the case of complex keys in a typed map). - Logical integrity checks must be done locally -- recursive types rely on their contained types to report errors, and the recursive type wraps the assemblers of their contained type in order to check and correctly invalidate/rollback the recursive construction. @@ -40,8 +40,8 @@ but the caller tries to continue anyway. - assigning a key with 'AssembleKey': - in case of success: clearly 'AssembleValue' should be ready to use next. - in case of failure from repeated key: - - the error must be returned immediately from either the 'NodeAssembler.AssignString' or the 'MapNodeAssembler.Finish' method. - - 'AssignString' for any simple keys; 'MapNodeAssembler.Finish' may be relevant in the case of complex keys in a typed map. + - the error must be returned immediately from either the 'NodeAssembler.AssignString' or the 'MapAssembler.Finish' method. + - 'AssignString' for any simple keys; 'MapAssembler.Finish' may be relevant in the case of complex keys in a typed map. - implementers take note: this implies the `NodeAssembler` returned by `AssembleKey` has some way to refer to the map assembler that spawned it. - no side effect should be visible if 'AssembleKey' is called again next. - (typically this doesn't require extra code for the string case, but it may require some active zeroing in the complex key case.) diff --git a/_rsrch/nodeassembler/nodeAssembler.go b/_rsrch/nodeassembler/nodeAssembler.go index 03a34109..b9b55aa2 100644 --- a/_rsrch/nodeassembler/nodeAssembler.go +++ b/_rsrch/nodeassembler/nodeAssembler.go @@ -32,8 +32,8 @@ Maybe. Let's see. */ type NodeAssembler interface { - BeginMap() MapNodeAssembler - BeginList() ListNodeAssembler + BeginMap() MapAssembler + BeginList() ListAssembler AssignNull() AssignBool(bool) AssignInt(int) @@ -45,7 +45,7 @@ type NodeAssembler interface { CheckError() error // where are these stored? when each child is done, could check this. and have it stored in each child. means each allocs (nonzero struct)... but isn't that true anyway? yeah. well, except now you're apparently getting that for scalars, which is bad. } -type MapNodeAssembler interface { +type MapAssembler interface { AssembleKey() MapKeyAssembler Insert(string, Node) @@ -61,7 +61,7 @@ type MapValueAssembler interface { NodeAssembler } -type ListNodeAssembler interface { +type ListAssembler interface { AssembleValue() NodeAssembler Append(Node) @@ -80,9 +80,9 @@ type Node interface { func demo() { var nb NodeBuilder - func(mb MapNodeAssembler) { + func(mb MapAssembler) { mb.AssembleKey().AssignString("key") - func(mb MapNodeAssembler) { + func(mb MapAssembler) { mb.AssembleKey().AssignString("nested") mb.AssembleValue().AssignBool(true) mb.Done() @@ -129,6 +129,6 @@ Notes: - a natively-typed assign method that works by value is the only way to minimize all the costs at once. - we do still need it though: `Assign2(Node,Node)` is the only way to copy an existing typed map key generically (without the temp-copy lamented above for the `AssembleKey.Assign` chain). - the `Append` and `Assign` method names might be too terse -- we didn't leave space for the natively typed methods, which we generally want to be the shortest ones. -- 'Assign' being a symbol on both NodeAssemler and MapNodeAssembler with different arity might be unfortunate -- would make it impossible to supply both with one concrete type. +- 'Assign' being a symbol on both NodeAssemler and MapAssembler with different arity might be unfortunate -- would make it impossible to supply both with one concrete type. */ diff --git a/codec/dagcbor/roundtrip_test.go b/codec/dagcbor/roundtrip_test.go index 94fed572..e35ac9ce 100644 --- a/codec/dagcbor/roundtrip_test.go +++ b/codec/dagcbor/roundtrip_test.go @@ -10,18 +10,18 @@ import ( basicnode "github.com/ipld/go-ipld-prime/node/basic" ) -var n = fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapNodeAssembler) { +var n = fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapAssembler) { na.AssembleDirectly("plain").AssignString("olde string") - na.AssembleDirectly("map").CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("map").CreateMap(2, func(na fluent.MapAssembler) { na.AssembleDirectly("one").AssignInt(1) na.AssembleDirectly("two").AssignInt(2) }) - na.AssembleDirectly("list").CreateList(2, func(na fluent.ListNodeAssembler) { + na.AssembleDirectly("list").CreateList(2, func(na fluent.ListAssembler) { na.AssembleValue().AssignString("three") na.AssembleValue().AssignString("four") }) - na.AssembleDirectly("nested").CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly("deeper").CreateList(1, func(na fluent.ListNodeAssembler) { + na.AssembleDirectly("nested").CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly("deeper").CreateList(1, func(na fluent.ListAssembler) { na.AssembleValue().AssignString("things") }) }) diff --git a/codec/dagjson/roundtrip_test.go b/codec/dagjson/roundtrip_test.go index b555d797..d7d9b842 100644 --- a/codec/dagjson/roundtrip_test.go +++ b/codec/dagjson/roundtrip_test.go @@ -10,18 +10,18 @@ import ( basicnode "github.com/ipld/go-ipld-prime/node/basic" ) -var n = fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapNodeAssembler) { +var n = fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapAssembler) { na.AssembleDirectly("plain").AssignString("olde string") - na.AssembleDirectly("map").CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("map").CreateMap(2, func(na fluent.MapAssembler) { na.AssembleDirectly("one").AssignInt(1) na.AssembleDirectly("two").AssignInt(2) }) - na.AssembleDirectly("list").CreateList(2, func(na fluent.ListNodeAssembler) { + na.AssembleDirectly("list").CreateList(2, func(na fluent.ListAssembler) { na.AssembleValue().AssignString("three") na.AssembleValue().AssignString("four") }) - na.AssembleDirectly("nested").CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly("deeper").CreateList(1, func(na fluent.ListNodeAssembler) { + na.AssembleDirectly("nested").CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly("deeper").CreateList(1, func(na fluent.ListAssembler) { na.AssembleValue().AssignString("things") }) }) diff --git a/fluent/fluentBuilder.go b/fluent/fluentBuilder.go index fff9ade9..f99d2b12 100644 --- a/fluent/fluentBuilder.go +++ b/fluent/fluentBuilder.go @@ -18,10 +18,10 @@ func MustBuild(ns ipld.NodeStyle, fn func(NodeAssembler)) ipld.Node { fn(WrapAssembler(nb)) return nb.Build() } -func MustBuildMap(ns ipld.NodeStyle, sizeHint int, fn func(MapNodeAssembler)) ipld.Node { +func MustBuildMap(ns ipld.NodeStyle, sizeHint int, fn func(MapAssembler)) ipld.Node { return MustBuild(ns, func(fna NodeAssembler) { fna.CreateMap(sizeHint, fn) }) } -func MustBuildList(ns ipld.NodeStyle, sizeHint int, fn func(ListNodeAssembler)) ipld.Node { +func MustBuildList(ns ipld.NodeStyle, sizeHint int, fn func(ListAssembler)) ipld.Node { return MustBuild(ns, func(fna NodeAssembler) { fna.CreateList(sizeHint, fn) }) } @@ -35,8 +35,8 @@ func WrapAssembler(na ipld.NodeAssembler) NodeAssembler { // and all recursive operations take a function as a parameter, // within which you will receive another {Map,List,}NodeAssembler. type NodeAssembler interface { - CreateMap(sizeHint int, fn func(MapNodeAssembler)) - CreateList(sizeHint int, fn func(ListNodeAssembler)) + CreateMap(sizeHint int, fn func(MapAssembler)) + CreateList(sizeHint int, fn func(ListAssembler)) AssignNull() AssignBool(bool) AssignInt(int) @@ -49,12 +49,12 @@ type NodeAssembler interface { Style() ipld.NodeStyle } -// MapNodeAssembler is the same as the interface in the core package, except: +// MapAssembler is the same as the interface in the core package, except: // instead of returning errors, any error will cause panic // (and you can collect these with `fluent.Recover`); // and all recursive operations take a function as a parameter, // within which you will receive another {Map,List,}NodeAssembler. -type MapNodeAssembler interface { +type MapAssembler interface { AssembleKey() NodeAssembler AssembleValue() NodeAssembler @@ -64,12 +64,12 @@ type MapNodeAssembler interface { ValueStyle(k string) ipld.NodeStyle } -// ListNodeAssembler is the same as the interface in the core package, except: +// ListAssembler is the same as the interface in the core package, except: // instead of returning errors, any error will cause panic // (and you can collect these with `fluent.Recover`); // and all recursive operations take a function as a parameter, // within which you will receive another {Map,List,}NodeAssembler. -type ListNodeAssembler interface { +type ListAssembler interface { AssembleValue() NodeAssembler ValueStyle() ipld.NodeStyle @@ -79,7 +79,7 @@ type nodeAssembler struct { na ipld.NodeAssembler } -func (fna *nodeAssembler) CreateMap(sizeHint int, fn func(MapNodeAssembler)) { +func (fna *nodeAssembler) CreateMap(sizeHint int, fn func(MapAssembler)) { if ma, err := fna.na.BeginMap(sizeHint); err != nil { panic(Error{err}) } else { @@ -89,7 +89,7 @@ func (fna *nodeAssembler) CreateMap(sizeHint int, fn func(MapNodeAssembler)) { } } } -func (fna *nodeAssembler) CreateList(sizeHint int, fn func(ListNodeAssembler)) { +func (fna *nodeAssembler) CreateList(sizeHint int, fn func(ListAssembler)) { if la, err := fna.na.BeginList(sizeHint); err != nil { panic(Error{err}) } else { @@ -144,7 +144,7 @@ func (fna *nodeAssembler) Style() ipld.NodeStyle { } type mapNodeAssembler struct { - ma ipld.MapNodeAssembler + ma ipld.MapAssembler } func (fma *mapNodeAssembler) AssembleKey() NodeAssembler { @@ -168,7 +168,7 @@ func (fma *mapNodeAssembler) ValueStyle(k string) ipld.NodeStyle { } type listNodeAssembler struct { - la ipld.ListNodeAssembler + la ipld.ListAssembler } func (fla *listNodeAssembler) AssembleValue() NodeAssembler { diff --git a/fluent/fluentBuilder_test.go b/fluent/fluentBuilder_test.go index a08e4145..d5e617c2 100644 --- a/fluent/fluentBuilder_test.go +++ b/fluent/fluentBuilder_test.go @@ -23,10 +23,10 @@ func TestBuild(t *testing.T) { }) t.Run("map build should work", func(t *testing.T) { n := fluent.MustBuild(basicnode.Style__Map{}, func(fna fluent.NodeAssembler) { - fna.CreateMap(3, func(fma fluent.MapNodeAssembler) { + fna.CreateMap(3, func(fma fluent.MapAssembler) { fma.AssembleDirectly("k1").AssignString("fine") fma.AssembleDirectly("k2").AssignString("super") - fma.AssembleDirectly("k3").CreateMap(3, func(fma fluent.MapNodeAssembler) { + fma.AssembleDirectly("k3").CreateMap(3, func(fma fluent.MapAssembler) { fma.AssembleDirectly("k31").AssignString("thanks") fma.AssembleDirectly("k32").AssignString("for") fma.AssembleDirectly("k33").AssignString("asking") @@ -45,10 +45,10 @@ func TestBuild(t *testing.T) { }) t.Run("list build should work", func(t *testing.T) { n := fluent.MustBuild(basicnode.Style__List{}, func(fna fluent.NodeAssembler) { - fna.CreateList(1, func(fla fluent.ListNodeAssembler) { - fla.AssembleValue().CreateList(1, func(fla fluent.ListNodeAssembler) { - fla.AssembleValue().CreateList(1, func(fla fluent.ListNodeAssembler) { - fla.AssembleValue().CreateList(1, func(fla fluent.ListNodeAssembler) { + fna.CreateList(1, func(fla fluent.ListAssembler) { + fla.AssembleValue().CreateList(1, func(fla fluent.ListAssembler) { + fla.AssembleValue().CreateList(1, func(fla fluent.ListAssembler) { + fla.AssembleValue().CreateList(1, func(fla fluent.ListAssembler) { fla.AssembleValue().AssignInt(2) }) }) diff --git a/node/basic/any.go b/node/basic/any.go index d42f8948..708de6de 100644 --- a/node/basic/any.go +++ b/node/basic/any.go @@ -77,7 +77,7 @@ func (nb *anyBuilder) Reset() { *nb = anyBuilder{} } -func (nb *anyBuilder) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (nb *anyBuilder) BeginMap(sizeHint int) (ipld.MapAssembler, error) { if nb.kind != ipld.ReprKind_Invalid { panic("misuse") } @@ -85,7 +85,7 @@ func (nb *anyBuilder) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { nb.mapBuilder.w = &plainMap{} return nb.mapBuilder.BeginMap(sizeHint) } -func (nb *anyBuilder) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (nb *anyBuilder) BeginList(sizeHint int) (ipld.ListAssembler, error) { if nb.kind != ipld.ReprKind_Invalid { panic("misuse") } diff --git a/node/basic/bool.go b/node/basic/bool.go index ba55355e..bb2489eb 100644 --- a/node/basic/bool.go +++ b/node/basic/bool.go @@ -103,10 +103,10 @@ type plainBool__Assembler struct { w *plainBool } -func (plainBool__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (plainBool__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return mixins.BoolAssembler{"bool"}.BeginMap(0) } -func (plainBool__Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (plainBool__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return mixins.BoolAssembler{"bool"}.BeginList(0) } func (plainBool__Assembler) AssignNull() error { diff --git a/node/basic/bytes.go b/node/basic/bytes.go index e63ba0f4..24452081 100644 --- a/node/basic/bytes.go +++ b/node/basic/bytes.go @@ -103,10 +103,10 @@ type plainBytes__Assembler struct { w *plainBytes } -func (plainBytes__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (plainBytes__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return mixins.BytesAssembler{"bytes"}.BeginMap(0) } -func (plainBytes__Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (plainBytes__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return mixins.BytesAssembler{"bytes"}.BeginList(0) } func (plainBytes__Assembler) AssignNull() error { diff --git a/node/basic/float.go b/node/basic/float.go index 98f90583..f7c824f7 100644 --- a/node/basic/float.go +++ b/node/basic/float.go @@ -103,10 +103,10 @@ type plainFloat__Assembler struct { w *plainFloat } -func (plainFloat__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (plainFloat__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return mixins.FloatAssembler{"float"}.BeginMap(0) } -func (plainFloat__Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (plainFloat__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return mixins.FloatAssembler{"float"}.BeginList(0) } func (plainFloat__Assembler) AssignNull() error { diff --git a/node/basic/int.go b/node/basic/int.go index b00893ae..0d306123 100644 --- a/node/basic/int.go +++ b/node/basic/int.go @@ -103,10 +103,10 @@ type plainInt__Assembler struct { w *plainInt } -func (plainInt__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (plainInt__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return mixins.IntAssembler{"int"}.BeginMap(0) } -func (plainInt__Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (plainInt__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return mixins.IntAssembler{"int"}.BeginList(0) } func (plainInt__Assembler) AssignNull() error { diff --git a/node/basic/link.go b/node/basic/link.go index d607cddd..bd6c47e8 100644 --- a/node/basic/link.go +++ b/node/basic/link.go @@ -104,10 +104,10 @@ type plainLink__Assembler struct { w *plainLink } -func (plainLink__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (plainLink__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return mixins.LinkAssembler{"link"}.BeginMap(0) } -func (plainLink__Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (plainLink__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return mixins.LinkAssembler{"link"}.BeginList(0) } func (plainLink__Assembler) AssignNull() error { diff --git a/node/basic/list.go b/node/basic/list.go index ee2e3101..af616f90 100644 --- a/node/basic/list.go +++ b/node/basic/list.go @@ -147,16 +147,16 @@ const ( laState_finished // 'w' will also be nil, but this is a politer statement ) -func (plainList__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (plainList__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return mixins.ListAssembler{"list"}.BeginMap(0) } -func (na *plainList__Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (na *plainList__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { if sizeHint < 0 { sizeHint = 0 } // Allocate storage space. na.w.x = make([]ipld.Node, 0, sizeHint) - // That's it; return self as the ListNodeAssembler. We already have all the right methods on this structure. + // That's it; return self as the ListAssembler. We already have all the right methods on this structure. return na, nil } func (plainList__Assembler) AssignNull() error { @@ -191,9 +191,9 @@ func (plainList__Assembler) Style() ipld.NodeStyle { return Style__List{} } -// -- ListNodeAssembler --> +// -- ListAssembler --> -// AssembleValue is part of conforming to ListNodeAssembler, which we do on +// AssembleValue is part of conforming to ListAssembler, which we do on // plainList__Assembler so that BeginList can just return a retyped pointer rather than new object. func (la *plainList__Assembler) AssembleValue() ipld.NodeAssembler { // Sanity check, then update, assembler state. @@ -206,7 +206,7 @@ func (la *plainList__Assembler) AssembleValue() ipld.NodeAssembler { return &la.va } -// Finish is part of conforming to ListNodeAssembler, which we do on +// Finish is part of conforming to ListAssembler, which we do on // plainList__Assembler so that BeginList can just return a retyped pointer rather than new object. func (la *plainList__Assembler) Finish() error { // Sanity check, then update, assembler state. @@ -221,16 +221,16 @@ func (plainList__Assembler) ValueStyle() ipld.NodeStyle { return Style__Any{} } -// -- ListNodeAssembler.ValueAssembler --> +// -- ListAssembler.ValueAssembler --> -func (lva *plainList__ValueAssembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (lva *plainList__ValueAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { ma := plainList__ValueAssemblerMap{} ma.ca.w = &plainMap{} ma.p = lva.la _, err := ma.ca.BeginMap(sizeHint) return &ma, err } -func (lva *plainList__ValueAssembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (lva *plainList__ValueAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { la := plainList__ValueAssemblerList{} la.ca.w = &plainList{} la.p = lva.la diff --git a/node/basic/map.go b/node/basic/map.go index dcc3059c..37081d4c 100644 --- a/node/basic/map.go +++ b/node/basic/map.go @@ -162,17 +162,17 @@ const ( maState_finished // 'w' will also be nil, but this is a politer statement ) -func (na *plainMap__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (na *plainMap__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { if sizeHint < 0 { sizeHint = 0 } // Allocate storage space. na.w.t = make([]plainMap__Entry, 0, sizeHint) na.w.m = make(map[string]ipld.Node, sizeHint) - // That's it; return self as the MapNodeAssembler. We already have all the right methods on this structure. + // That's it; return self as the MapAssembler. We already have all the right methods on this structure. return na, nil } -func (plainMap__Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (plainMap__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return mixins.MapAssembler{"map"}.BeginList(0) } func (plainMap__Assembler) AssignNull() error { @@ -207,7 +207,7 @@ func (plainMap__Assembler) Style() ipld.NodeStyle { return Style__Map{} } -// -- MapNodeAssembler --> +// -- MapAssembler --> // AssembleDirectly 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. @@ -228,7 +228,7 @@ func (ma *plainMap__Assembler) AssembleDirectly(k string) (ipld.NodeAssembler, e return &ma.va, nil } -// AssembleKey is part of conforming to MapNodeAssembler, which we do on +// AssembleKey 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) AssembleKey() ipld.NodeAssembler { // Sanity check, then update, assembler state. @@ -243,7 +243,7 @@ func (ma *plainMap__Assembler) AssembleKey() ipld.NodeAssembler { return &ma.ka } -// AssembleValue is part of conforming to MapNodeAssembler, which we do on +// AssembleValue 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) AssembleValue() ipld.NodeAssembler { // Sanity check, then update, assembler state. @@ -256,7 +256,7 @@ func (ma *plainMap__Assembler) AssembleValue() ipld.NodeAssembler { return &ma.va } -// Finish is part of conforming to MapNodeAssembler, which we do on +// Finish 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) Finish() error { // Sanity check, then update, assembler state. @@ -274,12 +274,12 @@ func (plainMap__Assembler) ValueStyle(_ string) ipld.NodeStyle { return Style__Any{} } -// -- MapNodeAssembler.KeyAssembler --> +// -- MapAssembler.KeyAssembler --> -func (plainMap__KeyAssembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (plainMap__KeyAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return mixins.StringAssembler{"string"}.BeginMap(0) } -func (plainMap__KeyAssembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (plainMap__KeyAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return mixins.StringAssembler{"string"}.BeginList(0) } func (plainMap__KeyAssembler) AssignNull() error { @@ -327,16 +327,16 @@ func (plainMap__KeyAssembler) Style() ipld.NodeStyle { return Style__String{} } -// -- MapNodeAssembler.ValueAssembler --> +// -- MapAssembler.ValueAssembler --> -func (mva *plainMap__ValueAssembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (mva *plainMap__ValueAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { ma := plainMap__ValueAssemblerMap{} ma.ca.w = &plainMap{} ma.p = mva.ma _, err := ma.ca.BeginMap(sizeHint) return &ma, err } -func (mva *plainMap__ValueAssembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (mva *plainMap__ValueAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { la := plainMap__ValueAssemblerList{} la.ca.w = &plainList{} la.p = mva.ma diff --git a/node/basic/string.go b/node/basic/string.go index d8850f81..7b05dd5e 100644 --- a/node/basic/string.go +++ b/node/basic/string.go @@ -108,10 +108,10 @@ type plainString__Assembler struct { w *plainString } -func (plainString__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (plainString__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return mixins.StringAssembler{"string"}.BeginMap(0) } -func (plainString__Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (plainString__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return mixins.StringAssembler{"string"}.BeginList(0) } func (plainString__Assembler) AssignNull() error { diff --git a/node/gendemo/int.go b/node/gendemo/int.go index 2dbdfa4d..db6ea4a9 100644 --- a/node/gendemo/int.go +++ b/node/gendemo/int.go @@ -98,10 +98,10 @@ type plainInt__Assembler struct { w *plainInt } -func (plainInt__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (plainInt__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return mixins.IntAssembler{"gendemo.Int"}.BeginMap(0) } -func (plainInt__Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (plainInt__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return mixins.IntAssembler{"gendemo.Int"}.BeginList(0) } func (plainInt__Assembler) AssignNull() error { diff --git a/node/gendemo/map_K2_T2.go b/node/gendemo/map_K2_T2.go index f9e53bda..f4c3a2bb 100644 --- a/node/gendemo/map_K2_T2.go +++ b/node/gendemo/map_K2_T2.go @@ -133,8 +133,8 @@ type _K2__ReprAssembler struct { // 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.MapNodeAssembler, error) { panic("no") } -func (_K2__Assembler) BeginList(_ int) (ipld.ListNodeAssembler, error) { panic("no") } +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") } @@ -313,10 +313,10 @@ type _T2__ReprAssembler struct { w *T2 } -func (ta *_T2__Assembler) BeginMap(_ int) (ipld.MapNodeAssembler, error) { +func (ta *_T2__Assembler) BeginMap(_ int) (ipld.MapAssembler, error) { return ta, nil } -func (_T2__Assembler) BeginList(_ int) (ipld.ListNodeAssembler, error) { panic("no") } +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") } diff --git a/node/gendemo/map_K_T.go b/node/gendemo/map_K_T.go index 0da037e6..7679d962 100644 --- a/node/gendemo/map_K_T.go +++ b/node/gendemo/map_K_T.go @@ -128,8 +128,8 @@ type _K__ReprAssembler struct { w *K } -func (_K__Assembler) BeginMap(_ int) (ipld.MapNodeAssembler, error) { panic("no") } -func (_K__Assembler) BeginList(_ int) (ipld.ListNodeAssembler, error) { panic("no") } +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") } @@ -159,8 +159,8 @@ type _T__ReprAssembler struct { w *T } -func (_T__Assembler) BeginMap(_ int) (ipld.MapNodeAssembler, error) { panic("no") } -func (_T__Assembler) BeginList(_ int) (ipld.ListNodeAssembler, error) { panic("no") } +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 { @@ -342,17 +342,17 @@ func (nb *_Map_K_T__Builder) Reset() { nb.w = &Map_K_T{} } -func (na *_Map_K_T__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +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 MapNodeAssembler. We already have all the right methods on this structure. + // 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.ListNodeAssembler, error) { panic("no") } +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") } @@ -427,8 +427,8 @@ func (ma *_Map_K_T__Assembler) Finish() error { 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.MapNodeAssembler, error) { panic("no") } -func (_Map_K_T__KeyAssembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { panic("no") } +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") } @@ -465,10 +465,10 @@ func (mka *_Map_K_T__KeyAssembler) AssignNode(v ipld.Node) error { } 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.MapNodeAssembler, error) { +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.ListNodeAssembler, 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. diff --git a/node/gendemo/string.go b/node/gendemo/string.go index 8a86a489..9c9a21b7 100644 --- a/node/gendemo/string.go +++ b/node/gendemo/string.go @@ -103,10 +103,10 @@ type plainString__Assembler struct { w *plainString } -func (plainString__Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (plainString__Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return mixins.StringAssembler{"gendemo.String"}.BeginMap(0) } -func (plainString__Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (plainString__Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return mixins.StringAssembler{"gendemo.String"}.BeginList(0) } func (plainString__Assembler) AssignNull() error { diff --git a/node/mixins/boolMixin.go b/node/mixins/boolMixin.go index 4c0ff3e5..cbe6be65 100644 --- a/node/mixins/boolMixin.go +++ b/node/mixins/boolMixin.go @@ -71,10 +71,10 @@ type BoolAssembler struct { TypeName string } -func (x BoolAssembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (x BoolAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_Bool} } -func (x BoolAssembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (x BoolAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Bool} } func (x BoolAssembler) AssignNull() error { diff --git a/node/mixins/bytesMixin.go b/node/mixins/bytesMixin.go index 15dcd041..ba798d11 100644 --- a/node/mixins/bytesMixin.go +++ b/node/mixins/bytesMixin.go @@ -71,10 +71,10 @@ type BytesAssembler struct { TypeName string } -func (x BytesAssembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (x BytesAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_Bytes} } -func (x BytesAssembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (x BytesAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Bytes} } func (x BytesAssembler) AssignNull() error { diff --git a/node/mixins/floatMixin.go b/node/mixins/floatMixin.go index 2c602b87..28986fce 100644 --- a/node/mixins/floatMixin.go +++ b/node/mixins/floatMixin.go @@ -71,10 +71,10 @@ type FloatAssembler struct { TypeName string } -func (x FloatAssembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (x FloatAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_Float} } -func (x FloatAssembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (x FloatAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Float} } func (x FloatAssembler) AssignNull() error { diff --git a/node/mixins/intMixin.go b/node/mixins/intMixin.go index cbc7f18c..f51d4f30 100644 --- a/node/mixins/intMixin.go +++ b/node/mixins/intMixin.go @@ -71,10 +71,10 @@ type IntAssembler struct { TypeName string } -func (x IntAssembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (x IntAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_Int} } -func (x IntAssembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (x IntAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Int} } func (x IntAssembler) AssignNull() error { diff --git a/node/mixins/linkMixin.go b/node/mixins/linkMixin.go index 28448eb0..e4fb7606 100644 --- a/node/mixins/linkMixin.go +++ b/node/mixins/linkMixin.go @@ -71,10 +71,10 @@ type LinkAssembler struct { TypeName string } -func (x LinkAssembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (x LinkAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_Link} } -func (x LinkAssembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (x LinkAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Link} } func (x LinkAssembler) AssignNull() error { diff --git a/node/mixins/listMixin.go b/node/mixins/listMixin.go index f42e9e5d..1286f30b 100644 --- a/node/mixins/listMixin.go +++ b/node/mixins/listMixin.go @@ -62,7 +62,7 @@ type ListAssembler struct { TypeName string } -func (x ListAssembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (x ListAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_List} } func (x ListAssembler) AssignNull() error { diff --git a/node/mixins/mapMixin.go b/node/mixins/mapMixin.go index 0dab58be..a205ce2d 100644 --- a/node/mixins/mapMixin.go +++ b/node/mixins/mapMixin.go @@ -59,7 +59,7 @@ type MapAssembler struct { TypeName string } -func (x MapAssembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (x MapAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_Map} } func (x MapAssembler) AssignNull() error { diff --git a/node/mixins/stringMixin.go b/node/mixins/stringMixin.go index 3cb5f270..2eb68a77 100644 --- a/node/mixins/stringMixin.go +++ b/node/mixins/stringMixin.go @@ -71,10 +71,10 @@ type StringAssembler struct { TypeName string } -func (x StringAssembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (x StringAssembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String} } -func (x StringAssembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (x StringAssembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_String} } func (x StringAssembler) AssignNull() error { diff --git a/node/mixins/tmplMixin.txt b/node/mixins/tmplMixin.txt index 46822e54..cd4f6beb 100644 --- a/node/mixins/tmplMixin.txt +++ b/node/mixins/tmplMixin.txt @@ -78,10 +78,10 @@ type @Kind@Assembler struct { TypeName string } -func (x @Kind@Assembler) BeginMap(sizeHint int) (ipld.MapNodeAssembler, error) { +func (x @Kind@Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_@Kind@} } -func (x @Kind@Assembler) BeginList(sizeHint int) (ipld.ListNodeAssembler, error) { +func (x @Kind@Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) { return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_@Kind@} } func (x @Kind@Assembler) AssignNull() error { diff --git a/node/tests/mapSpecs.go b/node/tests/mapSpecs.go index decc8c98..51fd2ef4 100644 --- a/node/tests/mapSpecs.go +++ b/node/tests/mapSpecs.go @@ -142,7 +142,7 @@ func SpecTestMapStrMapStrInt(t *testing.T, ns ipld.NodeStyle) { ma, err := nb.BeginMap(3) must.NotError(err) must.NotError(ma.AssembleKey().AssignString("whee")) - func(ma ipld.MapNodeAssembler, err error) { + func(ma ipld.MapAssembler, err error) { must.NotError(ma.AssembleKey().AssignString("m1k1")) must.NotError(ma.AssembleValue().AssignInt(1)) must.NotError(ma.AssembleKey().AssignString("m1k2")) @@ -150,7 +150,7 @@ func SpecTestMapStrMapStrInt(t *testing.T, ns ipld.NodeStyle) { must.NotError(ma.Finish()) }(ma.AssembleValue().BeginMap(2)) must.NotError(ma.AssembleKey().AssignString("woot")) - func(ma ipld.MapNodeAssembler, err error) { + func(ma ipld.MapAssembler, err error) { must.NotError(ma.AssembleKey().AssignString("m2k1")) must.NotError(ma.AssembleValue().AssignInt(3)) must.NotError(ma.AssembleKey().AssignString("m2k2")) @@ -158,7 +158,7 @@ func SpecTestMapStrMapStrInt(t *testing.T, ns ipld.NodeStyle) { must.NotError(ma.Finish()) }(ma.AssembleValue().BeginMap(2)) must.NotError(ma.AssembleKey().AssignString("waga")) - func(ma ipld.MapNodeAssembler, err error) { + func(ma ipld.MapAssembler, err error) { must.NotError(ma.AssembleKey().AssignString("m3k1")) must.NotError(ma.AssembleValue().AssignInt(5)) must.NotError(ma.AssembleKey().AssignString("m3k2")) diff --git a/nodeBuilder.go b/nodeBuilder.go index 513b4f4b..a78dbdf4 100644 --- a/nodeBuilder.go +++ b/nodeBuilder.go @@ -16,8 +16,8 @@ package ipld // language without first-class/compile-time support for them (as golang is) // would tend to push complexity and costs to execution time; we'd rather not.) type NodeAssembler interface { - BeginMap(sizeHint int) (MapNodeAssembler, error) - BeginList(sizeHint int) (ListNodeAssembler, error) + BeginMap(sizeHint int) (MapAssembler, error) + BeginList(sizeHint int) (ListAssembler, error) AssignNull() error AssignBool(bool) error AssignInt(int) error @@ -40,9 +40,9 @@ type NodeAssembler interface { Style() NodeStyle } -// MapNodeAssembler assembles a map node! (You guessed it.) +// MapAssembler assembles a map node! (You guessed it.) // -// Methods on MapNodeAssembler must be called in a valid order: +// Methods on MapAssembler must be called in a valid order: // assemble a key, then assemble a value, then loop as long as desired; // when finished, call 'Finish'. // @@ -55,7 +55,7 @@ type NodeAssembler interface { // Note that the NodeAssembler yielded from AssembleKey has additional behavior: // if the node assembled there matches a key already present in the map, // that assembler will emit the error! -type MapNodeAssembler interface { +type MapAssembler interface { AssembleKey() NodeAssembler // must be followed by call to AssembleValue. AssembleValue() NodeAssembler // must be called immediately after AssembleKey. @@ -91,7 +91,7 @@ type MapNodeAssembler interface { ValueStyle(k string) NodeStyle } -type ListNodeAssembler interface { +type ListAssembler interface { AssembleValue() NodeAssembler Finish() error @@ -101,7 +101,7 @@ type ListNodeAssembler interface { // You often don't need this (because you should be able to // just feed data and check errors), but it's here. // - // In contrast to the `MapNodeAssembler.ValueStyle(key)` function, + // In contrast to the `MapAssembler.ValueStyle(key)` function, // to determine the ValueStyle for lists we need no parameters; // lists always contain one value type (even if it's "any"). ValueStyle() NodeStyle diff --git a/traversal/focus_test.go b/traversal/focus_test.go index d62a04c0..38dcd46b 100644 --- a/traversal/focus_test.go +++ b/traversal/focus_test.go @@ -28,21 +28,21 @@ var storage = make(map[ipld.Link][]byte) var ( leafAlpha, leafAlphaLnk = encode(basicnode.NewString("alpha")) leafBeta, leafBetaLnk = encode(basicnode.NewString("beta")) - middleMapNode, middleMapNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + middleMapNode, middleMapNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleDirectly("foo").AssignBool(true) na.AssembleDirectly("bar").AssignBool(false) - na.AssembleDirectly("nested").CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("nested").CreateMap(2, func(na fluent.MapAssembler) { na.AssembleDirectly("alink").AssignLink(leafAlphaLnk) na.AssembleDirectly("nonlink").AssignString("zoo") }) })) - middleListNode, middleListNodeLnk = encode(fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListNodeAssembler) { + middleListNode, middleListNodeLnk = encode(fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListAssembler) { na.AssembleValue().AssignLink(leafAlphaLnk) na.AssembleValue().AssignLink(leafAlphaLnk) na.AssembleValue().AssignLink(leafBetaLnk) na.AssembleValue().AssignLink(leafAlphaLnk) })) - rootNode, rootNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapNodeAssembler) { + rootNode, rootNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapAssembler) { na.AssembleDirectly("plain").AssignString("olde string") na.AssembleDirectly("linkedString").AssignLink(leafAlphaLnk) na.AssembleDirectly("linkedMap").AssignLink(middleMapNodeLnk) diff --git a/traversal/selector/builder/builder.go b/traversal/selector/builder/builder.go index 7ff34415..ecadbe29 100644 --- a/traversal/selector/builder/builder.go +++ b/traversal/selector/builder/builder.go @@ -68,22 +68,22 @@ func NewSelectorSpecBuilder(ns ipld.NodeStyle) SelectorSpecBuilder { func (ssb *selectorSpecBuilder) ExploreRecursiveEdge() SelectorSpec { return selectorSpec{ - fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) }), } } func (ssb *selectorSpecBuilder) ExploreRecursive(limit selector.RecursionLimit, sequence SelectorSpec) SelectorSpec { return selectorSpec{ - fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { switch limit.Mode() { case selector.RecursionLimit_Depth: na.AssembleDirectly(selector.SelectorKey_LimitDepth).AssignInt(limit.Depth()) case selector.RecursionLimit_None: - na.AssembleDirectly(selector.SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(selector.SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) default: panic("Unsupported recursion limit type") } @@ -96,8 +96,8 @@ func (ssb *selectorSpecBuilder) ExploreRecursive(limit selector.RecursionLimit, func (ssb *selectorSpecBuilder) ExploreAll(next SelectorSpec) SelectorSpec { return selectorSpec{ - fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { na.AssembleDirectly(selector.SelectorKey_Next).AssignNode(next.Node()) }) }), @@ -105,8 +105,8 @@ func (ssb *selectorSpecBuilder) ExploreAll(next SelectorSpec) SelectorSpec { } func (ssb *selectorSpecBuilder) ExploreIndex(index int, next SelectorSpec) SelectorSpec { return selectorSpec{ - fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapNodeAssembler) { + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapAssembler) { na.AssembleDirectly(selector.SelectorKey_Index).AssignInt(index) na.AssembleDirectly(selector.SelectorKey_Next).AssignNode(next.Node()) }) @@ -116,8 +116,8 @@ func (ssb *selectorSpecBuilder) ExploreIndex(index int, next SelectorSpec) Selec func (ssb *selectorSpecBuilder) ExploreRange(start int, end int, next SelectorSpec) SelectorSpec { return selectorSpec{ - fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRange).CreateMap(3, func(na fluent.MapNodeAssembler) { + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRange).CreateMap(3, func(na fluent.MapAssembler) { na.AssembleDirectly(selector.SelectorKey_Start).AssignInt(start) na.AssembleDirectly(selector.SelectorKey_End).AssignInt(end) na.AssembleDirectly(selector.SelectorKey_Next).AssignNode(next.Node()) @@ -128,8 +128,8 @@ func (ssb *selectorSpecBuilder) ExploreRange(start int, end int, next SelectorSp func (ssb *selectorSpecBuilder) ExploreUnion(members ...SelectorSpec) SelectorSpec { return selectorSpec{ - fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreUnion).CreateList(len(members), func(na fluent.ListNodeAssembler) { + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreUnion).CreateList(len(members), func(na fluent.ListAssembler) { for _, member := range members { na.AssembleValue().AssignNode(member.Node()) } @@ -140,9 +140,9 @@ func (ssb *selectorSpecBuilder) ExploreUnion(members ...SelectorSpec) SelectorSp func (ssb *selectorSpecBuilder) ExploreFields(specBuilder ExploreFieldsSpecBuildingClosure) SelectorSpec { return selectorSpec{ - fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreFields).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Fields).CreateMap(-1, func(na fluent.MapNodeAssembler) { + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreFields).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Fields).CreateMap(-1, func(na fluent.MapAssembler) { specBuilder(exploreFieldsSpecBuilder{na}) }) }) @@ -152,14 +152,14 @@ func (ssb *selectorSpecBuilder) ExploreFields(specBuilder ExploreFieldsSpecBuild func (ssb *selectorSpecBuilder) Matcher() SelectorSpec { return selectorSpec{ - fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }), } } type exploreFieldsSpecBuilder struct { - na fluent.MapNodeAssembler + na fluent.MapAssembler } func (efsb exploreFieldsSpecBuilder) Insert(field string, s SelectorSpec) { diff --git a/traversal/selector/builder/builder_test.go b/traversal/selector/builder/builder_test.go index cf543a10..32db45e5 100644 --- a/traversal/selector/builder/builder_test.go +++ b/traversal/selector/builder/builder_test.go @@ -14,24 +14,24 @@ func TestBuildingSelectors(t *testing.T) { ssb := NewSelectorSpecBuilder(ns) t.Run("Matcher builds matcher nodes", func(t *testing.T) { sn := ssb.Matcher().Node() - esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) Wish(t, sn, ShouldEqual, esn) }) t.Run("ExploreRecursiveEdge builds ExploreRecursiveEdge nodes", func(t *testing.T) { sn := ssb.ExploreRecursiveEdge().Node() - esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) }) Wish(t, sn, ShouldEqual, esn) }) t.Run("ExploreAll builds ExploreAll nodes", func(t *testing.T) { sn := ssb.ExploreAll(ssb.Matcher()).Node() - esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -39,11 +39,11 @@ func TestBuildingSelectors(t *testing.T) { }) t.Run("ExploreIndex builds ExploreIndex nodes", func(t *testing.T) { sn := ssb.ExploreIndex(2, ssb.Matcher()).Node() - esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapNodeAssembler) { + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapAssembler) { na.AssembleDirectly(selector.SelectorKey_Index).AssignInt(2) - na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -51,12 +51,12 @@ func TestBuildingSelectors(t *testing.T) { }) t.Run("ExploreRange builds ExploreRange nodes", func(t *testing.T) { sn := ssb.ExploreRange(2, 3, ssb.Matcher()).Node() - esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRange).CreateMap(3, func(na fluent.MapNodeAssembler) { + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRange).CreateMap(3, func(na fluent.MapAssembler) { na.AssembleDirectly(selector.SelectorKey_Start).AssignInt(2) na.AssembleDirectly(selector.SelectorKey_End).AssignInt(3) - na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -64,15 +64,15 @@ func TestBuildingSelectors(t *testing.T) { }) t.Run("ExploreRecursive builds ExploreRecursive nodes", func(t *testing.T) { sn := ssb.ExploreRecursive(selector.RecursionLimitDepth(2), ssb.ExploreAll(ssb.ExploreRecursiveEdge())).Node() - esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { na.AssembleDirectly(selector.SelectorKey_LimitDepth).AssignInt(2) }) - na.AssembleDirectly(selector.SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(selector.SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -80,15 +80,15 @@ func TestBuildingSelectors(t *testing.T) { }) Wish(t, sn, ShouldEqual, esn) sn = ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge())).Node() - esn = fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + esn = fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) }) - na.AssembleDirectly(selector.SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(selector.SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -98,16 +98,16 @@ func TestBuildingSelectors(t *testing.T) { }) t.Run("ExploreUnion builds ExploreUnion nodes", func(t *testing.T) { sn := ssb.ExploreUnion(ssb.Matcher(), ssb.ExploreIndex(2, ssb.Matcher())).Node() - esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreUnion).CreateList(2, func(na fluent.ListNodeAssembler) { - na.AssembleValue().CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreUnion).CreateList(2, func(na fluent.ListAssembler) { + na.AssembleValue().CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) - na.AssembleValue().CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleValue().CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapAssembler) { na.AssembleDirectly(selector.SelectorKey_Index).AssignInt(2) - na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -117,11 +117,11 @@ func TestBuildingSelectors(t *testing.T) { }) t.Run("ExploreFields builds ExploreFields nodes", func(t *testing.T) { sn := ssb.ExploreFields(func(efsb ExploreFieldsSpecBuilder) { efsb.Insert("applesauce", ssb.Matcher()) }).Node() - esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreFields).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Fields).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly("applesauce").CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_ExploreFields).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Fields).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly("applesauce").CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) diff --git a/traversal/selector/exploreAll_test.go b/traversal/selector/exploreAll_test.go index 9d85c1b2..95462d2a 100644 --- a/traversal/selector/exploreAll_test.go +++ b/traversal/selector/exploreAll_test.go @@ -17,22 +17,22 @@ func TestParseExploreAll(t *testing.T) { Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without next field should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapNodeAssembler) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapAssembler) {}) _, err := ParseContext{}.ParseExploreAll(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreAll selector")) }) t.Run("parsing map node without next field with invalid selector node should return child's error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Next).AssignInt(0) }) _, err := ParseContext{}.ParseExploreAll(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) s, err := ParseContext{}.ParseExploreAll(sn) diff --git a/traversal/selector/exploreFields_test.go b/traversal/selector/exploreFields_test.go index 4b026cfd..907ea5c6 100644 --- a/traversal/selector/exploreFields_test.go +++ b/traversal/selector/exploreFields_test.go @@ -18,20 +18,20 @@ func TestParseExploreFields(t *testing.T) { Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without fields value should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapNodeAssembler) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapAssembler) {}) _, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: fields in ExploreFields selector must be present")) }) t.Run("parsing map node with fields value that is not a map should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Fields).AssignString("cheese") }) _, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: fields in ExploreFields selector must be a map")) }) t.Run("parsing map node with selector node in fields that is invalid should return child's error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Fields).CreateMap(1, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Fields).CreateMap(1, func(na fluent.MapAssembler) { na.AssembleDirectly("applesauce").AssignInt(0) }) }) @@ -39,10 +39,10 @@ func TestParseExploreFields(t *testing.T) { Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with fields value that is map of only valid selector node should parse", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Fields).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly("applesauce").CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Fields).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly("applesauce").CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) diff --git a/traversal/selector/exploreIndex_test.go b/traversal/selector/exploreIndex_test.go index 454139cc..49af399d 100644 --- a/traversal/selector/exploreIndex_test.go +++ b/traversal/selector/exploreIndex_test.go @@ -18,33 +18,33 @@ func TestParseExploreIndex(t *testing.T) { Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without next field should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Index).AssignInt(2) }) _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreIndex selector")) }) t.Run("parsing map node without index field should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: index field must be present in ExploreIndex selector")) }) t.Run("parsing map node with index field that is not an int should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Index).AssignString("cheese") - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: index field must be a number in ExploreIndex selector")) }) t.Run("parsing map node with next field with invalid selector node should return child's error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Index).AssignInt(2) na.AssembleDirectly(SelectorKey_Next).AssignInt(0) }) @@ -52,10 +52,10 @@ func TestParseExploreIndex(t *testing.T) { Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Index).AssignInt(2) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) s, err := ParseContext{}.ParseExploreIndex(sn) @@ -67,11 +67,11 @@ func TestParseExploreIndex(t *testing.T) { func TestExploreIndexExplore(t *testing.T) { s := ExploreIndex{Matcher{}, [1]ipld.PathSegment{ipld.PathSegmentOfInt(3)}} t.Run("exploring should return nil unless node is a list", func(t *testing.T) { - n := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapNodeAssembler) {}) + n := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapAssembler) {}) returnedSelector := s.Explore(n, ipld.PathSegmentOfInt(3)) Wish(t, returnedSelector, ShouldEqual, nil) }) - n := fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListNodeAssembler) { + n := fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListAssembler) { na.AssembleValue().AssignInt(0) na.AssembleValue().AssignInt(1) na.AssembleValue().AssignInt(2) diff --git a/traversal/selector/exploreRange_test.go b/traversal/selector/exploreRange_test.go index 6e9fec9d..8b5c34c4 100644 --- a/traversal/selector/exploreRange_test.go +++ b/traversal/selector/exploreRange_test.go @@ -18,7 +18,7 @@ func TestParseExploreRange(t *testing.T) { Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without next field should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Start).AssignInt(2) na.AssembleDirectly(SelectorKey_End).AssignInt(3) }) @@ -26,60 +26,60 @@ func TestParseExploreRange(t *testing.T) { Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreRange selector")) }) t.Run("parsing map node without start field should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_End).AssignInt(3) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: start field must be present in ExploreRange selector")) }) t.Run("parsing map node with start field that is not an int should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Start).AssignString("cheese") na.AssembleDirectly(SelectorKey_End).AssignInt(3) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: start field must be a number in ExploreRange selector")) }) t.Run("parsing map node without end field should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Start).AssignInt(2) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: end field must be present in ExploreRange selector")) }) t.Run("parsing map node with end field that is not an int should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Start).AssignInt(2) na.AssembleDirectly(SelectorKey_End).AssignString("cheese") - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: end field must be a number in ExploreRange selector")) }) t.Run("parsing map node where end is not greater than start should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Start).AssignInt(3) na.AssembleDirectly(SelectorKey_End).AssignInt(2) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: end field must be greater than start field in ExploreRange selector")) }) t.Run("parsing map node with next field with invalid selector node should return child's error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Start).AssignInt(2) na.AssembleDirectly(SelectorKey_End).AssignInt(3) na.AssembleDirectly(SelectorKey_Next).AssignInt(0) @@ -89,11 +89,11 @@ func TestParseExploreRange(t *testing.T) { }) t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Start).AssignInt(2) na.AssembleDirectly(SelectorKey_End).AssignInt(3) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) s, err := ParseContext{}.ParseExploreRange(sn) @@ -105,11 +105,11 @@ func TestParseExploreRange(t *testing.T) { func TestExploreRangeExplore(t *testing.T) { s := ExploreRange{Matcher{}, 3, 4, []ipld.PathSegment{ipld.PathSegmentOfInt(3)}} t.Run("exploring should return nil unless node is a list", func(t *testing.T) { - n := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapNodeAssembler) {}) + n := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapAssembler) {}) returnedSelector := s.Explore(n, ipld.PathSegmentOfInt(3)) Wish(t, returnedSelector, ShouldEqual, nil) }) - n := fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListNodeAssembler) { + n := fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListAssembler) { na.AssembleValue().AssignInt(0) na.AssembleValue().AssignInt(1) na.AssembleValue().AssignInt(2) diff --git a/traversal/selector/exploreRecursive_test.go b/traversal/selector/exploreRecursive_test.go index 5fe1e0bd..7b3220cd 100644 --- a/traversal/selector/exploreRecursive_test.go +++ b/traversal/selector/exploreRecursive_test.go @@ -20,8 +20,8 @@ func TestParseExploreRecursive(t *testing.T) { Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without sequence field should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) }) }) @@ -29,64 +29,64 @@ func TestParseExploreRecursive(t *testing.T) { Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: sequence field must be present in ExploreRecursive selector")) }) t.Run("parsing map node without limit field should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: limit field must be present in ExploreRecursive selector")) }) t.Run("parsing map node with limit field that is not a map should fail", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Limit).AssignString("cheese") - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: limit in ExploreRecursive is a keyed union and thus must be a map")) }) t.Run("parsing map node with limit field that is not a single entry map should fail", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(2, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(2, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) - na.AssembleDirectly(SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) }) - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: limit in ExploreRecursive is a keyed union and thus must be a single-entry map")) }) t.Run("parsing map node with limit field that does not have a known key should fail", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { na.AssembleDirectly("applesauce").AssignInt(2) }) - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: \"applesauce\" is not a known member of the limit union in ExploreRecursive")) }) t.Run("parsing map node with limit field of type depth that is not an int should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_LimitDepth).AssignString("cheese") }) - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: limit field of type depth must be a number in ExploreRecursive selector")) }) t.Run("parsing map node with sequence field with invalid selector node should return child's error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) }) na.AssembleDirectly(SelectorKey_Sequence).AssignInt(0) @@ -95,14 +95,14 @@ func TestParseExploreRecursive(t *testing.T) { Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with sequence field with valid selector w/o ExploreRecursiveEdge should not parse", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) }) - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -111,19 +111,19 @@ func TestParseExploreRecursive(t *testing.T) { Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: ExploreRecursive must have at least one ExploreRecursiveEdge")) }) t.Run("parsing map node that is ExploreRecursiveEdge without ExploreRecursive parent should not parse", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapNodeAssembler) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapAssembler) {}) _, err := ParseContext{}.ParseExploreRecursiveEdge(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: ExploreRecursiveEdge must be beneath ExploreRecursive")) }) t.Run("parsing map node with sequence field with valid selector node should parse", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) }) - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -134,14 +134,14 @@ func TestParseExploreRecursive(t *testing.T) { }) t.Run("parsing map node with sequence field with valid selector node and limit type none should parse", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) }) - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) diff --git a/traversal/selector/exploreUnion_test.go b/traversal/selector/exploreUnion_test.go index 1cc5d97e..390f5b6c 100644 --- a/traversal/selector/exploreUnion_test.go +++ b/traversal/selector/exploreUnion_test.go @@ -13,14 +13,14 @@ import ( func TestParseExploreUnion(t *testing.T) { t.Run("parsing non list node should error", func(t *testing.T) { - sn := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapNodeAssembler) {}) + sn := fluent.MustBuildMap(basicnode.Style__Map{}, 0, func(na fluent.MapAssembler) {}) _, err := ParseContext{}.ParseExploreUnion(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: explore union selector must be a list")) }) t.Run("parsing list node where one node is invalid should return child's error", func(t *testing.T) { - sn := fluent.MustBuildList(basicnode.Style__List{}, 2, func(na fluent.ListNodeAssembler) { - na.AssembleValue().CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + sn := fluent.MustBuildList(basicnode.Style__List{}, 2, func(na fluent.ListAssembler) { + na.AssembleValue().CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) na.AssembleValue().AssignInt(2) }) @@ -29,15 +29,15 @@ func TestParseExploreUnion(t *testing.T) { }) t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { - sn := fluent.MustBuildList(basicnode.Style__List{}, 2, func(na fluent.ListNodeAssembler) { - na.AssembleValue().CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + sn := fluent.MustBuildList(basicnode.Style__List{}, 2, func(na fluent.ListAssembler) { + na.AssembleValue().CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) - na.AssembleValue().CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleValue().CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapAssembler) { na.AssembleDirectly(SelectorKey_Index).AssignInt(2) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapNodeAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapNodeAssembler) {}) + na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -49,7 +49,7 @@ func TestParseExploreUnion(t *testing.T) { } func TestExploreUnionExplore(t *testing.T) { - n := fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListNodeAssembler) { + n := fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListAssembler) { na.AssembleValue().AssignInt(0) na.AssembleValue().AssignInt(1) na.AssembleValue().AssignInt(2) diff --git a/traversal/walk_test.go b/traversal/walk_test.go index 87429ca4..442307e6 100644 --- a/traversal/walk_test.go +++ b/traversal/walk_test.go @@ -20,21 +20,21 @@ import ( var ( leafAlpha, leafAlphaLnk = encode(basicnode.NewString("alpha")) leafBeta, leafBetaLnk = encode(basicnode.NewString("beta")) - middleMapNode, middleMapNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapNodeAssembler) { + middleMapNode, middleMapNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleDirectly("foo").AssignBool(true) na.AssembleDirectly("bar").AssignBool(false) - na.AssembleDirectly("nested").CreateMap(2, func(na fluent.MapNodeAssembler) { + na.AssembleDirectly("nested").CreateMap(2, func(na fluent.MapAssembler) { na.AssembleDirectly("alink").AssignLink(leafAlphaLnk) na.AssembleDirectly("nonlink").AssignString("zoo") }) })) - middleListNode, middleListNodeLnk = encode(fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListNodeAssembler) { + middleListNode, middleListNodeLnk = encode(fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListAssembler) { na.AssembleValue().AssignLink(leafAlphaLnk) na.AssembleValue().AssignLink(leafAlphaLnk) na.AssembleValue().AssignLink(leafBetaLnk) na.AssembleValue().AssignLink(leafAlphaLnk) })) - rootNode, rootNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapNodeAssembler) { + rootNode, rootNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapAssembler) { na.AssembleDirectly("plain").AssignString("olde string") na.AssembleDirectly("linkedString").AssignLink(leafAlphaLnk) na.AssembleDirectly("linkedMap").AssignLink(middleMapNodeLnk) @@ -140,7 +140,7 @@ func TestWalkMatching(t *testing.T) { Wish(t, n, ShouldEqual, basicnode.NewBool(false)) Wish(t, prog.Path.String(), ShouldEqual, "bar") case 3: - Wish(t, n, ShouldEqual, fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapNodeAssembler) { + Wish(t, n, ShouldEqual, fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { na.AssembleDirectly("alink").AssignLink(leafAlphaLnk) na.AssembleDirectly("nonlink").AssignString("zoo") })) From b3c369aef92c14bc37c1c8de2debd80e0ee28fc8 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Wed, 11 Mar 2020 12:00:09 +0100 Subject: [PATCH 07/11] ListAssembler.ValueStyle does need a parameter. It needs this for dealing with the exciting details of... well, you can read the comment. This is a heck of an example of how the schema system, even though we've been almost completely successful in isolating it in packages and not letting it appear or be referenced in any of the core interfaces and main dependencies... is still informing and shaping a few key details of the central interfaces. Future IPLD library implementers in other languages may want to take note of this commit for the above reason: it's useful to make sure you've also taken account of schema systems in your library design at an early point. Even if you don't intend to implement them before doing a 1.0 of the core interfaces and Data Model essentials, knowing what expressiveness will be needed will save you a lot of work. --- fluent/fluentBuilder.go | 6 +++--- node/basic/list.go | 4 ++-- node/basic/map.go | 2 +- nodeBuilder.go | 16 ++++++++++++---- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/fluent/fluentBuilder.go b/fluent/fluentBuilder.go index f99d2b12..b220bff1 100644 --- a/fluent/fluentBuilder.go +++ b/fluent/fluentBuilder.go @@ -72,7 +72,7 @@ type MapAssembler interface { type ListAssembler interface { AssembleValue() NodeAssembler - ValueStyle() ipld.NodeStyle + ValueStyle(idx int) ipld.NodeStyle } type nodeAssembler struct { @@ -174,6 +174,6 @@ type listNodeAssembler struct { func (fla *listNodeAssembler) AssembleValue() NodeAssembler { return &nodeAssembler{fla.la.AssembleValue()} } -func (fla *listNodeAssembler) ValueStyle() ipld.NodeStyle { - return fla.la.ValueStyle() +func (fla *listNodeAssembler) ValueStyle(idx int) ipld.NodeStyle { + return fla.la.ValueStyle(idx) } diff --git a/node/basic/list.go b/node/basic/list.go index af616f90..7d3ad95e 100644 --- a/node/basic/list.go +++ b/node/basic/list.go @@ -217,7 +217,7 @@ func (la *plainList__Assembler) Finish() error { // validators could run and report errors promptly, if this type had any. return nil } -func (plainList__Assembler) ValueStyle() ipld.NodeStyle { +func (plainList__Assembler) ValueStyle(_ int) ipld.NodeStyle { return Style__Any{} } @@ -320,7 +320,7 @@ type plainList__ValueAssemblerList struct { func (la *plainList__ValueAssemblerList) AssembleValue() ipld.NodeAssembler { return la.ca.AssembleValue() } -func (plainList__ValueAssemblerList) ValueStyle() ipld.NodeStyle { +func (plainList__ValueAssemblerList) ValueStyle(_ int) ipld.NodeStyle { return Style__Any{} } diff --git a/node/basic/map.go b/node/basic/map.go index 37081d4c..ca50535f 100644 --- a/node/basic/map.go +++ b/node/basic/map.go @@ -428,7 +428,7 @@ type plainMap__ValueAssemblerList struct { func (la *plainMap__ValueAssemblerList) AssembleValue() ipld.NodeAssembler { return la.ca.AssembleValue() } -func (plainMap__ValueAssemblerList) ValueStyle() ipld.NodeStyle { +func (plainMap__ValueAssemblerList) ValueStyle(_ int) ipld.NodeStyle { return Style__Any{} } diff --git a/nodeBuilder.go b/nodeBuilder.go index a78dbdf4..bef1e0bf 100644 --- a/nodeBuilder.go +++ b/nodeBuilder.go @@ -101,10 +101,18 @@ type ListAssembler interface { // You often don't need this (because you should be able to // just feed data and check errors), but it's here. // - // In contrast to the `MapAssembler.ValueStyle(key)` function, - // to determine the ValueStyle for lists we need no parameters; - // lists always contain one value type (even if it's "any"). - ValueStyle() NodeStyle + // ValueStyle, much like the matching method on the MapAssembler interface, + // requires a parameter specifying the index in the list in order to say + // what NodeStyle will be acceptable as a value at that position. + // For many lists (and *all* lists which operate exclusively at the Data Model level), + // this will return the same NodeStyle regardless of the value of 'idx'; + // the only time this value will vary is when operating with a Schema, + // and handling the representation NodeAssembler for a struct type with + // a representation of a list kind. + // If you know you are operating in a situation that won't have varying + // NodeStyles, it is acceptable to call `ValueStyle(0)` and use the + // resulting NodeStyle for all reasoning. + ValueStyle(idx int) NodeStyle } type NodeBuilder interface { From 9de3d2231101fcb6e77c8921f3d45d07f3b2fc75 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Thu, 26 Mar 2020 11:48:46 +0100 Subject: [PATCH 08/11] Finish AssignNode handling for recursives. Update to the hackme document to match. (Turns out a decent amount of early speculations about how handy it would be to nil out the 'w' pointer ended up a bit misguided.) This spawns another one of those things that's dang hard to test: half of these functions are for handling the case that something of the right *kind* but a different *style*/implementation is given... which we can't test except by ramming two different node implementations together. Even later, that's going to make things rather Interesting, because the test package dependencies will include one concrete node package and then be that much less usable by that package itself (ugh); getting correct coverage attribution to the odd package out there will be hard; and for now, it means we just plain have no tests for those branches. Making tests to check compatibility and correctness of these branches once we get more codegen integration up will be fun, though. --- node/basic/HACKME.md | 52 +++++++++++++++++++++++++++++--------------- node/basic/list.go | 35 ++++++++++++++++++++++++----- node/basic/map.go | 38 +++++++++++++++++++++++++++----- 3 files changed, 97 insertions(+), 28 deletions(-) diff --git a/node/basic/HACKME.md b/node/basic/HACKME.md index 538abe29..7f1c4bc9 100644 --- a/node/basic/HACKME.md +++ b/node/basic/HACKME.md @@ -65,25 +65,41 @@ if we're inside recursive structure, the parent assembler did so. The 'w' pointer is used throughout the life of the assembler. -When assembly becomes "finished", the 'w' pointer should be set to nil, -in order to make it impossible to continue to mutate that node. -However, this doesn't *quite* work... because in the case of builders (at the root), +Setting the 'w' pointer to nil is one of two mechanisms used internally +to mark that assembly has become "finished" (the other mechanism is using +an internal state enum field). +Setting the 'w' pointer to nil has two advantages: +one is that it makes it *impossible* to continue to mutate the target node; +the other is that we need no *additional* memory to track this state change. +However, we can't use the strategy of nilling 'w' in all cases: in particular, +when in the NodeBuilder at the root of some construction, we need to continue to hold onto the node between when it becomes "finished" -and when Build is called, allowing us to return it (and then finally nil 'w'). - -This has some kinda wild implications. In recursive structures, it means the -child assembler wrapper type is the one who takes reponsibility for nilling out -the 'w' pointer at the same time as it updates the parent's state machine to -proceed with the next entry. In the case of scalars at the root of a build, -it means *you can actually use the assign method more than once*. - -We could normalize the case with scalars at the root of a tree by adding another -piece of memory to the scalar builders... but currently we haven't bothered. -We're not in trouble on compositional correctness because nothing's visible -until Build is called (whereas recursives have to be a lot stricter around this, -because if something gets validated and finished, it's already essential that -it now be unable to mutate out of the validated state, even if it's also not -yet 'visible'). +and when Build is called; otherwise we can't actually return the value! +Different stratgies are therefore used in different parts of this package. + +Maps and lists use an internal state enum, because they already have one, +and so they might as well; there's no additional cost to this. +Since they can use this state to guard against additional mutations after "finish", +the map and list assemblers don't bother to nil their own 'w' at all. + +During recursion to assemble values _inside_ maps and lists, it's interesting: +the child assembler wrapper type takes reponsibility for nilling out +the 'w' pointer in the child assembler's state, doing this at the same time as +it updates the parent's state machine to clear proceeding with the next entry. + +In the case of scalars at the root of a build, we took a shortcut: +we actually don't fence against repeat mutations at all. +*You can actually use the assign method more than once*. +We can do this without breaking safety contracts because the scalars +all have a pass-by-value phase somewhere in their lifecycle +(calling `nb.AssignString("x")`, then `n := nb.Build()`, then `nb.AssignString("y")` +won't error if `nb` is a freestanding builder for strings... but it also +won't result in mutating `n` to contain `"y"`, so overall, it's safe). + +We could normalize the case with scalars at the root of a tree so that they +error more aggressively... but currently we haven't bothered, since this would +require adding another piece of memory to the scalar builders; and meanwhile +we're not in trouble on compositional correctness. Note that these remarks are for the `basicnode` package, but may also apply to other implementations too (e.g., our codegen output follows similar diff --git a/node/basic/list.go b/node/basic/list.go index 7d3ad95e..ddeb573b 100644 --- a/node/basic/list.go +++ b/node/basic/list.go @@ -181,11 +181,36 @@ func (plainList__Assembler) AssignLink(ipld.Link) error { return mixins.ListAssembler{"list"}.AssignLink(nil) } func (na *plainList__Assembler) AssignNode(v ipld.Node) error { - // todo: apply a generic 'copy' function. - // todo: probably can also shortcut to copying na.t and na.m if it's our same concrete type? - // (can't quite just `na.w = v`, because we don't have 'freeze' features, and we don't wanna open door to mutation of 'v'.) - // (wait... actually, probably we can? 'Assign' is a "finish" method. we can&should invalidate the wip pointer here.) - panic("later") + // Sanity check, then update, assembler state. + 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. + *na.w = *v2 + return nil + } + // If the above shortcut didn't work, resort to a generic copy. + // We call AssignNode for all the child values, giving them a chance to hit shortcuts even if we didn't. + if v.ReprKind() != ipld.ReprKind_List { + return ipld.ErrWrongKind{TypeName: "list", 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 + } + } + // validators could run and report errors promptly, if this type had any -- same as for regular Finish. + return nil } func (plainList__Assembler) Style() ipld.NodeStyle { return Style__List{} diff --git a/node/basic/map.go b/node/basic/map.go index ca50535f..dcb53141 100644 --- a/node/basic/map.go +++ b/node/basic/map.go @@ -197,11 +197,39 @@ func (plainMap__Assembler) AssignLink(ipld.Link) error { return mixins.MapAssembler{"map"}.AssignLink(nil) } func (na *plainMap__Assembler) AssignNode(v ipld.Node) error { - // todo: apply a generic 'copy' function. - // todo: probably can also shortcut to copying na.t and na.m if it's our same concrete type? - // (can't quite just `na.w = v`, because we don't have 'freeze' features, and we don't wanna open door to mutation of 'v'.) - // (wait... actually, probably we can? 'Assign' is a "finish" method. we can&should invalidate the wip pointer here.) - panic("later") + // Sanity check, then update, assembler state. + 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. + *na.w = *v2 + return nil + } + // If the above shortcut didn't work, resort to a generic copy. + // We call AssignNode for all the child values, giving them a chance to hit shortcuts even if we didn't. + if v.ReprKind() != ipld.ReprKind_Map { + return ipld.ErrWrongKind{TypeName: "map", 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 + } + } + // validators could run and report errors promptly, if this type had any -- same as for regular Finish. + return nil } func (plainMap__Assembler) Style() ipld.NodeStyle { return Style__Map{} From de201f2d32fa4fe4c07e6bf358286f7d074224a0 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Thu, 26 Mar 2020 12:09:43 +0100 Subject: [PATCH 09/11] Rename 's/AssembleDirectly/AssembleEntry/g'. --- codec/dagcbor/roundtrip_test.go | 14 ++-- codec/dagcbor/unmarshal.go | 2 +- codec/dagjson/roundtrip_test.go | 14 ++-- codec/dagjson/unmarshal.go | 2 +- codec/unmarshal.go | 2 +- fluent/fluentBuilder.go | 6 +- fluent/fluentBuilder_test.go | 12 +-- node/basic/list.go | 4 +- node/basic/map.go | 8 +- node/basic/map_test.go | 8 +- node/gendemo/map_K2_T2.go | 6 +- node/gendemo/map_K_T.go | 6 +- node/gendemo/map_K_T_test.go | 8 +- node/tests/mapBenchmarks.go | 12 +-- nodeBuilder.go | 2 +- schema/tests/testsForStructs.go | 6 +- traversal/focus_test.go | 18 ++--- traversal/selector/builder/builder.go | 40 +++++----- traversal/selector/builder/builder_test.go | 76 +++++++++---------- traversal/selector/exploreAll_test.go | 6 +- traversal/selector/exploreFields_test.go | 12 +-- traversal/selector/exploreIndex_test.go | 22 +++--- traversal/selector/exploreRange_test.go | 54 +++++++------- traversal/selector/exploreRecursive_test.go | 82 ++++++++++----------- traversal/selector/exploreUnion_test.go | 12 +-- traversal/walk_test.go | 22 +++--- 26 files changed, 228 insertions(+), 228 deletions(-) diff --git a/codec/dagcbor/roundtrip_test.go b/codec/dagcbor/roundtrip_test.go index e35ac9ce..3d56d525 100644 --- a/codec/dagcbor/roundtrip_test.go +++ b/codec/dagcbor/roundtrip_test.go @@ -11,17 +11,17 @@ import ( ) var n = fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapAssembler) { - na.AssembleDirectly("plain").AssignString("olde string") - na.AssembleDirectly("map").CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleDirectly("one").AssignInt(1) - na.AssembleDirectly("two").AssignInt(2) + na.AssembleEntry("plain").AssignString("olde string") + na.AssembleEntry("map").CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry("one").AssignInt(1) + na.AssembleEntry("two").AssignInt(2) }) - na.AssembleDirectly("list").CreateList(2, func(na fluent.ListAssembler) { + na.AssembleEntry("list").CreateList(2, func(na fluent.ListAssembler) { na.AssembleValue().AssignString("three") na.AssembleValue().AssignString("four") }) - na.AssembleDirectly("nested").CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly("deeper").CreateList(1, func(na fluent.ListAssembler) { + na.AssembleEntry("nested").CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry("deeper").CreateList(1, func(na fluent.ListAssembler) { na.AssembleValue().AssignString("things") }) }) diff --git a/codec/dagcbor/unmarshal.go b/codec/dagcbor/unmarshal.go index 34bb0a73..d22d40f9 100644 --- a/codec/dagcbor/unmarshal.go +++ b/codec/dagcbor/unmarshal.go @@ -70,7 +70,7 @@ func unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token) if observedLen > expectLen { return fmt.Errorf("unexpected continuation of map elements beyond declared length") } - mva, err := ma.AssembleDirectly(tk.Str) + mva, err := ma.AssembleEntry(tk.Str) if err != nil { // return in error if the key was rejected return err } diff --git a/codec/dagjson/roundtrip_test.go b/codec/dagjson/roundtrip_test.go index d7d9b842..b9463e4c 100644 --- a/codec/dagjson/roundtrip_test.go +++ b/codec/dagjson/roundtrip_test.go @@ -11,17 +11,17 @@ import ( ) var n = fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapAssembler) { - na.AssembleDirectly("plain").AssignString("olde string") - na.AssembleDirectly("map").CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleDirectly("one").AssignInt(1) - na.AssembleDirectly("two").AssignInt(2) + na.AssembleEntry("plain").AssignString("olde string") + na.AssembleEntry("map").CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry("one").AssignInt(1) + na.AssembleEntry("two").AssignInt(2) }) - na.AssembleDirectly("list").CreateList(2, func(na fluent.ListAssembler) { + na.AssembleEntry("list").CreateList(2, func(na fluent.ListAssembler) { na.AssembleValue().AssignString("three") na.AssembleValue().AssignString("four") }) - na.AssembleDirectly("nested").CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly("deeper").CreateList(1, func(na fluent.ListAssembler) { + na.AssembleEntry("nested").CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry("deeper").CreateList(1, func(na fluent.ListAssembler) { na.AssembleValue().AssignString("things") }) }) diff --git a/codec/dagjson/unmarshal.go b/codec/dagjson/unmarshal.go index 5e8484ec..a4d3da15 100644 --- a/codec/dagjson/unmarshal.go +++ b/codec/dagjson/unmarshal.go @@ -161,7 +161,7 @@ func (st *unmarshalState) unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSo default: return fmt.Errorf("unexpected %s token while expecting map key", st.tk[0].Type) } - mva, err := ma.AssembleDirectly(st.tk[0].Str) + mva, err := ma.AssembleEntry(st.tk[0].Str) if err != nil { // return in error if the key was rejected return err } diff --git a/codec/unmarshal.go b/codec/unmarshal.go index 7a9a4d4d..17cb76d7 100644 --- a/codec/unmarshal.go +++ b/codec/unmarshal.go @@ -85,7 +85,7 @@ func unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token) if observedLen > expectLen { return fmt.Errorf("unexpected continuation of map elements beyond declared length") } - mva, err := ma.AssembleDirectly(tk.Str) + mva, err := ma.AssembleEntry(tk.Str) if err != nil { // return in error if the key was rejected return err } diff --git a/fluent/fluentBuilder.go b/fluent/fluentBuilder.go index b220bff1..99b0e841 100644 --- a/fluent/fluentBuilder.go +++ b/fluent/fluentBuilder.go @@ -58,7 +58,7 @@ type MapAssembler interface { AssembleKey() NodeAssembler AssembleValue() NodeAssembler - AssembleDirectly(k string) NodeAssembler + AssembleEntry(k string) NodeAssembler KeyStyle() ipld.NodeStyle ValueStyle(k string) ipld.NodeStyle @@ -153,8 +153,8 @@ func (fma *mapNodeAssembler) AssembleKey() NodeAssembler { func (fma *mapNodeAssembler) AssembleValue() NodeAssembler { return &nodeAssembler{fma.ma.AssembleValue()} } -func (fma *mapNodeAssembler) AssembleDirectly(k string) NodeAssembler { - va, err := fma.ma.AssembleDirectly(k) +func (fma *mapNodeAssembler) AssembleEntry(k string) NodeAssembler { + va, err := fma.ma.AssembleEntry(k) if err != nil { panic(Error{err}) } diff --git a/fluent/fluentBuilder_test.go b/fluent/fluentBuilder_test.go index d5e617c2..ee5c8032 100644 --- a/fluent/fluentBuilder_test.go +++ b/fluent/fluentBuilder_test.go @@ -24,12 +24,12 @@ func TestBuild(t *testing.T) { t.Run("map build should work", func(t *testing.T) { n := fluent.MustBuild(basicnode.Style__Map{}, func(fna fluent.NodeAssembler) { fna.CreateMap(3, func(fma fluent.MapAssembler) { - fma.AssembleDirectly("k1").AssignString("fine") - fma.AssembleDirectly("k2").AssignString("super") - fma.AssembleDirectly("k3").CreateMap(3, func(fma fluent.MapAssembler) { - fma.AssembleDirectly("k31").AssignString("thanks") - fma.AssembleDirectly("k32").AssignString("for") - fma.AssembleDirectly("k33").AssignString("asking") + fma.AssembleEntry("k1").AssignString("fine") + fma.AssembleEntry("k2").AssignString("super") + fma.AssembleEntry("k3").CreateMap(3, func(fma fluent.MapAssembler) { + fma.AssembleEntry("k31").AssignString("thanks") + fma.AssembleEntry("k32").AssignString("for") + fma.AssembleEntry("k33").AssignString("asking") }) }) }) diff --git a/node/basic/list.go b/node/basic/list.go index ddeb573b..c00875f2 100644 --- a/node/basic/list.go +++ b/node/basic/list.go @@ -308,8 +308,8 @@ type plainList__ValueAssemblerMap struct { // just embedding plainMap__Assembler also behaves correctly, // but causes a lot of unnecessary autogenerated functions in the final binary. -func (ma *plainList__ValueAssemblerMap) AssembleDirectly(k string) (ipld.NodeAssembler, error) { - return ma.ca.AssembleDirectly(k) +func (ma *plainList__ValueAssemblerMap) AssembleEntry(k string) (ipld.NodeAssembler, error) { + return ma.ca.AssembleEntry(k) } func (ma *plainList__ValueAssemblerMap) AssembleKey() ipld.NodeAssembler { return ma.ca.AssembleKey() diff --git a/node/basic/map.go b/node/basic/map.go index dcb53141..f6d2ef45 100644 --- a/node/basic/map.go +++ b/node/basic/map.go @@ -237,9 +237,9 @@ func (plainMap__Assembler) Style() ipld.NodeStyle { // -- MapAssembler --> -// AssembleDirectly is part of conforming to MapAssembler, which we do on +// 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) AssembleDirectly(k string) (ipld.NodeAssembler, error) { +func (ma *plainMap__Assembler) AssembleEntry(k string) (ipld.NodeAssembler, error) { // Sanity check, then update, assembler state. if ma.state != maState_initial { panic("misuse") @@ -419,8 +419,8 @@ type plainMap__ValueAssemblerMap struct { // just embedding plainMap__Assembler also behaves correctly, // but causes a lot of unnecessary autogenerated functions in the final binary. -func (ma *plainMap__ValueAssemblerMap) AssembleDirectly(k string) (ipld.NodeAssembler, error) { - return ma.ca.AssembleDirectly(k) +func (ma *plainMap__ValueAssemblerMap) AssembleEntry(k string) (ipld.NodeAssembler, error) { + return ma.ca.AssembleEntry(k) } func (ma *plainMap__ValueAssemblerMap) AssembleKey() ipld.NodeAssembler { return ma.ca.AssembleKey() diff --git a/node/basic/map_test.go b/node/basic/map_test.go index 3778a09a..da803650 100644 --- a/node/basic/map_test.go +++ b/node/basic/map_test.go @@ -14,8 +14,8 @@ func TestMap(t *testing.T) { func BenchmarkMapStrInt_3n_AssembleStandard(b *testing.B) { tests.SpecBenchmarkMapStrInt_3n_AssembleStandard(b, Style__Map{}) } -func BenchmarkMapStrInt_3n_AssembleDirectly(b *testing.B) { - tests.SpecBenchmarkMapStrInt_3n_AssembleDirectly(b, Style__Map{}) +func BenchmarkMapStrInt_3n_AssembleEntry(b *testing.B) { + tests.SpecBenchmarkMapStrInt_3n_AssembleEntry(b, Style__Map{}) } func BenchmarkMapStrInt_3n_Iteration(b *testing.B) { tests.SpecBenchmarkMapStrInt_3n_Iteration(b, Style__Map{}) @@ -24,8 +24,8 @@ func BenchmarkMapStrInt_3n_Iteration(b *testing.B) { func BenchmarkMapStrInt_25n_AssembleStandard(b *testing.B) { tests.SpecBenchmarkMapStrInt_25n_AssembleStandard(b, Style__Map{}) } -func BenchmarkMapStrInt_25n_AssembleDirectly(b *testing.B) { - tests.SpecBenchmarkMapStrInt_25n_AssembleDirectly(b, Style__Map{}) +func BenchmarkMapStrInt_25n_AssembleEntry(b *testing.B) { + tests.SpecBenchmarkMapStrInt_25n_AssembleEntry(b, Style__Map{}) } func BenchmarkMapStrInt_25n_Iteration(b *testing.B) { tests.SpecBenchmarkMapStrInt_25n_Iteration(b, Style__Map{}) diff --git a/node/gendemo/map_K2_T2.go b/node/gendemo/map_K2_T2.go index f4c3a2bb..7f0d83a0 100644 --- a/node/gendemo/map_K2_T2.go +++ b/node/gendemo/map_K2_T2.go @@ -150,7 +150,7 @@ func (ta *_K2__Assembler) AssignNode(v ipld.Node) error { } func (_K2__Assembler) Style() ipld.NodeStyle { panic("later") } -func (ma *_K2__Assembler) AssembleDirectly(k string) (ipld.NodeAssembler, error) { +func (ma *_K2__Assembler) AssembleEntry(k string) (ipld.NodeAssembler, error) { // Sanity check, then update, assembler state. if ma.state != maState_initial { panic("misuse") @@ -183,7 +183,7 @@ func (ma *_K2__Assembler) AssembleKey() ipld.NodeAssembler { panic("misuse") } ma.state = maState_midKey - // TODO return a fairly dummy assembler which just contains a string switch (probably sharing code with AssembleDirectly). + // 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 { @@ -333,7 +333,7 @@ func (ta *_T2__Assembler) AssignNode(v ipld.Node) error { } func (_T2__Assembler) Style() ipld.NodeStyle { panic("later") } -func (ta *_T2__Assembler) AssembleDirectly(string) (ipld.NodeAssembler, error) { +func (ta *_T2__Assembler) AssembleEntry(string) (ipld.NodeAssembler, error) { // this'll be fun panic("soon") } diff --git a/node/gendemo/map_K_T.go b/node/gendemo/map_K_T.go index 7679d962..0b1f1c17 100644 --- a/node/gendemo/map_K_T.go +++ b/node/gendemo/map_K_T.go @@ -161,8 +161,8 @@ type _T__ReprAssembler struct { 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 (_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 @@ -371,7 +371,7 @@ func (ta *_Map_K_T__Assembler) AssignNode(v ipld.Node) error { } func (_Map_K_T__Assembler) Style() ipld.NodeStyle { panic("later") } -func (ma *_Map_K_T__Assembler) AssembleDirectly(k string) (ipld.NodeAssembler, error) { +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") diff --git a/node/gendemo/map_K_T_test.go b/node/gendemo/map_K_T_test.go index ae8d775b..c67d466d 100644 --- a/node/gendemo/map_K_T_test.go +++ b/node/gendemo/map_K_T_test.go @@ -13,8 +13,8 @@ func TestGennedMapStrInt(t *testing.T) { func BenchmarkMapStrInt_3n_AssembleStandard(b *testing.B) { tests.SpecBenchmarkMapStrInt_3n_AssembleStandard(b, Type__Map_K_T{}) } -func BenchmarkMapStrInt_3n_AssembleDirectly(b *testing.B) { - tests.SpecBenchmarkMapStrInt_3n_AssembleDirectly(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{}) @@ -23,8 +23,8 @@ func BenchmarkMapStrInt_3n_Iteration(b *testing.B) { func BenchmarkMapStrInt_25n_AssembleStandard(b *testing.B) { tests.SpecBenchmarkMapStrInt_25n_AssembleStandard(b, Type__Map_K_T{}) } -func BenchmarkMapStrInt_25n_AssembleDirectly(b *testing.B) { - tests.SpecBenchmarkMapStrInt_25n_AssembleDirectly(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/tests/mapBenchmarks.go b/node/tests/mapBenchmarks.go index 4846b85b..4b3a0f70 100644 --- a/node/tests/mapBenchmarks.go +++ b/node/tests/mapBenchmarks.go @@ -13,24 +13,24 @@ func SpecBenchmarkMapStrInt_3n_AssembleStandard(b *testing.B, ns ipld.NodeStyle) } } -func SpecBenchmarkMapStrInt_3n_AssembleDirectly(b *testing.B, ns ipld.NodeStyle) { +func SpecBenchmarkMapStrInt_3n_AssembleEntry(b *testing.B, ns ipld.NodeStyle) { for i := 0; i < b.N; i++ { nb := ns.NewBuilder() ma, err := nb.BeginMap(3) if err != nil { panic(err) } - if va, err := ma.AssembleDirectly("whee"); err != nil { + if va, err := ma.AssembleEntry("whee"); err != nil { panic(err) } else { must.NotError(va.AssignInt(1)) } - if va, err := ma.AssembleDirectly("woot"); err != nil { + if va, err := ma.AssembleEntry("woot"); err != nil { panic(err) } else { must.NotError(va.AssignInt(2)) } - if va, err := ma.AssembleDirectly("waga"); err != nil { + if va, err := ma.AssembleEntry("waga"); err != nil { panic(err) } else { must.NotError(va.AssignInt(3)) @@ -60,7 +60,7 @@ func SpecBenchmarkMapStrInt_25n_AssembleStandard(b *testing.B, ns ipld.NodeStyle } } -func SpecBenchmarkMapStrInt_25n_AssembleDirectly(b *testing.B, ns ipld.NodeStyle) { +func SpecBenchmarkMapStrInt_25n_AssembleEntry(b *testing.B, ns ipld.NodeStyle) { for i := 0; i < b.N; i++ { nb := ns.NewBuilder() ma, err := nb.BeginMap(25) @@ -68,7 +68,7 @@ func SpecBenchmarkMapStrInt_25n_AssembleDirectly(b *testing.B, ns ipld.NodeStyle panic(err) } for i := 1; i <= 25; i++ { - if va, err := ma.AssembleDirectly(tableStrInt[i-1].s); err != nil { + if va, err := ma.AssembleEntry(tableStrInt[i-1].s); err != nil { panic(err) } else { must.NotError(va.AssignInt(tableStrInt[i-1].i)) diff --git a/nodeBuilder.go b/nodeBuilder.go index bef1e0bf..20d92957 100644 --- a/nodeBuilder.go +++ b/nodeBuilder.go @@ -59,7 +59,7 @@ type MapAssembler interface { AssembleKey() NodeAssembler // must be followed by call to AssembleValue. AssembleValue() NodeAssembler // must be called immediately after AssembleKey. - AssembleDirectly(k string) (NodeAssembler, error) // shortcut combining AssembleKey and AssembleValue into one step; valid when the key is a string kind. + AssembleEntry(k string) (NodeAssembler, error) // shortcut combining AssembleKey and AssembleValue into one step; valid when the key is a string kind. Finish() error diff --git a/schema/tests/testsForStructs.go b/schema/tests/testsForStructs.go index 615f19fc..7b9bdad7 100644 --- a/schema/tests/testsForStructs.go +++ b/schema/tests/testsForStructs.go @@ -27,16 +27,16 @@ func TestFoo(t *testing.T, newNb func(typ schema.Type) ipld.NodeBuilder) { ma, err := nb.BeginMap(3) Wish(t, err, ShouldEqual, nil) // Set 'f1' to a valid string. - va, err := ma.AssembleDirectly("f1") + va, err := ma.AssembleEntry("f1") Wish(t, err, ShouldEqual, nil) Wish(t, va.AssignString("asdf"), ShouldEqual, nil) // Skip setting 'f2' -- it's optional. // Set 'f3' to null. Nulls aren't typed. - va, err = ma.AssembleDirectly("f3") + va, err = ma.AssembleEntry("f3") Wish(t, err, ShouldEqual, nil) Wish(t, va.AssignNull(), ShouldEqual, nil) // Set 'f4' to a valid string. - va, err = ma.AssembleDirectly("f4") + va, err = ma.AssembleEntry("f4") Wish(t, err, ShouldEqual, nil) Wish(t, va.AssignString("qwer"), ShouldEqual, nil) Wish(t, ma.Finish(), ShouldEqual, nil) diff --git a/traversal/focus_test.go b/traversal/focus_test.go index 38dcd46b..be94f19e 100644 --- a/traversal/focus_test.go +++ b/traversal/focus_test.go @@ -29,11 +29,11 @@ var ( leafAlpha, leafAlphaLnk = encode(basicnode.NewString("alpha")) leafBeta, leafBetaLnk = encode(basicnode.NewString("beta")) middleMapNode, middleMapNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { - na.AssembleDirectly("foo").AssignBool(true) - na.AssembleDirectly("bar").AssignBool(false) - na.AssembleDirectly("nested").CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleDirectly("alink").AssignLink(leafAlphaLnk) - na.AssembleDirectly("nonlink").AssignString("zoo") + na.AssembleEntry("foo").AssignBool(true) + na.AssembleEntry("bar").AssignBool(false) + na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry("alink").AssignLink(leafAlphaLnk) + na.AssembleEntry("nonlink").AssignString("zoo") }) })) middleListNode, middleListNodeLnk = encode(fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListAssembler) { @@ -43,10 +43,10 @@ var ( na.AssembleValue().AssignLink(leafAlphaLnk) })) rootNode, rootNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapAssembler) { - na.AssembleDirectly("plain").AssignString("olde string") - na.AssembleDirectly("linkedString").AssignLink(leafAlphaLnk) - na.AssembleDirectly("linkedMap").AssignLink(middleMapNodeLnk) - na.AssembleDirectly("linkedList").AssignLink(middleListNodeLnk) + na.AssembleEntry("plain").AssignString("olde string") + na.AssembleEntry("linkedString").AssignLink(leafAlphaLnk) + na.AssembleEntry("linkedMap").AssignLink(middleMapNodeLnk) + na.AssembleEntry("linkedList").AssignLink(middleListNodeLnk) })) ) diff --git a/traversal/selector/builder/builder.go b/traversal/selector/builder/builder.go index ecadbe29..39c4cf2f 100644 --- a/traversal/selector/builder/builder.go +++ b/traversal/selector/builder/builder.go @@ -69,7 +69,7 @@ func NewSelectorSpecBuilder(ns ipld.NodeStyle) SelectorSpecBuilder { func (ssb *selectorSpecBuilder) ExploreRecursiveEdge() SelectorSpec { return selectorSpec{ fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) }), } } @@ -77,18 +77,18 @@ func (ssb *selectorSpecBuilder) ExploreRecursiveEdge() SelectorSpec { func (ssb *selectorSpecBuilder) ExploreRecursive(limit selector.RecursionLimit, sequence SelectorSpec) SelectorSpec { return selectorSpec{ fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { switch limit.Mode() { case selector.RecursionLimit_Depth: - na.AssembleDirectly(selector.SelectorKey_LimitDepth).AssignInt(limit.Depth()) + na.AssembleEntry(selector.SelectorKey_LimitDepth).AssignInt(limit.Depth()) case selector.RecursionLimit_None: - na.AssembleDirectly(selector.SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) default: panic("Unsupported recursion limit type") } }) - na.AssembleDirectly(selector.SelectorKey_Sequence).AssignNode(sequence.Node()) + na.AssembleEntry(selector.SelectorKey_Sequence).AssignNode(sequence.Node()) }) }), } @@ -97,8 +97,8 @@ func (ssb *selectorSpecBuilder) ExploreRecursive(limit selector.RecursionLimit, func (ssb *selectorSpecBuilder) ExploreAll(next SelectorSpec) SelectorSpec { return selectorSpec{ fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Next).AssignNode(next.Node()) + na.AssembleEntry(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Next).AssignNode(next.Node()) }) }), } @@ -106,9 +106,9 @@ func (ssb *selectorSpecBuilder) ExploreAll(next SelectorSpec) SelectorSpec { func (ssb *selectorSpecBuilder) ExploreIndex(index int, next SelectorSpec) SelectorSpec { return selectorSpec{ fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Index).AssignInt(index) - na.AssembleDirectly(selector.SelectorKey_Next).AssignNode(next.Node()) + na.AssembleEntry(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Index).AssignInt(index) + na.AssembleEntry(selector.SelectorKey_Next).AssignNode(next.Node()) }) }), } @@ -117,10 +117,10 @@ func (ssb *selectorSpecBuilder) ExploreIndex(index int, next SelectorSpec) Selec func (ssb *selectorSpecBuilder) ExploreRange(start int, end int, next SelectorSpec) SelectorSpec { return selectorSpec{ fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRange).CreateMap(3, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Start).AssignInt(start) - na.AssembleDirectly(selector.SelectorKey_End).AssignInt(end) - na.AssembleDirectly(selector.SelectorKey_Next).AssignNode(next.Node()) + na.AssembleEntry(selector.SelectorKey_ExploreRange).CreateMap(3, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Start).AssignInt(start) + na.AssembleEntry(selector.SelectorKey_End).AssignInt(end) + na.AssembleEntry(selector.SelectorKey_Next).AssignNode(next.Node()) }) }), } @@ -129,7 +129,7 @@ func (ssb *selectorSpecBuilder) ExploreRange(start int, end int, next SelectorSp func (ssb *selectorSpecBuilder) ExploreUnion(members ...SelectorSpec) SelectorSpec { return selectorSpec{ fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreUnion).CreateList(len(members), func(na fluent.ListAssembler) { + na.AssembleEntry(selector.SelectorKey_ExploreUnion).CreateList(len(members), func(na fluent.ListAssembler) { for _, member := range members { na.AssembleValue().AssignNode(member.Node()) } @@ -141,8 +141,8 @@ func (ssb *selectorSpecBuilder) ExploreUnion(members ...SelectorSpec) SelectorSp func (ssb *selectorSpecBuilder) ExploreFields(specBuilder ExploreFieldsSpecBuildingClosure) SelectorSpec { return selectorSpec{ fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreFields).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Fields).CreateMap(-1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_ExploreFields).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Fields).CreateMap(-1, func(na fluent.MapAssembler) { specBuilder(exploreFieldsSpecBuilder{na}) }) }) @@ -153,7 +153,7 @@ func (ssb *selectorSpecBuilder) ExploreFields(specBuilder ExploreFieldsSpecBuild func (ssb *selectorSpecBuilder) Matcher() SelectorSpec { return selectorSpec{ fluent.MustBuildMap(ssb.ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }), } } @@ -163,5 +163,5 @@ type exploreFieldsSpecBuilder struct { } func (efsb exploreFieldsSpecBuilder) Insert(field string, s SelectorSpec) { - efsb.na.AssembleDirectly(field).AssignNode(s.Node()) + efsb.na.AssembleEntry(field).AssignNode(s.Node()) } diff --git a/traversal/selector/builder/builder_test.go b/traversal/selector/builder/builder_test.go index 32db45e5..bc9ddcaa 100644 --- a/traversal/selector/builder/builder_test.go +++ b/traversal/selector/builder/builder_test.go @@ -15,23 +15,23 @@ func TestBuildingSelectors(t *testing.T) { t.Run("Matcher builds matcher nodes", func(t *testing.T) { sn := ssb.Matcher().Node() esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) Wish(t, sn, ShouldEqual, esn) }) t.Run("ExploreRecursiveEdge builds ExploreRecursiveEdge nodes", func(t *testing.T) { sn := ssb.ExploreRecursiveEdge().Node() esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) }) Wish(t, sn, ShouldEqual, esn) }) t.Run("ExploreAll builds ExploreAll nodes", func(t *testing.T) { sn := ssb.ExploreAll(ssb.Matcher()).Node() esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -40,10 +40,10 @@ func TestBuildingSelectors(t *testing.T) { t.Run("ExploreIndex builds ExploreIndex nodes", func(t *testing.T) { sn := ssb.ExploreIndex(2, ssb.Matcher()).Node() esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Index).AssignInt(2) - na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Index).AssignInt(2) + na.AssembleEntry(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -52,11 +52,11 @@ func TestBuildingSelectors(t *testing.T) { t.Run("ExploreRange builds ExploreRange nodes", func(t *testing.T) { sn := ssb.ExploreRange(2, 3, ssb.Matcher()).Node() esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRange).CreateMap(3, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Start).AssignInt(2) - na.AssembleDirectly(selector.SelectorKey_End).AssignInt(3) - na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_ExploreRange).CreateMap(3, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Start).AssignInt(2) + na.AssembleEntry(selector.SelectorKey_End).AssignInt(3) + na.AssembleEntry(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -65,14 +65,14 @@ func TestBuildingSelectors(t *testing.T) { t.Run("ExploreRecursive builds ExploreRecursive nodes", func(t *testing.T) { sn := ssb.ExploreRecursive(selector.RecursionLimitDepth(2), ssb.ExploreAll(ssb.ExploreRecursiveEdge())).Node() esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_LimitDepth).AssignInt(2) + na.AssembleEntry(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_LimitDepth).AssignInt(2) }) - na.AssembleDirectly(selector.SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -81,14 +81,14 @@ func TestBuildingSelectors(t *testing.T) { Wish(t, sn, ShouldEqual, esn) sn = ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge())).Node() esn = fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_ExploreRecursive).CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) }) - na.AssembleDirectly(selector.SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -99,15 +99,15 @@ func TestBuildingSelectors(t *testing.T) { t.Run("ExploreUnion builds ExploreUnion nodes", func(t *testing.T) { sn := ssb.ExploreUnion(ssb.Matcher(), ssb.ExploreIndex(2, ssb.Matcher())).Node() esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreUnion).CreateList(2, func(na fluent.ListAssembler) { + na.AssembleEntry(selector.SelectorKey_ExploreUnion).CreateList(2, func(na fluent.ListAssembler) { na.AssembleValue().CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) na.AssembleValue().CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Index).AssignInt(2) - na.AssembleDirectly(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Index).AssignInt(2) + na.AssembleEntry(selector.SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -118,10 +118,10 @@ func TestBuildingSelectors(t *testing.T) { t.Run("ExploreFields builds ExploreFields nodes", func(t *testing.T) { sn := ssb.ExploreFields(func(efsb ExploreFieldsSpecBuilder) { efsb.Insert("applesauce", ssb.Matcher()) }).Node() esn := fluent.MustBuildMap(ns, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_ExploreFields).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Fields).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly("applesauce").CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(selector.SelectorKey_ExploreFields).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Fields).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry("applesauce").CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(selector.SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) diff --git a/traversal/selector/exploreAll_test.go b/traversal/selector/exploreAll_test.go index 95462d2a..fa7b84a1 100644 --- a/traversal/selector/exploreAll_test.go +++ b/traversal/selector/exploreAll_test.go @@ -24,15 +24,15 @@ func TestParseExploreAll(t *testing.T) { t.Run("parsing map node without next field with invalid selector node should return child's error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Next).AssignInt(0) + na.AssembleEntry(SelectorKey_Next).AssignInt(0) }) _, err := ParseContext{}.ParseExploreAll(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) s, err := ParseContext{}.ParseExploreAll(sn) diff --git a/traversal/selector/exploreFields_test.go b/traversal/selector/exploreFields_test.go index 907ea5c6..52a6bffb 100644 --- a/traversal/selector/exploreFields_test.go +++ b/traversal/selector/exploreFields_test.go @@ -24,15 +24,15 @@ func TestParseExploreFields(t *testing.T) { }) t.Run("parsing map node with fields value that is not a map should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Fields).AssignString("cheese") + na.AssembleEntry(SelectorKey_Fields).AssignString("cheese") }) _, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: fields in ExploreFields selector must be a map")) }) t.Run("parsing map node with selector node in fields that is invalid should return child's error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Fields).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly("applesauce").AssignInt(0) + na.AssembleEntry(SelectorKey_Fields).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry("applesauce").AssignInt(0) }) }) _, err := ParseContext{}.ParseExploreFields(sn) @@ -40,9 +40,9 @@ func TestParseExploreFields(t *testing.T) { }) t.Run("parsing map node with fields value that is map of only valid selector node should parse", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Fields).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly("applesauce").CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Fields).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry("applesauce").CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) diff --git a/traversal/selector/exploreIndex_test.go b/traversal/selector/exploreIndex_test.go index 49af399d..6dd0eb16 100644 --- a/traversal/selector/exploreIndex_test.go +++ b/traversal/selector/exploreIndex_test.go @@ -19,15 +19,15 @@ func TestParseExploreIndex(t *testing.T) { }) t.Run("parsing map node without next field should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Index).AssignInt(2) + na.AssembleEntry(SelectorKey_Index).AssignInt(2) }) _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreIndex selector")) }) t.Run("parsing map node without index field should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreIndex(sn) @@ -35,9 +35,9 @@ func TestParseExploreIndex(t *testing.T) { }) t.Run("parsing map node with index field that is not an int should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Index).AssignString("cheese") - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Index).AssignString("cheese") + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreIndex(sn) @@ -45,17 +45,17 @@ func TestParseExploreIndex(t *testing.T) { }) t.Run("parsing map node with next field with invalid selector node should return child's error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Index).AssignInt(2) - na.AssembleDirectly(SelectorKey_Next).AssignInt(0) + na.AssembleEntry(SelectorKey_Index).AssignInt(2) + na.AssembleEntry(SelectorKey_Next).AssignInt(0) }) _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Index).AssignInt(2) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Index).AssignInt(2) + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) s, err := ParseContext{}.ParseExploreIndex(sn) diff --git a/traversal/selector/exploreRange_test.go b/traversal/selector/exploreRange_test.go index 8b5c34c4..2e62b4e2 100644 --- a/traversal/selector/exploreRange_test.go +++ b/traversal/selector/exploreRange_test.go @@ -19,17 +19,17 @@ func TestParseExploreRange(t *testing.T) { }) t.Run("parsing map node without next field should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Start).AssignInt(2) - na.AssembleDirectly(SelectorKey_End).AssignInt(3) + na.AssembleEntry(SelectorKey_Start).AssignInt(2) + na.AssembleEntry(SelectorKey_End).AssignInt(3) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreRange selector")) }) t.Run("parsing map node without start field should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_End).AssignInt(3) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_End).AssignInt(3) + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRange(sn) @@ -37,10 +37,10 @@ func TestParseExploreRange(t *testing.T) { }) t.Run("parsing map node with start field that is not an int should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Start).AssignString("cheese") - na.AssembleDirectly(SelectorKey_End).AssignInt(3) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Start).AssignString("cheese") + na.AssembleEntry(SelectorKey_End).AssignInt(3) + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRange(sn) @@ -48,9 +48,9 @@ func TestParseExploreRange(t *testing.T) { }) t.Run("parsing map node without end field should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Start).AssignInt(2) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Start).AssignInt(2) + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRange(sn) @@ -58,10 +58,10 @@ func TestParseExploreRange(t *testing.T) { }) t.Run("parsing map node with end field that is not an int should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Start).AssignInt(2) - na.AssembleDirectly(SelectorKey_End).AssignString("cheese") - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Start).AssignInt(2) + na.AssembleEntry(SelectorKey_End).AssignString("cheese") + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRange(sn) @@ -69,10 +69,10 @@ func TestParseExploreRange(t *testing.T) { }) t.Run("parsing map node where end is not greater than start should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Start).AssignInt(3) - na.AssembleDirectly(SelectorKey_End).AssignInt(2) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Start).AssignInt(3) + na.AssembleEntry(SelectorKey_End).AssignInt(2) + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRange(sn) @@ -80,9 +80,9 @@ func TestParseExploreRange(t *testing.T) { }) t.Run("parsing map node with next field with invalid selector node should return child's error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Start).AssignInt(2) - na.AssembleDirectly(SelectorKey_End).AssignInt(3) - na.AssembleDirectly(SelectorKey_Next).AssignInt(0) + na.AssembleEntry(SelectorKey_Start).AssignInt(2) + na.AssembleEntry(SelectorKey_End).AssignInt(3) + na.AssembleEntry(SelectorKey_Next).AssignInt(0) }) _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) @@ -90,10 +90,10 @@ func TestParseExploreRange(t *testing.T) { t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Start).AssignInt(2) - na.AssembleDirectly(SelectorKey_End).AssignInt(3) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Start).AssignInt(2) + na.AssembleEntry(SelectorKey_End).AssignInt(3) + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) s, err := ParseContext{}.ParseExploreRange(sn) diff --git a/traversal/selector/exploreRecursive_test.go b/traversal/selector/exploreRecursive_test.go index 7b3220cd..30c2d205 100644 --- a/traversal/selector/exploreRecursive_test.go +++ b/traversal/selector/exploreRecursive_test.go @@ -21,8 +21,8 @@ func TestParseExploreRecursive(t *testing.T) { }) t.Run("parsing map node without sequence field should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) + na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_LimitDepth).AssignInt(2) }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) @@ -30,8 +30,8 @@ func TestParseExploreRecursive(t *testing.T) { }) t.Run("parsing map node without limit field should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) @@ -39,9 +39,9 @@ func TestParseExploreRecursive(t *testing.T) { }) t.Run("parsing map node with limit field that is not a map should fail", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Limit).AssignString("cheese") - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Limit).AssignString("cheese") + na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) @@ -49,12 +49,12 @@ func TestParseExploreRecursive(t *testing.T) { }) t.Run("parsing map node with limit field that is not a single entry map should fail", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) - na.AssembleDirectly(SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Limit).CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_LimitDepth).AssignInt(2) + na.AssembleEntry(SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) }) - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) @@ -62,11 +62,11 @@ func TestParseExploreRecursive(t *testing.T) { }) t.Run("parsing map node with limit field that does not have a known key should fail", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly("applesauce").AssignInt(2) + na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry("applesauce").AssignInt(2) }) - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) @@ -74,11 +74,11 @@ func TestParseExploreRecursive(t *testing.T) { }) t.Run("parsing map node with limit field of type depth that is not an int should error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_LimitDepth).AssignString("cheese") + na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_LimitDepth).AssignString("cheese") }) - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) _, err := ParseContext{}.ParseExploreRecursive(sn) @@ -86,23 +86,23 @@ func TestParseExploreRecursive(t *testing.T) { }) t.Run("parsing map node with sequence field with invalid selector node should return child's error", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) + na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_LimitDepth).AssignInt(2) }) - na.AssembleDirectly(SelectorKey_Sequence).AssignInt(0) + na.AssembleEntry(SelectorKey_Sequence).AssignInt(0) }) _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with sequence field with valid selector w/o ExploreRecursiveEdge should not parse", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) + na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_LimitDepth).AssignInt(2) }) - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -117,13 +117,13 @@ func TestParseExploreRecursive(t *testing.T) { }) t.Run("parsing map node with sequence field with valid selector node should parse", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_LimitDepth).AssignInt(2) + na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_LimitDepth).AssignInt(2) }) - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) @@ -135,13 +135,13 @@ func TestParseExploreRecursive(t *testing.T) { t.Run("parsing map node with sequence field with valid selector node and limit type none should parse", func(t *testing.T) { sn := fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) }) - na.AssembleDirectly(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) diff --git a/traversal/selector/exploreUnion_test.go b/traversal/selector/exploreUnion_test.go index 390f5b6c..1db0853f 100644 --- a/traversal/selector/exploreUnion_test.go +++ b/traversal/selector/exploreUnion_test.go @@ -20,7 +20,7 @@ func TestParseExploreUnion(t *testing.T) { t.Run("parsing list node where one node is invalid should return child's error", func(t *testing.T) { sn := fluent.MustBuildList(basicnode.Style__List{}, 2, func(na fluent.ListAssembler) { na.AssembleValue().CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) na.AssembleValue().AssignInt(2) }) @@ -31,13 +31,13 @@ func TestParseExploreUnion(t *testing.T) { t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { sn := fluent.MustBuildList(basicnode.Style__List{}, 2, func(na fluent.ListAssembler) { na.AssembleValue().CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) na.AssembleValue().CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Index).AssignInt(2) - na.AssembleDirectly(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { - na.AssembleDirectly(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) + na.AssembleEntry(SelectorKey_ExploreIndex).CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Index).AssignInt(2) + na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { + na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) }) }) }) diff --git a/traversal/walk_test.go b/traversal/walk_test.go index 442307e6..a76a6b62 100644 --- a/traversal/walk_test.go +++ b/traversal/walk_test.go @@ -21,11 +21,11 @@ var ( leafAlpha, leafAlphaLnk = encode(basicnode.NewString("alpha")) leafBeta, leafBetaLnk = encode(basicnode.NewString("beta")) middleMapNode, middleMapNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 3, func(na fluent.MapAssembler) { - na.AssembleDirectly("foo").AssignBool(true) - na.AssembleDirectly("bar").AssignBool(false) - na.AssembleDirectly("nested").CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleDirectly("alink").AssignLink(leafAlphaLnk) - na.AssembleDirectly("nonlink").AssignString("zoo") + na.AssembleEntry("foo").AssignBool(true) + na.AssembleEntry("bar").AssignBool(false) + na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry("alink").AssignLink(leafAlphaLnk) + na.AssembleEntry("nonlink").AssignString("zoo") }) })) middleListNode, middleListNodeLnk = encode(fluent.MustBuildList(basicnode.Style__List{}, 4, func(na fluent.ListAssembler) { @@ -35,10 +35,10 @@ var ( na.AssembleValue().AssignLink(leafAlphaLnk) })) rootNode, rootNodeLnk = encode(fluent.MustBuildMap(basicnode.Style__Map{}, 4, func(na fluent.MapAssembler) { - na.AssembleDirectly("plain").AssignString("olde string") - na.AssembleDirectly("linkedString").AssignLink(leafAlphaLnk) - na.AssembleDirectly("linkedMap").AssignLink(middleMapNodeLnk) - na.AssembleDirectly("linkedList").AssignLink(middleListNodeLnk) + na.AssembleEntry("plain").AssignString("olde string") + na.AssembleEntry("linkedString").AssignLink(leafAlphaLnk) + na.AssembleEntry("linkedMap").AssignLink(middleMapNodeLnk) + na.AssembleEntry("linkedList").AssignLink(middleListNodeLnk) })) ) */ @@ -141,8 +141,8 @@ func TestWalkMatching(t *testing.T) { Wish(t, prog.Path.String(), ShouldEqual, "bar") case 3: Wish(t, n, ShouldEqual, fluent.MustBuildMap(basicnode.Style__Map{}, 2, func(na fluent.MapAssembler) { - na.AssembleDirectly("alink").AssignLink(leafAlphaLnk) - na.AssembleDirectly("nonlink").AssignString("zoo") + na.AssembleEntry("alink").AssignLink(leafAlphaLnk) + na.AssembleEntry("nonlink").AssignString("zoo") })) Wish(t, prog.Path.String(), ShouldEqual, "nested") case 4: From ce94db1fdd28bd8071c2800bd1ed718e24f3d607 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Fri, 27 Mar 2020 12:58:01 +0100 Subject: [PATCH 10/11] Expose styles with nicer syntax. I'm not sure if there's a name for this way of grouping methods, but the end result is dotted.Access.Patterns, and it's kinda nice. This was extensively discussed in the PR: https://github.com/ipld/go-ipld-prime/pull/49#issuecomment-596465234 https://github.com/ipld/go-ipld-prime/pull/49#issuecomment-597549108 https://github.com/ipld/go-ipld-prime/pull/49#issuecomment-604412736 The "inlinability_test.go" file can be compiled with special flags (described in the comment at the top of the file) to see the outcome in assembly. Result? Yep, things are still inlinable; this change is performance neutral. --- node/basic/HACKME.md | 43 ++++++++++++++++---- node/basic/inlinability/inlinability_test.go | 22 ++++++++++ node/basic/style.go | 26 ++++++++++++ 3 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 node/basic/inlinability/inlinability_test.go create mode 100644 node/basic/style.go diff --git a/node/basic/HACKME.md b/node/basic/HACKME.md index 7f1c4bc9..fa090f9f 100644 --- a/node/basic/HACKME.md +++ b/node/basic/HACKME.md @@ -105,14 +105,41 @@ Note that these remarks are for the `basicnode` package, but may also apply to other implementations too (e.g., our codegen output follows similar overall logic). -### nodestyles are implemented as exported concrete types +### nodestyles are available through a singleton -This is sorta arbitrary. We could equally easily make them exported as -package scope vars (which happen to be singletons), or as functions -(which happen to have fixed returns). +Every NodeStyle available from this package is exposed as a field +in a struct of which there's one public exported instance available, +called 'Style'. -These choices affect syntax, but not really semantics. +This means you can use it like this: -In any of these cases, we'd still end up with concrete types per style -(so that there's a place to hang the methods)... so, it seemed simplest -to just export them. +```go +nbm := basicnode.Style.Map.NewBuilder() +nbs := basicnode.Style.String.NewBuilder() +nba := basicnode.Style.Any.NewBuilder() +// etc +``` + +(If you're interested in the performance of this: it's free! +Methods called at the end of the chain are inlinable. +Since all of the types of the structures on the way there are zero-member +structs, the compiler can effectively treat them as constants, +and thus freely elide any memory dereferences that would +otherwise be necessary to get methods on such a value.) + +### nodestyles are (also) available as exported concrete types + +The 'Style' singleton is one way to access the NodeStyle in this package; +their exported types are another equivalent way. + +```go +basicnode.Style.Map = basicnode.Style__Map{} +``` + +It is recommended to use the singleton style; +they compile to identical assembly, and the singleton is syntactically prettier. + +We may make these concrete types unexported in the future. +A decision on this is deferred until some time has passed and +we can accumulate reasonable certainty that there's no need for an exported type +(such as type assertions, etc). diff --git a/node/basic/inlinability/inlinability_test.go b/node/basic/inlinability/inlinability_test.go new file mode 100644 index 00000000..a73b2462 --- /dev/null +++ b/node/basic/inlinability/inlinability_test.go @@ -0,0 +1,22 @@ +// Compile with '-gcflags -S' and grep the assembly for `"".Test`. +// You'll find that both methods produce identical bodies modulo line numbers. +package inlinability + +import ( + "testing" + + ipld "github.com/ipld/go-ipld-prime" + basicnode "github.com/ipld/go-ipld-prime/node/basic" +) + +var sink ipld.NodeBuilder + +func TestStructReference(t *testing.T) { + nb := basicnode.Style__String{}.NewBuilder() + sink = nb +} + +func TestVarReference(t *testing.T) { + nb := basicnode.Style.String.NewBuilder() + sink = nb +} diff --git a/node/basic/style.go b/node/basic/style.go new file mode 100644 index 00000000..a5b87f55 --- /dev/null +++ b/node/basic/style.go @@ -0,0 +1,26 @@ +package basicnode + +// Style embeds a NodeStyle for every kind of Node implementation in this package. +// You can use it like this: +// +// basicnode.Style.Map.NewBuilder().BeginMap() //... +// +// and: +// +// basicnode.Style.String.NewBuilder().AssignString("x") // ... +// +// Most of the styles here are for one particular Kind of node (e.g. string, int, etc); +// you can use the "Any" style if you want a builder that can accept any kind of data. +var Style style + +type style struct { + Any Style__Any + Map Style__Map + List Style__List + Bool Style__Bool + Int Style__Int + Float Style__Float + String Style__String + Bytes Style__Bytes + Link Style__Link +} From 8a3e80f794864d4a6137dbc411ffec36ebd401e8 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Fri, 27 Mar 2020 13:03:23 +0100 Subject: [PATCH 11/11] Discard inlinability check. This was only useful as a design research expedition. It's here in history if you want to refer to it. --- node/basic/inlinability/inlinability_test.go | 22 -------------------- 1 file changed, 22 deletions(-) delete mode 100644 node/basic/inlinability/inlinability_test.go diff --git a/node/basic/inlinability/inlinability_test.go b/node/basic/inlinability/inlinability_test.go deleted file mode 100644 index a73b2462..00000000 --- a/node/basic/inlinability/inlinability_test.go +++ /dev/null @@ -1,22 +0,0 @@ -// Compile with '-gcflags -S' and grep the assembly for `"".Test`. -// You'll find that both methods produce identical bodies modulo line numbers. -package inlinability - -import ( - "testing" - - ipld "github.com/ipld/go-ipld-prime" - basicnode "github.com/ipld/go-ipld-prime/node/basic" -) - -var sink ipld.NodeBuilder - -func TestStructReference(t *testing.T) { - nb := basicnode.Style__String{}.NewBuilder() - sink = nb -} - -func TestVarReference(t *testing.T) { - nb := basicnode.Style.String.NewBuilder() - sink = nb -}