Closed warpfork closed 3 years ago
I might start working on this immediately. I'm nerdsniped, and I've stubbed my toe on this repeatedly; I don't want to keep stubbing my toe on it every time I try to write good new examples for the godocs.
Some thoughts:
I think fmt.Stringer
would be the simplest, and I would argue that we should strongly recommend that nodes implement a String
method anyway. It's just too useful for quick debugging.
Like you say, it gets tricky with complex types like structs or maps. Before you implement anything, have you tried something like https://godoc.org/github.com/kr/pretty#Println? If it's close to being good enough, we could likely implement something very similar that's IPLD-specific and does a better job for our needs. The big advantage here over String
being that complex structures would be able to be shown in a prettier way, with newlines and indentation that can be configured.
Then there's fmt.GoStringer
like you say, but IMO we should just automate that via a reflection API like kr/pretty
. It makes little sense to me to have every node implement this themselves.
I'll give a quick eyeball to that pretty
library, but I think in this case we'll probably do something ourselves, yeah. Customizability is probably not a virtue for this; I'd rather not take on a transitive dependency for this; and also, I'm kind of hoping to use this as an opportunity to do a little research spike in the direction of https://github.com/ipld/go-ipld-prime/issues/86 .
I think I might do a little feature-detection for nodes matching the schema.TypedNode
interface, and annotate their printout with type info in a standard way, too. (This'll make the result even more clearly not a multicodec, because that kind of info would be verboten to emit or expect to read in a multicodec, because it would violate layering, but it's already established this wouldn't be a multicodec so that should be okay.)
Sounds good to me. My only strong opinion here is that the bulk of the code should be generic, and that a Node implementation shouldn't need extra methods to support basic pretty-printing. Reflection here shouldn't be a problem at all, because pretty-printing is for humans.
As mentioned on Slack, I'd like to be able to use something like this for portable test fixtures, so it'd be good if the output was generic enough that it was simple to implement in other languages. I've been thinking that a pretty-print version of dag-json might be the most sensible way forward here because we have dag-json in most places that matter and those implementations should be straightforward to hack to prett-print. It's these darn CIDs and Bytes that get in the way and we already have a convention in dag-json so I guess we ought to re-use that.
dag-json-pretty
could be a legit codec with strong rules about whitespace .. btw.
https://github.com/ipld/go-ipld-prime/issues/191 is tangentally related to this. tl;dr: There are some under-answered questions about what a good UX is around absent values when typed nodes are in play.
It also means that trying to implement a pretty-print using some build-a-codec helper functions (as https://github.com/ipld/go-ipld-prime/issues/86 might dream of) is fraught with a few elements of peril, because it's looking like codecs generally shouldn't handle Absent
(and should error if they see it), whereas a pretty-print should handle Absent
in order to be maximally useful.
Delightfully, I feel that I can now call this closed, due to the introduction of #238.
I'm trying to write some examples for docs, and the output is... erm.
Node implementation internals tend to be full of details that are not useful to present to users. For example, a basicnode handed to
fmt.Printf("%v", n)
will spout out some nonsense like&{map[k1:0xc000097ef0 k2:0xc000097f00 k3:0xc0000e0e60] [{k1 0xc000097ef0} ...
We need some stringers.
I think the way to go about this is probably two-step:
When we're writing examples for documentation, I'm not sure if we'll then prefer to call that codec with a node argument, or just plain Printf the node in question, but we can figure out which of those is preferable later. I kinda figure I want
"%v"
to not be a mess of pointers, anyway; that's just not helpful.I said "codec" above, but I don't think this has to be a multicodec or anything. It's probably fine if it's an implementation detail of this library and not standardized across IPLD. And I really don't care if it's parseable again at all; emit-only is fine for this usecase. The only thing that makes it like a "codec" is that it's going to want to recurse over nodes in roughly the same way, and it emits a stream of bytes to an
io.Writer
.The closest workaround right now: is to emit dag-json, or, if you want control over the whitespace (which, yes please, for the use case of human-readable examples!), do something terribly custom like this: https://github.com/ipld/go-ipld-prime/blob/350032422383277e6545b9b1a49112123b5c43fb/schema/gen/go/testcase_test.go#L209-L212
These work, as a workaround, but there's two drawbacks to this:
refmt
library is a little perturbing