diff --git a/core/coreapi/interface/options/unixfs.go b/core/coreapi/interface/options/unixfs.go index 819cc3b6b..6dbab93b6 100644 --- a/core/coreapi/interface/options/unixfs.go +++ b/core/coreapi/interface/options/unixfs.go @@ -44,6 +44,9 @@ type UnixfsAddSettings struct { type UnixfsLsSettings struct { Async bool + + ResolveType bool + ResolveSize bool } type UnixfsAddOption func(*UnixfsAddSettings) error @@ -320,3 +323,17 @@ func (unixfsOpts) Async(async bool) UnixfsLsOption { return nil } } + +func (unixfsOpts) ResolveSize(resolve bool) UnixfsLsOption { + return func(settings *UnixfsLsSettings) error { + settings.ResolveSize = resolve + return nil + } +} + +func (unixfsOpts) ResolveType(resolve bool) UnixfsLsOption { + return func(settings *UnixfsLsSettings) error { + settings.ResolveSize = resolve + return nil + } +} \ No newline at end of file diff --git a/core/coreapi/interface/unixfs.go b/core/coreapi/interface/unixfs.go index ba2673fee..846b74629 100644 --- a/core/coreapi/interface/unixfs.go +++ b/core/coreapi/interface/unixfs.go @@ -2,10 +2,10 @@ package iface import ( "context" - "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ft "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs" + "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs/pb" + ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" ) @@ -16,6 +16,14 @@ type AddEvent struct { Size string `json:",omitempty"` } +type LsLink struct { + Link *ipld.Link + Size uint64 + Type unixfs_pb.Data_DataType + + Err error +} + // UnixfsAPI is the basic interface to immutable files in IPFS // NOTE: This API is heavily WIP, things are guaranteed to break frequently type UnixfsAPI interface { @@ -31,5 +39,5 @@ type UnixfsAPI interface { Get(context.Context, Path) (files.Node, error) // Ls returns the list of links in a directory - Ls(context.Context, Path, ...options.UnixfsLsOption) (<-chan ft.LinkResult, error) + Ls(context.Context, Path, ...options.UnixfsLsOption) (<-chan LsLink, error) } diff --git a/core/coreapi/unixfs.go b/core/coreapi/unixfs.go index 5d23d360d..9a66de3ca 100644 --- a/core/coreapi/unixfs.go +++ b/core/coreapi/unixfs.go @@ -15,11 +15,13 @@ import ( unixfile "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs/file" uio "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs/io" mfs "gx/ipfs/QmR66iEqVtNMbbZxTHPY3F6W5QLFqZEDbFD7gzbE9HpYXU/go-mfs" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" bstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" blockservice "gx/ipfs/QmVKQHuzni68SWByzJgBUCwHvvr4TWiXfutNWWwpZpp4rE/go-blockservice" files "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" dag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" + merkledag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" dagtest "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag/test" cidutil "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil" ) @@ -143,7 +145,7 @@ func (api *UnixfsAPI) Get(ctx context.Context, p coreiface.Path) (files.Node, er // Ls returns the contents of an IPFS or IPNS object(s) at path p, with the format: // ` ` -func (api *UnixfsAPI) Ls(ctx context.Context, p coreiface.Path, opts ...options.UnixfsLsOption) (<-chan ft.LinkResult, error) { +func (api *UnixfsAPI) Ls(ctx context.Context, p coreiface.Path, opts ...options.UnixfsLsOption) (<-chan coreiface.LsLink, error) { settings, err := options.UnixfsLsOptions(opts...) if err != nil { return nil, err @@ -156,36 +158,93 @@ func (api *UnixfsAPI) Ls(ctx context.Context, p coreiface.Path, opts ...options. dir, err := uio.NewDirectoryFromNode(api.dag, dagnode) if err == uio.ErrNotADir { - return lsFromLinks(dagnode.Links()) + return api.lsFromLinks(ctx, dagnode.Links(), settings) } if err != nil { return nil, err } if !settings.Async { - return lsFromDir(ctx, dir) + return api.lsFromDir(ctx, dir, settings) } - return lsFromLinksAsync(ctx, dir) + return api.lsFromLinksAsync(ctx, dir, settings) } -func lsFromLinksAsync(ctx context.Context, dir uio.Directory) (<-chan ft.LinkResult, error) { +func (api *UnixfsAPI) processLink(ctx context.Context, linkres ft.LinkResult, settings *options.UnixfsLsSettings) coreiface.LsLink { + lnk := coreiface.LsLink{ + Link: linkres.Link, + Err: linkres.Err, + } + if lnk.Err != nil { + return lnk + } - return dir.EnumLinksAsync(ctx), nil + switch lnk.Link.Cid.Type() { + case cid.Raw: + // No need to check with raw leaves + lnk.Type = ft.TFile + lnk.Size = lnk.Link.Size + case cid.DagProtobuf: + if !settings.ResolveSize && !settings.ResolveType { + break + } + + linkNode, err := lnk.Link.GetNode(ctx, api.dag) + if err != nil { + lnk.Err = err + break + } + + if pn, ok := linkNode.(*merkledag.ProtoNode); ok { + d, err := ft.FSNodeFromBytes(pn.Data()) + if err != nil { + lnk.Err = err + break + } + if settings.ResolveType { + lnk.Type = d.Type() + } + if d.Type() == ft.TFile && settings.ResolveSize { + lnk.Size = d.FileSize() + } + } + } + + return lnk } -func lsFromDir(ctx context.Context, dir uio.Directory) (<-chan ft.LinkResult, error) { +func (api *UnixfsAPI) lsFromLinksAsync(ctx context.Context, dir uio.Directory, settings *options.UnixfsLsSettings) (<-chan coreiface.LsLink, error) { + out := make(chan coreiface.LsLink) + + go func() { + defer close(out) + for l := range dir.EnumLinksAsync(ctx) { + select { + case out <- api.processLink(ctx, l, settings): //TODO: perf: processing can be done in background and in parallel + case <-ctx.Done(): + return + } + } + }() + + return out, nil +} + +func (api *UnixfsAPI) lsFromDir(ctx context.Context, dir uio.Directory, settings *options.UnixfsLsSettings) (<-chan coreiface.LsLink, error) { l, err := dir.Links(ctx) if err != nil { return nil, err } - return lsFromLinks(l) + return api.lsFromLinks(ctx, l, settings) } -func lsFromLinks(ndlinks []*ipld.Link) (<-chan ft.LinkResult, error) { - links := make(chan ft.LinkResult, len(ndlinks)) +func (api *UnixfsAPI) lsFromLinks(ctx context.Context, ndlinks []*ipld.Link, settings *options.UnixfsLsSettings) (<-chan coreiface.LsLink, error) { + links := make(chan coreiface.LsLink, len(ndlinks)) for _, l := range ndlinks { - links <- ft.LinkResult{Link: &ipld.Link{Name: l.Name, Size: l.Size, Cid: l.Cid}} + lr := ft.LinkResult{Link: &ipld.Link{Name: l.Name, Size: l.Size, Cid: l.Cid}} + + links <- api.processLink(ctx, lr, settings) //TODO: can be parallel if settings.Async } close(links) return links, nil