@cryptotaxi247 / kubo / commits / 224d6a3ba

refactor(cmds): do not return errors embedded in result type (#10527)

incl. https://github.com/ipfs/boxo/pull/738

Andrew Gillis committed Dec 3, 2024 at 09:15 UTC 224d6a3ba4fbf690312cb436cf78b4cac3fb7ec6
19 files changed +408 -341
client/rpc/pin.go
+25 -39
@@ -62,10 +62,12 @@ type pinLsObject struct {
62 Type string
63 }
64
65 -func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) (<-chan iface.Pin, error) {
65 +func (api *PinAPI) Ls(ctx context.Context, pins chan<- iface.Pin, opts ...caopts.PinLsOption) error {
66 + defer close(pins)
67 +
68 options, err := caopts.PinLsOptions(opts...)
69 if err != nil {
68 - return nil, err
70 + return err
71 }
72
73 res, err := api.core().Request("pin/ls").
@@ -73,48 +75,32 @@ func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) (<-chan i
75 Option("stream", true).
76 Send(ctx)
77 if err != nil {
76 - return nil, err
78 + return err
79 }
78 -
79 - pins := make(chan iface.Pin)
80 - go func(ch chan<- iface.Pin) {
81 - defer res.Output.Close()
82 - defer close(ch)
83 -
84 - dec := json.NewDecoder(res.Output)
85 - var out pinLsObject
86 - for {
87 - switch err := dec.Decode(&out); err {
88 - case nil:
89 - case io.EOF:
90 - return
91 - default:
92 - select {
93 - case ch <- pin{err: err}:
94 - return
95 - case <-ctx.Done():
96 - return
97 - }
80 + defer res.Output.Close()
81 +
82 + dec := json.NewDecoder(res.Output)
83 + var out pinLsObject
84 + for {
85 + err := dec.Decode(&out)
86 + if err != nil {
87 + if err != io.EOF {
88 + return err
89 }
90 + return nil
91 + }
92
100 - c, err := cid.Parse(out.Cid)
101 - if err != nil {
102 - select {
103 - case ch <- pin{err: err}:
104 - return
105 - case <-ctx.Done():
106 - return
107 - }
108 - }
93 + c, err := cid.Parse(out.Cid)
94 + if err != nil {
95 + return err
96 + }
97
110 - select {
111 - case ch <- pin{typ: out.Type, name: out.Name, path: path.FromCid(c)}:
112 - case <-ctx.Done():
113 - return
114 - }
98 + select {
99 + case pins <- pin{typ: out.Type, name: out.Name, path: path.FromCid(c)}:
100 + case <-ctx.Done():
101 + return ctx.Err()
102 }
116 - }(pins)
117 - return pins, nil
103 + }
104 }
105
106 // IsPinned returns whether or not the given cid is pinned
client/rpc/unixfs.go
+48 -68
@@ -144,10 +144,12 @@ type lsOutput struct {
144 Objects []lsObject
145 }
146
147 -func (api *UnixfsAPI) Ls(ctx context.Context, p path.Path, opts ...caopts.UnixfsLsOption) (<-chan iface.DirEntry, error) {
147 +func (api *UnixfsAPI) Ls(ctx context.Context, p path.Path, out chan<- iface.DirEntry, opts ...caopts.UnixfsLsOption) error {
148 + defer close(out)
149 +
150 options, err := caopts.UnixfsLsOptions(opts...)
151 if err != nil {
150 - return nil, err
152 + return err
153 }
154
155 resp, err := api.core().Request("ls", p.String()).
@@ -156,86 +158,64 @@ func (api *UnixfsAPI) Ls(ctx context.Context, p path.Path, opts ...caopts.Unixfs
158 Option("stream", true).
159 Send(ctx)
160 if err != nil {
159 - return nil, err
161 + return err
162 }
163 if resp.Error != nil {
162 - return nil, resp.Error
164 + return err
165 }
166 + defer resp.Close()
167
168 dec := json.NewDecoder(resp.Output)
166 - out := make(chan iface.DirEntry)
167 -
168 - go func() {
169 - defer resp.Close()
170 - defer close(out)
171 -
172 - for {
173 - var link lsOutput
174 - if err := dec.Decode(&link); err != nil {
175 - if err == io.EOF {
176 - return
177 - }
178 - select {
179 - case out <- iface.DirEntry{Err: err}:
180 - case <-ctx.Done():
181 - }
182 - return
183 - }
169
185 - if len(link.Objects) != 1 {
186 - select {
187 - case out <- iface.DirEntry{Err: errors.New("unexpected Objects len")}:
188 - case <-ctx.Done():
189 - }
190 - return
170 + for {
171 + var link lsOutput
172 + if err = dec.Decode(&link); err != nil {
173 + if err != io.EOF {
174 + return err
175 }
176 + return nil
177 + }
178
193 - if len(link.Objects[0].Links) != 1 {
194 - select {
195 - case out <- iface.DirEntry{Err: errors.New("unexpected Links len")}:
196 - case <-ctx.Done():
197 - }
198 - return
199 - }
179 + if len(link.Objects) != 1 {
180 + return errors.New("unexpected Objects len")
181 + }
182
201 - l0 := link.Objects[0].Links[0]
183 + if len(link.Objects[0].Links) != 1 {
184 + return errors.New("unexpected Links len")
185 + }
186
203 - c, err := cid.Decode(l0.Hash)
204 - if err != nil {
205 - select {
206 - case out <- iface.DirEntry{Err: err}:
207 - case <-ctx.Done():
208 - }
209 - return
210 - }
187 + l0 := link.Objects[0].Links[0]
188
212 - var ftype iface.FileType
213 - switch l0.Type {
214 - case unixfs.TRaw, unixfs.TFile:
215 - ftype = iface.TFile
216 - case unixfs.THAMTShard, unixfs.TDirectory, unixfs.TMetadata:
217 - ftype = iface.TDirectory
218 - case unixfs.TSymlink:
219 - ftype = iface.TSymlink
220 - }
189 + c, err := cid.Decode(l0.Hash)
190 + if err != nil {
191 + return err
192 + }
193
222 - select {
223 - case out <- iface.DirEntry{
224 - Name: l0.Name,
225 - Cid: c,
226 - Size: l0.Size,
227 - Type: ftype,
228 - Target: l0.Target,
229 -
230 - Mode: l0.Mode,
231 - ModTime: l0.ModTime,
232 - }:
233 - case <-ctx.Done():
234 - }
194 + var ftype iface.FileType
195 + switch l0.Type {
196 + case unixfs.TRaw, unixfs.TFile:
197 + ftype = iface.TFile
198 + case unixfs.THAMTShard, unixfs.TDirectory, unixfs.TMetadata:
199 + ftype = iface.TDirectory
200 + case unixfs.TSymlink:
201 + ftype = iface.TSymlink
202 }
236 - }()
203
238 - return out, nil
204 + select {
205 + case out <- iface.DirEntry{
206 + Name: l0.Name,
207 + Cid: c,
208 + Size: l0.Size,
209 + Type: ftype,
210 + Target: l0.Target,
211 +
212 + Mode: l0.Mode,
213 + ModTime: l0.ModTime,
214 + }:
215 + case <-ctx.Done():
216 + return ctx.Err()
217 + }
218 + }
219 }
220
221 func (api *UnixfsAPI) core() *HttpApi {
cmd/ipfs/kubo/pinmfs.go
+1 -1
@@ -183,7 +183,7 @@ func pinMFS(ctx context.Context, node pinMFSNode, cid cid.Cid, svcName string, s
183
184 // check if MFS pin exists (across all possible states) and inspect its CID
185 pinStatuses := []pinclient.Status{pinclient.StatusQueued, pinclient.StatusPinning, pinclient.StatusPinned, pinclient.StatusFailed}
186 - lsPinCh, lsErrCh := c.Ls(ctx, pinclient.PinOpts.FilterName(pinName), pinclient.PinOpts.FilterStatus(pinStatuses...))
186 + lsPinCh, lsErrCh := c.GoLs(ctx, pinclient.PinOpts.FilterName(pinName), pinclient.PinOpts.FilterStatus(pinStatuses...))
187 existingRequestID := "" // is there any pre-existing MFS pin with pinName (for any CID)?
188 pinning := false // is CID for current MFS already being pinned?
189 pinTime := time.Now().UTC()
core/commands/ls.go
+14 -9
@@ -1,6 +1,7 @@
1 package commands
2
3 import (
4 + "context"
5 "fmt"
6 "io"
7 "os"
@@ -133,23 +134,24 @@ The JSON output contains type information.
134 }
135 }
136
137 + lsCtx, cancel := context.WithCancel(req.Context)
138 + defer cancel()
139 +
140 for i, fpath := range paths {
141 pth, err := cmdutils.PathOrCidPath(fpath)
142 if err != nil {
143 return err
144 }
145
142 - results, err := api.Unixfs().Ls(req.Context, pth,
143 - options.Unixfs.ResolveChildren(resolveSize || resolveType))
144 - if err != nil {
145 - return err
146 - }
146 + results := make(chan iface.DirEntry)
147 + lsErr := make(chan error, 1)
148 + go func() {
149 + lsErr <- api.Unixfs().Ls(lsCtx, pth, results,
150 + options.Unixfs.ResolveChildren(resolveSize || resolveType))
151 + }()
152
153 processLink, dirDone = processDir()
154 for link := range results {
150 - if link.Err != nil {
151 - return link.Err
152 - }
155 var ftype unixfs_pb.Data_DataType
156 switch link.Type {
157 case iface.TFile:
@@ -170,10 +172,13 @@ The JSON output contains type information.
172 Mode: link.Mode,
173 ModTime: link.ModTime,
174 }
173 - if err := processLink(paths[i], lsLink); err != nil {
175 + if err = processLink(paths[i], lsLink); err != nil {
176 return err
177 }
178 }
179 + if err = <-lsErr; err != nil {
180 + return err
181 + }
182 dirDone(i)
183 }
184 return done()
core/commands/pin/pin.go
+9 -9
@@ -557,15 +557,16 @@ func pinLsAll(req *cmds.Request, typeStr string, detailed bool, name string, api
557 panic("unhandled pin type")
558 }
559
560 - pins, err := api.Pin().Ls(req.Context, opt, options.Pin.Ls.Detailed(detailed), options.Pin.Ls.Name(name))
561 - if err != nil {
562 - return err
563 - }
560 + pins := make(chan coreiface.Pin)
561 + lsErr := make(chan error, 1)
562 + lsCtx, cancel := context.WithCancel(req.Context)
563 + defer cancel()
564 +
565 + go func() {
566 + lsErr <- api.Pin().Ls(lsCtx, pins, opt, options.Pin.Ls.Detailed(detailed), options.Pin.Ls.Name(name))
567 + }()
568
569 for p := range pins {
566 - if err := p.Err(); err != nil {
567 - return err
568 - }
570 err = emit(PinLsOutputWrapper{
571 PinLsObject: PinLsObject{
572 Type: p.Type(),
@@ -577,8 +578,7 @@ func pinLsAll(req *cmds.Request, typeStr string, detailed bool, name string, api
578 return err
579 }
580 }
580 -
581 - return nil
581 + return <-lsErr
582 }
583
584 const (
core/commands/pin/remotepin.go
+35 -34
@@ -285,26 +285,26 @@ Pass '--status=queued,pinning,pinned,failed' to list pins in all states.
285 cmds.DelimitedStringsOption(",", pinStatusOptionName, "Return pins with the specified statuses (queued,pinning,pinned,failed).").WithDefault([]string{"pinned"}),
286 },
287 Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
288 - ctx, cancel := context.WithCancel(req.Context)
289 - defer cancel()
290 -
288 c, err := getRemotePinServiceFromRequest(req, env)
289 if err != nil {
290 return err
291 }
292
296 - psCh, errCh, err := lsRemote(ctx, req, c)
297 - if err != nil {
298 - return err
299 - }
293 + ctx, cancel := context.WithCancel(req.Context)
294 + defer cancel()
295
296 + psCh := make(chan pinclient.PinStatusGetter)
297 + lsErr := make(chan error, 1)
298 + go func() {
299 + lsErr <- lsRemote(ctx, req, c, psCh)
300 + }()
301 for ps := range psCh {
302 if err := res.Emit(toRemotePinOutput(ps)); err != nil {
303 return err
304 }
305 }
306
307 - return <-errCh
307 + return <-lsErr
308 },
309 Type: RemotePinOutput{},
310 Encoders: cmds.EncoderMap{
@@ -317,7 +317,7 @@ Pass '--status=queued,pinning,pinned,failed' to list pins in all states.
317 }
318
319 // Executes GET /pins/?query-with-filters
320 -func lsRemote(ctx context.Context, req *cmds.Request, c *pinclient.Client) (chan pinclient.PinStatusGetter, chan error, error) {
320 +func lsRemote(ctx context.Context, req *cmds.Request, c *pinclient.Client, out chan<- pinclient.PinStatusGetter) error {
321 opts := []pinclient.LsOption{}
322 if name, nameFound := req.Options[pinNameOptionName]; nameFound {
323 nameStr := name.(string)
@@ -330,7 +330,8 @@ func lsRemote(ctx context.Context, req *cmds.Request, c *pinclient.Client) (chan
330 for _, rawCID := range cidsRawArr {
331 parsedCID, err := cid.Decode(rawCID)
332 if err != nil {
333 - return nil, nil, fmt.Errorf("CID %q cannot be parsed: %v", rawCID, err)
333 + close(out)
334 + return fmt.Errorf("CID %q cannot be parsed: %v", rawCID, err)
335 }
336 parsedCIDs = append(parsedCIDs, parsedCID)
337 }
@@ -342,16 +343,15 @@ func lsRemote(ctx context.Context, req *cmds.Request, c *pinclient.Client) (chan
343 for _, rawStatus := range statusRawArr {
344 s := pinclient.Status(rawStatus)
345 if s.String() == string(pinclient.StatusUnknown) {
345 - return nil, nil, fmt.Errorf("status %q is not valid", rawStatus)
346 + close(out)
347 + return fmt.Errorf("status %q is not valid", rawStatus)
348 }
349 parsedStatuses = append(parsedStatuses, s)
350 }
351 opts = append(opts, pinclient.PinOpts.FilterStatus(parsedStatuses...))
352 }
353
352 - psCh, errCh := c.Ls(ctx, opts...)
353 -
354 - return psCh, errCh, nil
354 + return c.Ls(ctx, out, opts...)
355 }
356
357 var rmRemotePinCmd = &cmds.Command{
@@ -393,36 +393,37 @@ To list and then remove all pending pin requests, pass an explicit status list:
393 cmds.BoolOption(pinForceOptionName, "Allow removal of multiple pins matching the query without additional confirmation.").WithDefault(false),
394 },
395 Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
396 - ctx, cancel := context.WithCancel(req.Context)
397 - defer cancel()
398 -
396 c, err := getRemotePinServiceFromRequest(req, env)
397 if err != nil {
398 return err
399 }
400
401 rmIDs := []string{}
405 - if len(req.Arguments) == 0 {
406 - psCh, errCh, err := lsRemote(ctx, req, c)
407 - if err != nil {
408 - return err
409 - }
410 - for ps := range psCh {
411 - rmIDs = append(rmIDs, ps.GetRequestId())
412 - }
413 - if err = <-errCh; err != nil {
414 - return fmt.Errorf("error while listing remote pins: %v", err)
415 - }
416 -
417 - if len(rmIDs) > 1 && !req.Options[pinForceOptionName].(bool) {
418 - return fmt.Errorf("multiple remote pins are matching this query, add --force to confirm the bulk removal")
419 - }
420 - } else {
402 + if len(req.Arguments) != 0 {
403 return fmt.Errorf("unexpected argument %q", req.Arguments[0])
404 }
405
406 + psCh := make(chan pinclient.PinStatusGetter)
407 + errCh := make(chan error, 1)
408 + ctx, cancel := context.WithCancel(req.Context)
409 + defer cancel()
410 +
411 + go func() {
412 + errCh <- lsRemote(ctx, req, c, psCh)
413 + }()
414 + for ps := range psCh {
415 + rmIDs = append(rmIDs, ps.GetRequestId())
416 + }
417 + if err = <-errCh; err != nil {
418 + return fmt.Errorf("error while listing remote pins: %v", err)
419 + }
420 +
421 + if len(rmIDs) > 1 && !req.Options[pinForceOptionName].(bool) {
422 + return fmt.Errorf("multiple remote pins are matching this query, add --force to confirm the bulk removal")
423 + }
424 +
425 for _, rmID := range rmIDs {
425 - if err := c.DeleteByID(ctx, rmID); err != nil {
426 + if err = c.DeleteByID(ctx, rmID); err != nil {
427 return fmt.Errorf("removing pin identified by requestid=%q failed: %v", rmID, err)
428 }
429 }
core/coreapi/pin.go
+71 -82
@@ -51,13 +51,14 @@ func (api *PinAPI) Add(ctx context.Context, p path.Path, opts ...caopts.PinAddOp
51 return api.pinning.Flush(ctx)
52 }
53
54 -func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) (<-chan coreiface.Pin, error) {
54 +func (api *PinAPI) Ls(ctx context.Context, pins chan<- coreiface.Pin, opts ...caopts.PinLsOption) error {
55 ctx, span := tracing.Span(ctx, "CoreAPI.PinAPI", "Ls")
56 defer span.End()
57
58 settings, err := caopts.PinLsOptions(opts...)
59 if err != nil {
60 - return nil, err
60 + close(pins)
61 + return err
62 }
63
64 span.SetAttributes(attribute.String("type", settings.Type))
@@ -65,10 +66,11 @@ func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) (<-chan c
66 switch settings.Type {
67 case "all", "direct", "indirect", "recursive":
68 default:
68 - return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", settings.Type)
69 + close(pins)
70 + return fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", settings.Type)
71 }
72
71 - return api.pinLsAll(ctx, settings.Type, settings.Detailed, settings.Name), nil
73 + return api.pinLsAll(ctx, settings.Type, settings.Detailed, settings.Name, pins)
74 }
75
76 func (api *PinAPI) IsPinned(ctx context.Context, p path.Path, opts ...caopts.PinIsPinnedOption) (string, bool, error) {
@@ -230,6 +232,7 @@ func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, erro
232 }
233
234 out := make(chan coreiface.PinStatus)
235 +
236 go func() {
237 defer close(out)
238 for p := range api.pinning.RecursiveKeys(ctx, false) {
@@ -254,7 +257,6 @@ type pinInfo struct {
257 pinType string
258 path path.ImmutablePath
259 name string
257 - err error
260 }
261
262 func (p *pinInfo) Path() path.ImmutablePath {
@@ -269,17 +271,12 @@ func (p *pinInfo) Name() string {
271 return p.name
272 }
273
272 -func (p *pinInfo) Err() error {
273 - return p.err
274 -}
275 -
274 // pinLsAll is an internal function for returning a list of pins
275 //
276 // The caller must keep reading results until the channel is closed to prevent
277 // leaking the goroutine that is fetching pins.
280 -func (api *PinAPI) pinLsAll(ctx context.Context, typeStr string, detailed bool, name string) <-chan coreiface.Pin {
281 - out := make(chan coreiface.Pin, 1)
282 -
278 +func (api *PinAPI) pinLsAll(ctx context.Context, typeStr string, detailed bool, name string, out chan<- coreiface.Pin) error {
279 + defer close(out)
280 emittedSet := cid.NewSet()
281
282 AddToResultKeys := func(c cid.Cid, pinName, typeStr string) error {
@@ -297,87 +294,79 @@ func (api *PinAPI) pinLsAll(ctx context.Context, typeStr string, detailed bool,
294 return nil
295 }
296
300 - go func() {
301 - defer close(out)
302 -
303 - var rkeys []cid.Cid
304 - var err error
305 - if typeStr == "recursive" || typeStr == "all" {
306 - for streamedCid := range api.pinning.RecursiveKeys(ctx, detailed) {
307 - if streamedCid.Err != nil {
308 - out <- &pinInfo{err: streamedCid.Err}
309 - return
310 - }
311 - if err = AddToResultKeys(streamedCid.Pin.Key, streamedCid.Pin.Name, "recursive"); err != nil {
312 - out <- &pinInfo{err: err}
313 - return
314 - }
315 - rkeys = append(rkeys, streamedCid.Pin.Key)
297 + var rkeys []cid.Cid
298 + var err error
299 + if typeStr == "recursive" || typeStr == "all" {
300 + for streamedCid := range api.pinning.RecursiveKeys(ctx, detailed) {
301 + if streamedCid.Err != nil {
302 + return streamedCid.Err
303 }
304 + if err = AddToResultKeys(streamedCid.Pin.Key, streamedCid.Pin.Name, "recursive"); err != nil {
305 + return err
306 + }
307 + rkeys = append(rkeys, streamedCid.Pin.Key)
308 }
318 - if typeStr == "direct" || typeStr == "all" {
319 - for streamedCid := range api.pinning.DirectKeys(ctx, detailed) {
320 - if streamedCid.Err != nil {
321 - out <- &pinInfo{err: streamedCid.Err}
322 - return
323 - }
324 - if err = AddToResultKeys(streamedCid.Pin.Key, streamedCid.Pin.Name, "direct"); err != nil {
325 - out <- &pinInfo{err: err}
326 - return
327 - }
309 + }
310 + if typeStr == "direct" || typeStr == "all" {
311 + for streamedCid := range api.pinning.DirectKeys(ctx, detailed) {
312 + if streamedCid.Err != nil {
313 + return streamedCid.Err
314 + }
315 + if err = AddToResultKeys(streamedCid.Pin.Key, streamedCid.Pin.Name, "direct"); err != nil {
316 + return err
317 }
318 }
330 - if typeStr == "indirect" {
331 - // We need to first visit the direct pins that have priority
332 - // without emitting them
333 -
334 - for streamedCid := range api.pinning.DirectKeys(ctx, detailed) {
335 - if streamedCid.Err != nil {
336 - out <- &pinInfo{err: streamedCid.Err}
337 - return
338 - }
339 - emittedSet.Add(streamedCid.Pin.Key)
319 + }
320 + if typeStr == "indirect" {
321 + // We need to first visit the direct pins that have priority
322 + // without emitting them
323 +
324 + for streamedCid := range api.pinning.DirectKeys(ctx, detailed) {
325 + if streamedCid.Err != nil {
326 + return streamedCid.Err
327 }
328 + emittedSet.Add(streamedCid.Pin.Key)
329 + }
330
342 - for streamedCid := range api.pinning.RecursiveKeys(ctx, detailed) {
343 - if streamedCid.Err != nil {
344 - out <- &pinInfo{err: streamedCid.Err}
345 - return
346 - }
347 - emittedSet.Add(streamedCid.Pin.Key)
348 - rkeys = append(rkeys, streamedCid.Pin.Key)
331 + for streamedCid := range api.pinning.RecursiveKeys(ctx, detailed) {
332 + if streamedCid.Err != nil {
333 + return streamedCid.Err
334 }
335 + emittedSet.Add(streamedCid.Pin.Key)
336 + rkeys = append(rkeys, streamedCid.Pin.Key)
337 }
351 - if typeStr == "indirect" || typeStr == "all" {
352 - walkingSet := cid.NewSet()
353 - for _, k := range rkeys {
354 - err = merkledag.Walk(
355 - ctx, merkledag.GetLinksWithDAG(api.dag), k,
356 - func(c cid.Cid) bool {
357 - if !walkingSet.Visit(c) {
358 - return false
359 - }
360 - if emittedSet.Has(c) {
361 - return true // skipped
362 - }
363 - err := AddToResultKeys(c, "", "indirect")
364 - if err != nil {
365 - out <- &pinInfo{err: err}
366 - return false
367 - }
368 - return true
369 - },
370 - merkledag.SkipRoot(), merkledag.Concurrent(),
371 - )
372 - if err != nil {
373 - out <- &pinInfo{err: err}
374 - return
375 - }
338 + }
339 + if typeStr == "indirect" || typeStr == "all" {
340 + if len(rkeys) == 0 {
341 + return nil
342 + }
343 + var addErr error
344 + walkingSet := cid.NewSet()
345 + for _, k := range rkeys {
346 + err = merkledag.Walk(
347 + ctx, merkledag.GetLinksWithDAG(api.dag), k,
348 + func(c cid.Cid) bool {
349 + if !walkingSet.Visit(c) {
350 + return false
351 + }
352 + if emittedSet.Has(c) {
353 + return true // skipped
354 + }
355 + addErr = AddToResultKeys(c, "", "indirect")
356 + return addErr == nil
357 + },
358 + merkledag.SkipRoot(), merkledag.Concurrent(),
359 + )
360 + if err != nil {
361 + return err
362 + }
363 + if addErr != nil {
364 + return addErr
365 }
366 }
378 - }()
367 + }
368
380 - return out
369 + return nil
370 }
371
372 func (api *PinAPI) core() coreiface.CoreAPI {
core/coreapi/unixfs.go
+47 -31
@@ -2,6 +2,7 @@ package coreapi
2
3 import (
4 "context"
5 + "errors"
6 "fmt"
7
8 blockservice "github.com/ipfs/boxo/blockservice"
@@ -197,13 +198,15 @@ func (api *UnixfsAPI) Get(ctx context.Context, p path.Path) (files.Node, error)
198
199 // Ls returns the contents of an IPFS or IPNS object(s) at path p, with the format:
200 // `<link base58 hash> <link size in bytes> <link name>`
200 -func (api *UnixfsAPI) Ls(ctx context.Context, p path.Path, opts ...options.UnixfsLsOption) (<-chan coreiface.DirEntry, error) {
201 +func (api *UnixfsAPI) Ls(ctx context.Context, p path.Path, out chan<- coreiface.DirEntry, opts ...options.UnixfsLsOption) error {
202 ctx, span := tracing.Span(ctx, "CoreAPI.UnixfsAPI", "Ls", trace.WithAttributes(attribute.String("path", p.String())))
203 defer span.End()
204
205 + defer close(out)
206 +
207 settings, err := options.UnixfsLsOptions(opts...)
208 if err != nil {
206 - return nil, err
209 + return err
210 }
211
212 span.SetAttributes(attribute.Bool("resolvechildren", settings.ResolveChildren))
@@ -213,21 +216,21 @@ func (api *UnixfsAPI) Ls(ctx context.Context, p path.Path, opts ...options.Unixf
216
217 dagnode, err := ses.ResolveNode(ctx, p)
218 if err != nil {
216 - return nil, err
219 + return err
220 }
221
222 dir, err := uio.NewDirectoryFromNode(ses.dag, dagnode)
220 - if err == uio.ErrNotADir {
221 - return uses.lsFromLinks(ctx, dagnode.Links(), settings)
222 - }
223 if err != nil {
224 - return nil, err
224 + if errors.Is(err, uio.ErrNotADir) {
225 + return uses.lsFromLinks(ctx, dagnode.Links(), settings, out)
226 + }
227 + return err
228 }
229
227 - return uses.lsFromLinksAsync(ctx, dir, settings)
230 + return uses.lsFromDirLinks(ctx, dir, settings, out)
231 }
232
230 -func (api *UnixfsAPI) processLink(ctx context.Context, linkres ft.LinkResult, settings *options.UnixfsLsSettings) coreiface.DirEntry {
233 +func (api *UnixfsAPI) processLink(ctx context.Context, linkres ft.LinkResult, settings *options.UnixfsLsSettings) (coreiface.DirEntry, error) {
234 ctx, span := tracing.Span(ctx, "CoreAPI.UnixfsAPI", "ProcessLink")
235 defer span.End()
236 if linkres.Link != nil {
@@ -235,7 +238,7 @@ func (api *UnixfsAPI) processLink(ctx context.Context, linkres ft.LinkResult, se
238 }
239
240 if linkres.Err != nil {
238 - return coreiface.DirEntry{Err: linkres.Err}
241 + return coreiface.DirEntry{}, linkres.Err
242 }
243
244 lnk := coreiface.DirEntry{
@@ -252,15 +255,13 @@ func (api *UnixfsAPI) processLink(ctx context.Context, linkres ft.LinkResult, se
255 if settings.ResolveChildren {
256 linkNode, err := linkres.Link.GetNode(ctx, api.dag)
257 if err != nil {
255 - lnk.Err = err
256 - break
258 + return coreiface.DirEntry{}, err
259 }
260
261 if pn, ok := linkNode.(*merkledag.ProtoNode); ok {
262 d, err := ft.FSNodeFromBytes(pn.Data())
263 if err != nil {
262 - lnk.Err = err
263 - break
264 + return coreiface.DirEntry{}, err
265 }
266 switch d.Type() {
267 case ft.TFile, ft.TRaw:
@@ -284,35 +285,50 @@ func (api *UnixfsAPI) processLink(ctx context.Context, linkres ft.LinkResult, se
285 }
286 }
287
287 - return lnk
288 + return lnk, nil
289 }
290
290 -func (api *UnixfsAPI) lsFromLinksAsync(ctx context.Context, dir uio.Directory, settings *options.UnixfsLsSettings) (<-chan coreiface.DirEntry, error) {
291 - out := make(chan coreiface.DirEntry, uio.DefaultShardWidth)
291 +func (api *UnixfsAPI) lsFromDirLinks(ctx context.Context, dir uio.Directory, settings *options.UnixfsLsSettings, out chan<- coreiface.DirEntry) error {
292 + for l := range dir.EnumLinksAsync(ctx) {
293 + dirEnt, err := api.processLink(ctx, l, settings) // TODO: perf: processing can be done in background and in parallel
294 + if err != nil {
295 + return err
296 + }
297 + select {
298 + case out <- dirEnt:
299 + case <-ctx.Done():
300 + return nil
301 + }
302 + }
303 + return nil
304 +}
305
306 +func (api *UnixfsAPI) lsFromLinks(ctx context.Context, ndlinks []*ipld.Link, settings *options.UnixfsLsSettings, out chan<- coreiface.DirEntry) error {
307 + // Create links channel large enough to not block when writing to out is slower.
308 + links := make(chan coreiface.DirEntry, len(ndlinks))
309 + errs := make(chan error, 1)
310 go func() {
294 - defer close(out)
295 - for l := range dir.EnumLinksAsync(ctx) {
311 + defer close(links)
312 + defer close(errs)
313 + for _, l := range ndlinks {
314 + lr := ft.LinkResult{Link: &ipld.Link{Name: l.Name, Size: l.Size, Cid: l.Cid}}
315 + lnk, err := api.processLink(ctx, lr, settings) // TODO: can be parallel if settings.Async
316 + if err != nil {
317 + errs <- err
318 + return
319 + }
320 select {
297 - case out <- api.processLink(ctx, l, settings): // TODO: perf: processing can be done in background and in parallel
321 + case links <- lnk:
322 case <-ctx.Done():
323 return
324 }
325 }
326 }()
327
304 - return out, nil
305 -}
306 -
307 -func (api *UnixfsAPI) lsFromLinks(ctx context.Context, ndlinks []*ipld.Link, settings *options.UnixfsLsSettings) (<-chan coreiface.DirEntry, error) {
308 - links := make(chan coreiface.DirEntry, len(ndlinks))
309 - for _, l := range ndlinks {
310 - lr := ft.LinkResult{Link: &ipld.Link{Name: l.Name, Size: l.Size, Cid: l.Cid}}
311 -
312 - links <- api.processLink(ctx, lr, settings) // TODO: can be parallel if settings.Async
328 + for lnk := range links {
329 + out <- lnk
330 }
314 - close(links)
315 - return links, nil
331 + return <-errs
332 }
333
334 func (api *UnixfsAPI) core() *CoreAPI {
core/coreiface/pin.go
+3 -5
@@ -18,9 +18,6 @@ type Pin interface {
18
19 // Type of the pin
20 Type() string
21 -
22 - // if not nil, an error happened. Everything else should be ignored.
23 - Err() error
21 }
22
23 // PinStatus holds information about pin health
@@ -50,8 +47,9 @@ type PinAPI interface {
47 // tree
48 Add(context.Context, path.Path, ...options.PinAddOption) error
49
53 - // Ls returns list of pinned objects on this node
54 - Ls(context.Context, ...options.PinLsOption) (<-chan Pin, error)
50 + // Ls returns this node's pinned objects on the provided channel. The
51 + // channel is closed when there are no more pins and an error is returned.
52 + Ls(context.Context, chan<- Pin, ...options.PinLsOption) error
53
54 // IsPinned returns whether or not the given cid is pinned
55 // and an explanation of why its pinned
core/coreiface/tests/block.go
+10 -2
@@ -323,9 +323,17 @@ func (tp *TestSuite) TestBlockPin(t *testing.T) {
323 t.Fatal(err)
324 }
325
326 - if pins, err := api.Pin().Ls(ctx); err != nil || len(pins) != 0 {
326 + pinCh := make(chan coreiface.Pin)
327 + go func() {
328 + err = api.Pin().Ls(ctx, pinCh)
329 + }()
330 +
331 + for range pinCh {
332 t.Fatal("expected 0 pins")
333 }
334 + if err != nil {
335 + t.Fatal(err)
336 + }
337
338 res, err := api.Block().Put(
339 ctx,
@@ -337,7 +345,7 @@ func (tp *TestSuite) TestBlockPin(t *testing.T) {
345 t.Fatal(err)
346 }
347
340 - pins, err := accPins(api.Pin().Ls(ctx))
348 + pins, err := accPins(ctx, api)
349 if err != nil {
350 t.Fatal(err)
351 }
core/coreiface/tests/pin.go
+23 -23
@@ -67,7 +67,7 @@ func (tp *TestSuite) TestPinSimple(t *testing.T) {
67 t.Fatal(err)
68 }
69
70 - list, err := accPins(api.Pin().Ls(ctx))
70 + list, err := accPins(ctx, api)
71 if err != nil {
72 t.Fatal(err)
73 }
@@ -91,7 +91,7 @@ func (tp *TestSuite) TestPinSimple(t *testing.T) {
91 t.Fatal(err)
92 }
93
94 - list, err = accPins(api.Pin().Ls(ctx))
94 + list, err = accPins(ctx, api)
95 if err != nil {
96 t.Fatal(err)
97 }
@@ -143,7 +143,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
143 t.Fatal(err)
144 }
145
146 - list, err := accPins(api.Pin().Ls(ctx))
146 + list, err := accPins(ctx, api)
147 if err != nil {
148 t.Fatal(err)
149 }
@@ -152,7 +152,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
152 t.Errorf("unexpected pin list len: %d", len(list))
153 }
154
155 - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Direct()))
155 + list, err = accPins(ctx, api, opt.Pin.Ls.Direct())
156 if err != nil {
157 t.Fatal(err)
158 }
@@ -165,7 +165,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
165 t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.FromCid(nd3.Cid()).String())
166 }
167
168 - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Recursive()))
168 + list, err = accPins(ctx, api, opt.Pin.Ls.Recursive())
169 if err != nil {
170 t.Fatal(err)
171 }
@@ -178,7 +178,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
178 t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.FromCid(nd2.Cid()).String())
179 }
180
181 - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Indirect()))
181 + list, err = accPins(ctx, api, opt.Pin.Ls.Indirect())
182 if err != nil {
183 t.Fatal(err)
184 }
@@ -436,21 +436,21 @@ func getThreeChainedNodes(t *testing.T, ctx context.Context, api iface.CoreAPI,
436 func assertPinTypes(t *testing.T, ctx context.Context, api iface.CoreAPI, recusive, direct, indirect []cidContainer) {
437 assertPinLsAllConsistency(t, ctx, api)
438
439 - list, err := accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Recursive()))
439 + list, err := accPins(ctx, api, opt.Pin.Ls.Recursive())
440 if err != nil {
441 t.Fatal(err)
442 }
443
444 assertPinCids(t, list, recusive...)
445
446 - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Direct()))
446 + list, err = accPins(ctx, api, opt.Pin.Ls.Direct())
447 if err != nil {
448 t.Fatal(err)
449 }
450
451 assertPinCids(t, list, direct...)
452
453 - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Indirect()))
453 + list, err = accPins(ctx, api, opt.Pin.Ls.Indirect())
454 if err != nil {
455 t.Fatal(err)
456 }
@@ -500,7 +500,7 @@ func assertPinCids(t *testing.T, pins []iface.Pin, cids ...cidContainer) {
500 // assertPinLsAllConsistency verifies that listing all pins gives the same result as listing the pin types individually
501 func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.CoreAPI) {
502 t.Helper()
503 - allPins, err := accPins(api.Pin().Ls(ctx))
503 + allPins, err := accPins(ctx, api)
504 if err != nil {
505 t.Fatal(err)
506 }
@@ -531,7 +531,7 @@ func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.Core
531 }
532
533 for typeStr, pinProps := range typeMap {
534 - pins, err := accPins(api.Pin().Ls(ctx, pinProps.PinLsOption))
534 + pins, err := accPins(ctx, api, pinProps.PinLsOption)
535 if err != nil {
536 t.Fatal(err)
537 }
@@ -593,19 +593,19 @@ func assertNotPinned(t *testing.T, ctx context.Context, api iface.CoreAPI, p pat
593 }
594 }
595
596 -func accPins(pins <-chan iface.Pin, err error) ([]iface.Pin, error) {
597 - if err != nil {
598 - return nil, err
599 - }
600 -
601 - var result []iface.Pin
596 +func accPins(ctx context.Context, api iface.CoreAPI, opts ...opt.PinLsOption) ([]iface.Pin, error) {
597 + var err error
598 + pins := make(chan iface.Pin)
599 + go func() {
600 + err = api.Pin().Ls(ctx, pins, opts...)
601 + }()
602
603 + var results []iface.Pin
604 for pin := range pins {
604 - if pin.Err() != nil {
605 - return nil, pin.Err()
606 - }
607 - result = append(result, pin)
605 + results = append(results, pin)
606 }
609 -
610 - return result, nil
607 + if err != nil {
608 + return nil, err
609 + }
610 + return results, nil
611 }
core/coreiface/tests/unixfs.go
+44 -24
@@ -544,7 +544,7 @@ func (tp *TestSuite) TestAddPinned(t *testing.T) {
544 t.Fatal(err)
545 }
546
547 - pins, err := accPins(api.Pin().Ls(ctx))
547 + pins, err := accPins(ctx, api)
548 if err != nil {
549 t.Fatal(err)
550 }
@@ -681,14 +681,15 @@ func (tp *TestSuite) TestLs(t *testing.T) {
681 t.Fatal(err)
682 }
683
684 - entries, err := api.Unixfs().Ls(ctx, p)
685 - if err != nil {
686 - t.Fatal(err)
687 - }
684 + errCh := make(chan error, 1)
685 + entries := make(chan coreiface.DirEntry)
686 + go func() {
687 + errCh <- api.Unixfs().Ls(ctx, p, entries)
688 + }()
689
689 - entry := <-entries
690 - if entry.Err != nil {
691 - t.Fatal(entry.Err)
690 + entry, ok := <-entries
691 + if !ok {
692 + t.Fatal("expected another entry")
693 }
694 if entry.Size != 15 {
695 t.Errorf("expected size = 15, got %d", entry.Size)
@@ -702,9 +703,9 @@ func (tp *TestSuite) TestLs(t *testing.T) {
703 if entry.Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" {
704 t.Errorf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", entry.Cid)
705 }
705 - entry = <-entries
706 - if entry.Err != nil {
707 - t.Fatal(entry.Err)
706 + entry, ok = <-entries
707 + if !ok {
708 + t.Fatal("expected another entry")
709 }
710 if entry.Type != coreiface.TSymlink {
711 t.Errorf("wrong type %s", entry.Type)
@@ -716,11 +717,12 @@ func (tp *TestSuite) TestLs(t *testing.T) {
717 t.Errorf("expected symlink target to be /foo/bar, got %s", entry.Target)
718 }
719
719 - if l, ok := <-entries; ok {
720 - t.Errorf("didn't expect a second link")
721 - if l.Err != nil {
722 - t.Error(l.Err)
723 - }
720 + _, ok = <-entries
721 + if ok {
722 + t.Errorf("didn't expect a another link")
723 + }
724 + if err = <-errCh; err != nil {
725 + t.Error(err)
726 }
727 }
728
@@ -779,13 +781,22 @@ func (tp *TestSuite) TestLsEmptyDir(t *testing.T) {
781 t.Fatal(err)
782 }
783
782 - links, err := api.Unixfs().Ls(ctx, p)
783 - if err != nil {
784 + errCh := make(chan error, 1)
785 + links := make(chan coreiface.DirEntry)
786 + go func() {
787 + errCh <- api.Unixfs().Ls(ctx, p, links)
788 + }()
789 +
790 + var count int
791 + for range links {
792 + count++
793 + }
794 + if err = <-errCh; err != nil {
795 t.Fatal(err)
796 }
797
787 - if len(links) != 0 {
788 - t.Fatalf("expected 0 links, got %d", len(links))
798 + if count != 0 {
799 + t.Fatalf("expected 0 links, got %d", count)
800 }
801 }
802
@@ -808,13 +819,22 @@ func (tp *TestSuite) TestLsNonUnixfs(t *testing.T) {
819 t.Fatal(err)
820 }
821
811 - links, err := api.Unixfs().Ls(ctx, path.FromCid(nd.Cid()))
812 - if err != nil {
822 + errCh := make(chan error, 1)
823 + links := make(chan coreiface.DirEntry)
824 + go func() {
825 + errCh <- api.Unixfs().Ls(ctx, path.FromCid(nd.Cid()), links)
826 + }()
827 +
828 + var count int
829 + for range links {
830 + count++
831 + }
832 + if err = <-errCh; err != nil {
833 t.Fatal(err)
834 }
835
816 - if len(links) != 0 {
817 - t.Fatalf("expected 0 links, got %d", len(links))
836 + if count != 0 {
837 + t.Fatalf("expected 0 links, got %d", count)
838 }
839 }
840
core/coreiface/unixfs.go
+53 -5
@@ -2,6 +2,7 @@ package iface
2
3 import (
4 "context"
5 + "iter"
6 "os"
7 "time"
8
@@ -63,8 +64,6 @@ type DirEntry struct {
64
65 Mode os.FileMode
66 ModTime time.Time
66 -
67 - Err error
67 }
68
69 // UnixfsAPI is the basic interface to immutable files in IPFS
@@ -81,7 +80,56 @@ type UnixfsAPI interface {
80 // to operations performed on the returned file
81 Get(context.Context, path.Path) (files.Node, error)
82
84 - // Ls returns the list of links in a directory. Links aren't guaranteed to be
85 - // returned in order
86 - Ls(context.Context, path.Path, ...options.UnixfsLsOption) (<-chan DirEntry, error)
83 + // Ls writes the links in a directory to the DirEntry channel. Links aren't
84 + // guaranteed to be returned in order. If an error occurs or the context is
85 + // canceled, the DirEntry channel is closed and an error is returned.
86 + //
87 + // Example:
88 + //
89 + // dirs := make(chan DirEntry)
90 + // lsErr := make(chan error, 1)
91 + // go func() {
92 + // lsErr <- Ls(ctx, p, dirs)
93 + // }()
94 + // for dirEnt := range dirs {
95 + // fmt.Println("Dir name:", dirEnt.Name)
96 + // }
97 + // err := <-lsErr
98 + // if err != nil {
99 + // return fmt.Errorf("error listing directory: %w", err)
100 + // }
101 + Ls(context.Context, path.Path, chan<- DirEntry, ...options.UnixfsLsOption) error
102 +}
103 +
104 +// LsIter returns a go iterator that allows ranging over DirEntry results.
105 +// Iteration stops if the context is canceled or if the iterator yields an
106 +// error.
107 +//
108 +// Exmaple:
109 +//
110 +// for dirEnt, err := LsIter(ctx, ufsAPI, p) {
111 +// if err != nil {
112 +// return fmt.Errorf("error listing directory: %w", err)
113 +// }
114 +// fmt.Println("Dir name:", dirEnt.Name)
115 +// }
116 +func LsIter(ctx context.Context, api UnixfsAPI, p path.Path, opts ...options.UnixfsLsOption) iter.Seq2[DirEntry, error] {
117 + return func(yield func(DirEntry, error) bool) {
118 + ctx, cancel := context.WithCancel(ctx)
119 + defer cancel() // cancel Ls if done iterating early
120 +
121 + dirs := make(chan DirEntry)
122 + lsErr := make(chan error, 1)
123 + go func() {
124 + lsErr <- api.Ls(ctx, p, dirs, opts...)
125 + }()
126 + for dirEnt := range dirs {
127 + if !yield(dirEnt, nil) {
128 + return
129 + }
130 + }
131 + if err := <-lsErr; err != nil {
132 + yield(DirEntry{}, err)
133 + }
134 + }
135 }
docs/examples/kubo-as-a-library/go.mod
+3 -1
@@ -7,7 +7,7 @@ go 1.23
7 replace github.com/ipfs/kubo => ./../../..
8
9 require (
10 - github.com/ipfs/boxo v0.24.4-0.20241125210908-37756ce2eeb1
10 + github.com/ipfs/boxo v0.24.4-0.20241203185533-3a3e8afa3492
11 github.com/ipfs/kubo v0.0.0-00010101000000-000000000000
12 github.com/libp2p/go-libp2p v0.37.2
13 github.com/multiformats/go-multiaddr v0.13.0
@@ -52,6 +52,8 @@ require (
52 github.com/francoispqt/gojay v1.2.13 // indirect
53 github.com/fsnotify/fsnotify v1.7.0 // indirect
54 github.com/gabriel-vasile/mimetype v1.4.6 // indirect
55 + github.com/gammazero/chanqueue v1.0.0 // indirect
56 + github.com/gammazero/deque v1.0.0 // indirect
57 github.com/getsentry/sentry-go v0.27.0 // indirect
58 github.com/go-jose/go-jose/v4 v4.0.4 // indirect
59 github.com/go-logr/logr v1.4.2 // indirect
docs/examples/kubo-as-a-library/go.sum
+6 -2
@@ -164,6 +164,10 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
164 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
165 github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
166 github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
167 +github.com/gammazero/chanqueue v1.0.0 h1:FER/sMailGFA3DDvFooEkipAMU+3c9Bg3bheloPSz6o=
168 +github.com/gammazero/chanqueue v1.0.0/go.mod h1:fMwpwEiuUgpab0sH4VHiVcEoji1pSi+EIzeG4TPeKPc=
169 +github.com/gammazero/deque v1.0.0 h1:LTmimT8H7bXkkCy6gZX7zNLtkbz4NdS2z8LZuor3j34=
170 +github.com/gammazero/deque v1.0.0/go.mod h1:iflpYvtGfM3U8S8j+sZEKIak3SAKYpA5/SQewgfXDKo=
171 github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
172 github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
173 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -298,8 +302,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c h1:7Uy
302 github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c/go.mod h1:6EekK/jo+TynwSE/ZOiOJd4eEvRXoavEC3vquKtv4yI=
303 github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
304 github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
301 -github.com/ipfs/boxo v0.24.4-0.20241125210908-37756ce2eeb1 h1:Ox1qTlON8qG46rUL7dDEwnIt7W9MhaidtvR/97RywWw=
302 -github.com/ipfs/boxo v0.24.4-0.20241125210908-37756ce2eeb1/go.mod h1:Kxk43F+avGAsJSwhJW4isNYrpGwXHRJCvJ19Pt+MQc4=
305 +github.com/ipfs/boxo v0.24.4-0.20241203185533-3a3e8afa3492 h1:kiS5+H+6aJeNWWDynuYu/ijgzkBTrInl++VFcNDgq+g=
306 +github.com/ipfs/boxo v0.24.4-0.20241203185533-3a3e8afa3492/go.mod h1:lAoydO+oJhB1e7pUn4ju1Z1fuUIwy+zb0hQXRb/bu2g=
307 github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
308 github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
309 github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ=
go.mod
+3 -1
@@ -22,7 +22,7 @@ require (
22 github.com/hashicorp/go-version v1.7.0
23 github.com/ipfs-shipyard/nopfs v0.0.12
24 github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c
25 - github.com/ipfs/boxo v0.24.4-0.20241125210908-37756ce2eeb1
25 + github.com/ipfs/boxo v0.24.4-0.20241203185533-3a3e8afa3492
26 github.com/ipfs/go-block-format v0.2.0
27 github.com/ipfs/go-cid v0.4.1
28 github.com/ipfs/go-cidutil v0.1.0
@@ -125,6 +125,8 @@ require (
125 github.com/flynn/noise v1.1.0 // indirect
126 github.com/francoispqt/gojay v1.2.13 // indirect
127 github.com/gabriel-vasile/mimetype v1.4.6 // indirect
128 + github.com/gammazero/chanqueue v1.0.0 // indirect
129 + github.com/gammazero/deque v1.0.0 // indirect
130 github.com/getsentry/sentry-go v0.27.0 // indirect
131 github.com/go-jose/go-jose/v4 v4.0.4 // indirect
132 github.com/go-kit/log v0.2.1 // indirect
go.sum
+6 -2
@@ -198,6 +198,10 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
198 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
199 github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
200 github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
201 +github.com/gammazero/chanqueue v1.0.0 h1:FER/sMailGFA3DDvFooEkipAMU+3c9Bg3bheloPSz6o=
202 +github.com/gammazero/chanqueue v1.0.0/go.mod h1:fMwpwEiuUgpab0sH4VHiVcEoji1pSi+EIzeG4TPeKPc=
203 +github.com/gammazero/deque v1.0.0 h1:LTmimT8H7bXkkCy6gZX7zNLtkbz4NdS2z8LZuor3j34=
204 +github.com/gammazero/deque v1.0.0/go.mod h1:iflpYvtGfM3U8S8j+sZEKIak3SAKYpA5/SQewgfXDKo=
205 github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
206 github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
207 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -362,8 +366,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c h1:7Uy
366 github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c/go.mod h1:6EekK/jo+TynwSE/ZOiOJd4eEvRXoavEC3vquKtv4yI=
367 github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
368 github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
365 -github.com/ipfs/boxo v0.24.4-0.20241125210908-37756ce2eeb1 h1:Ox1qTlON8qG46rUL7dDEwnIt7W9MhaidtvR/97RywWw=
366 -github.com/ipfs/boxo v0.24.4-0.20241125210908-37756ce2eeb1/go.mod h1:Kxk43F+avGAsJSwhJW4isNYrpGwXHRJCvJ19Pt+MQc4=
369 +github.com/ipfs/boxo v0.24.4-0.20241203185533-3a3e8afa3492 h1:kiS5+H+6aJeNWWDynuYu/ijgzkBTrInl++VFcNDgq+g=
370 +github.com/ipfs/boxo v0.24.4-0.20241203185533-3a3e8afa3492/go.mod h1:lAoydO+oJhB1e7pUn4ju1Z1fuUIwy+zb0hQXRb/bu2g=
371 github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
372 github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
373 github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ=
test/dependencies/go.mod
+1 -1
@@ -119,7 +119,7 @@ require (
119 github.com/huin/goupnp v1.3.0 // indirect
120 github.com/inconshreveable/mousetrap v1.1.0 // indirect
121 github.com/ipfs/bbloom v0.0.4 // indirect
122 - github.com/ipfs/boxo v0.24.4-0.20241125210908-37756ce2eeb1 // indirect
122 + github.com/ipfs/boxo v0.24.4-0.20241203185533-3a3e8afa3492 // indirect
123 github.com/ipfs/go-block-format v0.2.0 // indirect
124 github.com/ipfs/go-cid v0.4.1 // indirect
125 github.com/ipfs/go-datastore v0.6.0 // indirect
test/dependencies/go.sum
+6 -2
@@ -162,6 +162,10 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
162 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
163 github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
164 github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
165 +github.com/gammazero/chanqueue v1.0.0 h1:FER/sMailGFA3DDvFooEkipAMU+3c9Bg3bheloPSz6o=
166 +github.com/gammazero/chanqueue v1.0.0/go.mod h1:fMwpwEiuUgpab0sH4VHiVcEoji1pSi+EIzeG4TPeKPc=
167 +github.com/gammazero/deque v1.0.0 h1:LTmimT8H7bXkkCy6gZX7zNLtkbz4NdS2z8LZuor3j34=
168 +github.com/gammazero/deque v1.0.0/go.mod h1:iflpYvtGfM3U8S8j+sZEKIak3SAKYpA5/SQewgfXDKo=
169 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
170 github.com/ghostiam/protogetter v0.3.6 h1:R7qEWaSgFCsy20yYHNIJsU9ZOb8TziSRRxuAOTVKeOk=
171 github.com/ghostiam/protogetter v0.3.6/go.mod h1:7lpeDnEJ1ZjL/YtyoN99ljO4z0pd3H0d18/t2dPBxHw=
@@ -318,8 +322,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
322 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
323 github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
324 github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
321 -github.com/ipfs/boxo v0.24.4-0.20241125210908-37756ce2eeb1 h1:Ox1qTlON8qG46rUL7dDEwnIt7W9MhaidtvR/97RywWw=
322 -github.com/ipfs/boxo v0.24.4-0.20241125210908-37756ce2eeb1/go.mod h1:Kxk43F+avGAsJSwhJW4isNYrpGwXHRJCvJ19Pt+MQc4=
325 +github.com/ipfs/boxo v0.24.4-0.20241203185533-3a3e8afa3492 h1:kiS5+H+6aJeNWWDynuYu/ijgzkBTrInl++VFcNDgq+g=
326 +github.com/ipfs/boxo v0.24.4-0.20241203185533-3a3e8afa3492/go.mod h1:lAoydO+oJhB1e7pUn4ju1Z1fuUIwy+zb0hQXRb/bu2g=
327 github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs=
328 github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM=
329 github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=