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=