+59
-3
cmd/atr-inspect.go
+59
-3
cmd/atr-inspect.go
···
1
1
package cmd
2
2
3
3
import (
4
+
"context"
5
+
"encoding/hex"
4
6
"fmt"
5
7
"os"
8
+
"sort"
9
+
"strings"
6
10
7
11
"github.com/atscan/atr/cli"
8
12
"github.com/atscan/atr/engine"
···
13
17
)
14
18
15
19
func init() {
16
-
rootCmd.AddCommand(InspectCmd)
20
+
rootCmd.AddCommand(LsCmd)
17
21
}
18
22
19
-
var InspectCmd = &cobra.Command{
23
+
var LsCmd = &cobra.Command{
20
24
Use: "inspect",
21
25
Aliases: []string{"i"},
22
26
Short: "Inspect repo(s)",
···
36
40
}
37
41
yellow := color.New(color.FgYellow).SprintFunc()
38
42
cyan := color.New(color.FgCyan).SprintFunc()
39
-
fmt.Printf("%v:\n Head: %s\n Size: %s Items: %v\n\n", yellow(ss.File), cyan(ss.Root.String()), cyan(humanize.Bytes(uint64(ss.Size))), cyan(humanize.Comma(int64(len(ss.Items)))))
43
+
boldCyan := color.New(color.FgCyan, color.Bold).SprintFunc()
44
+
green := color.New(color.FgGreen).SprintFunc()
45
+
46
+
fmt.Printf("%v:\n", yellow(ss.File))
47
+
fmt.Printf(" DID: %s Repo Version: %v\n", boldCyan(ss.Repo.SignedCommit().Did), cyan(ss.Repo.SignedCommit().Version))
48
+
fmt.Printf(" Head: %s\n", cyan(ss.Root.String()))
49
+
fmt.Printf(" Sig: %s\n", cyan(hex.EncodeToString(ss.Repo.SignedCommit().Sig)))
50
+
51
+
stats, _ := ss.GetCollectionStats("")
52
+
keys := make([]string, 0, len(stats))
53
+
total := 0
54
+
for k := range stats {
55
+
keys = append(keys, k)
56
+
total += stats[k]
57
+
}
58
+
sort.Strings(keys)
59
+
cp, _ := ss.Repo.GetCommitsPath(-1)
60
+
_, v, _ := ss.Repo.GetRecord(context.TODO(), "app.bsky.actor.profile/self")
61
+
62
+
dn := v["displayName"]
63
+
if dn != nil {
64
+
dn = boldCyan(dn)
65
+
} else {
66
+
dn = "(empty)"
67
+
}
68
+
desc := v["description"]
69
+
if desc != nil {
70
+
desc = cyan(strings.Replace(desc.(string), "\n", "\n ", -1))
71
+
} else {
72
+
desc = "(empty)"
73
+
}
74
+
75
+
fmt.Printf(" Size: %s Blocks: %v Commits: %v Objects: %v\n", cyan(humanize.Bytes(uint64(ss.Size))), cyan(humanize.Comma(int64(ss.Repo.Blocks))), cyan(humanize.Comma(int64(len(cp)))), cyan(humanize.Comma(int64(total))))
76
+
fmt.Printf(" Profile:\n")
77
+
fmt.Printf(" Display Name: %v\n", dn)
78
+
fmt.Printf(" Description: %v\n", desc)
79
+
80
+
fmt.Printf(" Collections:\n")
81
+
for _, k := range keys {
82
+
fmt.Printf(" %s: %v\n", green(k), cyan(humanize.Comma(int64(stats[k]))))
83
+
}
84
+
fmt.Printf(" Last 5 commits:\n")
85
+
for i, cid := range cp {
86
+
fmt.Printf(" %v\n", cyan(cid.String()))
87
+
if i >= 5 {
88
+
break
89
+
}
90
+
}
91
+
if len(cp) > 5 {
92
+
fmt.Printf(" %s\n", cyan("..."))
93
+
}
94
+
95
+
fmt.Println("")
40
96
}
41
97
42
98
stat, _ := os.Stdin.Stat()
+72
cmd/atr-log.go
+72
cmd/atr-log.go
···
1
+
package cmd
2
+
3
+
import (
4
+
"fmt"
5
+
"os"
6
+
7
+
"github.com/atscan/atr/cli"
8
+
"github.com/atscan/atr/engine"
9
+
"github.com/atscan/atr/repo"
10
+
"github.com/fatih/color"
11
+
"github.com/spf13/cobra"
12
+
)
13
+
14
+
var (
15
+
LogRaw bool
16
+
)
17
+
18
+
func init() {
19
+
rootCmd.AddCommand(LogCmd)
20
+
LogCmd.Flags().BoolVar(&LogRaw, "raw", false, "Do not use colors (faster)")
21
+
}
22
+
23
+
var LogCmd = &cobra.Command{
24
+
Use: "log [target] [--raw]",
25
+
Aliases: []string{"l"},
26
+
Short: "Show commit history (path)",
27
+
Long: ``,
28
+
Run: func(cmd *cobra.Command, args []string) {
29
+
ctx := cli.Context{
30
+
WorkingDir: workingDir,
31
+
Args: args,
32
+
}
33
+
walk := func(ss repo.RepoSnapshot, err error) {
34
+
if ss.Root.String() == "b" {
35
+
return
36
+
}
37
+
cp, _ := ss.Repo.GetCommitsPath(-1)
38
+
//cp = util.ReverseCidSlice(cp)
39
+
40
+
if LogRaw {
41
+
for _, cid := range cp {
42
+
fmt.Printf("%v\n", cid.String())
43
+
}
44
+
} else {
45
+
yellow := color.New(color.FgYellow).SprintFunc()
46
+
cyan := color.New(color.FgCyan).SprintFunc()
47
+
green := color.New(color.FgGreen).SprintFunc()
48
+
49
+
fmt.Printf("[%v]\n", yellow(ss.File))
50
+
for i, cid := range cp {
51
+
stats, _ := ss.GetCollectionStats(cid.String())
52
+
53
+
sum := 0
54
+
for _, v := range stats {
55
+
sum += v
56
+
}
57
+
fmt.Printf("%v [#%v] %v objects\n", cyan(cid.String()), len(cp)-i, green(sum))
58
+
}
59
+
}
60
+
//fmt.Printf("\n")
61
+
}
62
+
63
+
stat, _ := os.Stdin.Stat()
64
+
if (stat.Mode() & os.ModeCharDevice) == 0 {
65
+
// data is being piped to stdin
66
+
engine.WalkStream(&ctx, os.Stdin, walk)
67
+
} else {
68
+
//stdin is from a terminal
69
+
engine.WalkFiles(&ctx, walk)
70
+
}
71
+
},
72
+
}
+11
cmd/atr-show.go
+11
cmd/atr-show.go
···
22
22
QueryJmes string
23
23
Type string
24
24
Raw bool
25
+
Root string
26
+
GoBack int
25
27
)
26
28
27
29
func init() {
28
30
rootCmd.AddCommand(ShowCmd)
31
+
ShowCmd.Flags().StringVarP(&Root, "root", "r", "", "Use specific root")
32
+
ShowCmd.Flags().IntVarP(&GoBack, "back", "b", 0, "Go back (n) commits")
29
33
ShowCmd.Flags().StringVarP(&Type, "type", "t", "", "Filter by item type")
30
34
ShowCmd.Flags().StringVarP(&Query, "query", "q", "", "Query results (jq)")
31
35
ShowCmd.Flags().StringVarP(&QueryJmes, "query-jmes", "x", "", "Query results (jmespath)")
···
78
82
hg := cli.Highlight(style)
79
83
80
84
walk := func(ss repo.RepoSnapshot, err error) {
85
+
86
+
rr := Root
87
+
if GoBack > 0 {
88
+
gb, _ := ss.Repo.GetCommitsPath(GoBack)
89
+
rr = gb[len(gb)-1].String()
90
+
}
91
+
ss.LoadItems(rr)
81
92
82
93
for _, e := range ss.Items {
83
94
tf := Type
+12
-14
engine/io.go
+12
-14
engine/io.go
···
10
10
"strings"
11
11
12
12
"github.com/atscan/atr/repo"
13
-
"github.com/ipfs/go-cid"
14
13
"github.com/klauspost/compress/zstd"
15
14
)
16
15
···
52
51
}
53
52
54
53
func LoadFromStream(input io.Reader) (repo.RepoSnapshot, error) {
54
+
ss, err := LoadRepoFromStream(input)
55
+
if err != nil {
56
+
return ss, err
57
+
}
58
+
59
+
ss.Root = ss.Repo.Head()
60
+
61
+
return ss, nil
62
+
}
63
+
64
+
func LoadRepoFromStream(input io.Reader) (repo.RepoSnapshot, error) {
55
65
rctx := context.TODO()
56
66
ss := repo.RepoSnapshot{}
57
67
···
66
76
if err != nil {
67
77
return ss, err
68
78
}
69
-
ss.Root = r.Head()
70
-
var out []repo.RepoItem
71
-
if err := r.ForEach(rctx, "", func(k string, v cid.Cid) error {
72
-
_, rec, err := r.GetRecord(rctx, k)
73
-
if err != nil {
74
-
log.Println("Cannot get record:", v.String())
75
-
}
76
-
out = append(out, repo.RepoItem{Cid: v, Path: k, Body: rec})
77
-
ss.Items = out
78
-
return nil
79
-
}); err != nil {
80
-
return ss, err
81
-
}
79
+
ss.Repo = *r
82
80
return ss, nil
83
81
}
+2
-2
go.mod
+2
-2
go.mod
···
8
8
github.com/dustin/go-humanize v1.0.1
9
9
github.com/fatih/color v1.15.0
10
10
github.com/fxamacker/cbor/v2 v2.4.0
11
+
github.com/ipfs/boxo v0.8.0
11
12
github.com/ipfs/go-cid v0.4.1
12
13
github.com/ipfs/go-datastore v0.6.0
13
-
github.com/ipfs/go-ipfs-blockstore v1.3.0
14
14
github.com/ipfs/go-ipld-cbor v0.0.7-0.20230126201833-a73d038d90bc
15
15
github.com/ipld/go-car/v2 v2.10.1
16
16
github.com/itchyny/gojq v0.12.13
···
33
33
github.com/inconshreveable/mousetrap v1.1.0 // indirect
34
34
github.com/ipfs/bbloom v0.0.4 // indirect
35
35
github.com/ipfs/go-block-format v0.1.2 // indirect
36
+
github.com/ipfs/go-ipfs-blockstore v1.3.0 // indirect
36
37
github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect
37
38
github.com/ipfs/go-ipfs-util v0.0.2 // indirect
38
39
github.com/ipfs/go-ipld-format v0.5.0 // indirect
···
57
58
github.com/multiformats/go-varint v0.0.7 // indirect
58
59
github.com/opentracing/opentracing-go v1.2.0 // indirect
59
60
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect
60
-
github.com/pkg/errors v0.9.1 // indirect
61
61
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect
62
62
github.com/spaolacci/murmur3 v1.1.0 // indirect
63
63
github.com/spf13/pflag v1.0.5 // indirect
+4
-3
go.sum
+4
-3
go.sum
···
1
1
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2
2
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
3
3
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
4
-
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
5
4
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
5
+
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
6
6
github.com/bluesky-social/indigo v0.0.0-20230714174244-57d75d8cfc65 h1:0z5rF7oA9eqqoAFoiHTfioBK8U4hzMmyGMoajEqJVFE=
7
7
github.com/bluesky-social/indigo v0.0.0-20230714174244-57d75d8cfc65/go.mod h1:oDI5NiD0XzShv5VITWyUJNP3pSh4prTDEzhKbkdKORA=
8
8
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
···
30
30
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
31
31
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
32
32
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
33
-
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
34
33
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
34
+
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
35
35
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
36
36
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
37
37
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
···
45
45
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
46
46
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
47
47
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
48
+
github.com/ipfs/boxo v0.8.0 h1:UdjAJmHzQHo/j3g3b1bAcAXCj/GM6iTwvSlBDvPBNBs=
49
+
github.com/ipfs/boxo v0.8.0/go.mod h1:RIsi4CnTyQ7AUsNn5gXljJYZlQrHBMnJp94p73liFiA=
48
50
github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
49
51
github.com/ipfs/go-block-format v0.1.2 h1:GAjkfhVx1f4YTODS6Esrj1wt2HhrtwTnhEr+DyPUaJo=
50
52
github.com/ipfs/go-block-format v0.1.2/go.mod h1:mACVcrxarQKstUU3Yf/RdwbC4DzPV6++rO2a3d+a/KE=
···
159
161
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw=
160
162
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
161
163
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
162
-
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
163
164
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
164
165
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
165
166
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0=
-16
repo/ext.go
-16
repo/ext.go
+61
-12
repo/repo.go
+61
-12
repo/repo.go
···
9
9
10
10
"github.com/bluesky-social/indigo/mst"
11
11
"github.com/bluesky-social/indigo/util"
12
+
blockstore "github.com/ipfs/boxo/blockstore"
12
13
"github.com/ipfs/go-cid"
13
14
"github.com/ipfs/go-datastore"
14
-
blockstore "github.com/ipfs/go-ipfs-blockstore"
15
15
cbor "github.com/ipfs/go-ipld-cbor"
16
16
"github.com/ipld/go-car/v2"
17
17
)
···
35
35
}
36
36
37
37
type Repo struct {
38
+
Blocks int
39
+
38
40
sc SignedCommit
39
41
cst cbor.IpldStore
40
42
bs blockstore.Blockstore
···
67
69
return buf.Bytes(), nil
68
70
}*/
69
71
70
-
func IngestRepo(ctx context.Context, bs blockstore.Blockstore, r io.Reader) (cid.Cid, error) {
72
+
func IngestRepo(ctx context.Context, bs blockstore.Blockstore, r io.Reader) (cid.Cid, int, error) {
71
73
br, err := car.NewBlockReader(r)
72
74
if err != nil {
73
-
return cid.Undef, err
75
+
return cid.Undef, 0, err
74
76
}
75
77
78
+
size := 0
76
79
for {
77
80
blk, err := br.Next()
78
81
if err != nil {
79
82
if err == io.EOF {
80
83
break
81
84
}
82
-
return cid.Undef, err
85
+
return cid.Undef, size, err
83
86
}
84
87
85
88
if err := bs.Put(ctx, blk); err != nil {
86
-
return cid.Undef, err
89
+
return cid.Undef, size, err
87
90
}
91
+
size++
88
92
}
89
93
90
-
return br.Roots[0], nil
94
+
return br.Roots[0], size, nil
91
95
}
92
96
93
97
func ReadRepoFromCar(ctx context.Context, r io.Reader) (*Repo, error) {
94
98
bs := blockstore.NewBlockstore(datastore.NewMapDatastore())
95
-
root, err := IngestRepo(ctx, bs, r)
99
+
root, size, err := IngestRepo(ctx, bs, r)
96
100
if err != nil {
97
101
return nil, err
98
102
}
99
103
100
-
return OpenRepo(ctx, bs, root, false)
104
+
return OpenRepo(ctx, bs, root, size)
101
105
}
102
106
103
-
func OpenRepo(ctx context.Context, bs blockstore.Blockstore, root cid.Cid, fullRepo bool) (*Repo, error) {
107
+
func OpenRepo(ctx context.Context, bs blockstore.Blockstore, root cid.Cid, size int) (*Repo, error) {
104
108
cst := util.CborStore(bs)
105
109
106
110
var sc SignedCommit
···
117
121
bs: bs,
118
122
cst: cst,
119
123
repoCid: root,
124
+
Blocks: size,
120
125
}, nil
121
126
}
122
127
128
+
func (r *Repo) GetCommitsPath(len int) ([]cid.Cid, error) {
129
+
path := []cid.Cid{}
130
+
path = append(path, r.repoCid)
131
+
if r.sc.Prev != nil {
132
+
getParentCommits(r, *r.sc.Prev, &path, len-1)
133
+
}
134
+
return path, nil
135
+
}
136
+
137
+
func getParentCommits(r *Repo, c cid.Cid, p *[]cid.Cid, len int) ([]cid.Cid, error) {
138
+
var sc SignedCommit
139
+
ctx := context.TODO()
140
+
if err := r.cst.Get(ctx, c, &sc); err != nil {
141
+
return nil, fmt.Errorf("loading root from blockstore: %w", err)
142
+
}
143
+
*p = append(*p, c)
144
+
if len == 0 {
145
+
return nil, nil
146
+
} else {
147
+
len--
148
+
}
149
+
if sc.Prev != nil {
150
+
return getParentCommits(r, *sc.Prev, p, len)
151
+
}
152
+
return nil, nil
153
+
}
154
+
123
155
func (r *Repo) Head() cid.Cid {
124
156
return r.repoCid
125
157
}
126
158
159
+
func (r *Repo) SignedCommit() SignedCommit {
160
+
return r.sc
161
+
}
162
+
163
+
func (r *Repo) MerkleSearchTree() *mst.MerkleSearchTree {
164
+
return r.mst
165
+
}
166
+
167
+
func (r *Repo) BlockStore() blockstore.Blockstore {
168
+
return r.bs
169
+
}
170
+
127
171
func (r *Repo) getMst(ctx context.Context) (*mst.MerkleSearchTree, error) {
128
172
if r.mst != nil {
129
173
return r.mst, nil
···
134
178
return t, nil
135
179
}
136
180
181
+
func (r *Repo) MST() *mst.MerkleSearchTree {
182
+
mst, _ := r.getMst(context.TODO())
183
+
return mst
184
+
}
185
+
137
186
var ErrDoneIterating = fmt.Errorf("done iterating")
138
187
139
188
func (r *Repo) ForEach(ctx context.Context, prefix string, cb func(k string, v cid.Cid) error) error {
140
-
t := mst.LoadMST(r.cst, r.sc.Data)
189
+
t, _ := r.getMst(ctx)
141
190
142
191
if err := t.WalkLeavesFrom(ctx, prefix, cb); err != nil {
143
192
if err != ErrDoneIterating {
···
148
197
return nil
149
198
}
150
199
151
-
func (r *Repo) GetRecord(ctx context.Context, rpath string) (cid.Cid, interface{}, error) {
200
+
func (r *Repo) GetRecord(ctx context.Context, rpath string) (cid.Cid, map[string]interface{}, error) {
152
201
mst, err := r.getMst(ctx)
153
202
if err != nil {
154
203
return cid.Undef, nil, fmt.Errorf("getting repo mst: %w", err)
···
163
212
if err != nil {
164
213
return cid.Undef, nil, err
165
214
}
166
-
var v interface{}
215
+
var v map[string]interface{}
167
216
cbor2.Unmarshal(blk.RawData(), &v)
168
217
169
218
return cc, v, nil
+84
repo/snapshot.go
+84
repo/snapshot.go
···
1
+
package repo
2
+
3
+
import (
4
+
"context"
5
+
"log"
6
+
"strings"
7
+
8
+
cid "github.com/ipfs/go-cid"
9
+
)
10
+
11
+
type RepoItem struct {
12
+
Cid cid.Cid
13
+
Path string
14
+
Body interface{}
15
+
}
16
+
17
+
type RepoSnapshot struct {
18
+
Root cid.Cid
19
+
File string
20
+
Size int
21
+
Items []RepoItem
22
+
Repo Repo
23
+
}
24
+
25
+
func (ss *RepoSnapshot) GetCollectionStats(root string) (map[string]int, error) {
26
+
rctx := context.TODO()
27
+
28
+
rr := ss.Repo
29
+
if root != "" && root != "b" {
30
+
cid, err := cid.Parse(root)
31
+
if err != nil {
32
+
log.Fatalf("cannot parse CID: %s", root)
33
+
}
34
+
cr, err := OpenRepo(rctx, rr.BlockStore(), cid, 0)
35
+
if err != nil {
36
+
log.Fatal("cannot open repo")
37
+
}
38
+
rr = *cr
39
+
}
40
+
stats := make(map[string]int)
41
+
if err := rr.ForEach(rctx, "", func(k string, v cid.Cid) error {
42
+
col := strings.Split(k, "/")[0]
43
+
_, ok := stats[col]
44
+
if !ok {
45
+
stats[col] = 0
46
+
}
47
+
stats[col]++
48
+
return nil
49
+
}); err != nil {
50
+
return nil, err
51
+
}
52
+
return stats, nil
53
+
}
54
+
55
+
func (ss *RepoSnapshot) LoadItems(root string) error {
56
+
rctx := context.TODO()
57
+
58
+
rr := ss.Repo
59
+
if root != "" && root != "b" {
60
+
cid, err := cid.Parse(root)
61
+
if err != nil {
62
+
log.Fatalf("cannot parse CID: %s", root)
63
+
}
64
+
cr, err := OpenRepo(rctx, rr.BlockStore(), cid, 0)
65
+
if err != nil {
66
+
log.Fatal("cannot open repo")
67
+
}
68
+
rr = *cr
69
+
}
70
+
71
+
var out []RepoItem
72
+
if err := rr.ForEach(rctx, "", func(k string, v cid.Cid) error {
73
+
_, rec, err := rr.GetRecord(rctx, k)
74
+
if err != nil {
75
+
log.Println("Cannot get record:", v.String())
76
+
}
77
+
out = append(out, RepoItem{Cid: v, Path: k, Body: rec})
78
+
ss.Items = out
79
+
return nil
80
+
}); err != nil {
81
+
return err
82
+
}
83
+
return nil
84
+
}