/
command_ls.go
153 lines (123 loc) · 3.6 KB
/
command_ls.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package cli
import (
"context"
"fmt"
"strings"
"github.com/pkg/errors"
"github.com/kopia/kopia/fs"
"github.com/kopia/kopia/repo"
"github.com/kopia/kopia/repo/object"
"github.com/kopia/kopia/snapshot/snapshotfs"
)
type commandList struct {
long bool
recursive bool
showOID bool
errorSummary bool
path string
out textOutput
}
func (c *commandList) setup(svc appServices, parent commandParent) {
cmd := parent.Command("list", "List a directory stored in repository object.").Alias("ls")
cmd.Flag("long", "Long output").Short('l').BoolVar(&c.long)
cmd.Flag("recursive", "Recursive output").Short('r').BoolVar(&c.recursive)
cmd.Flag("show-object-id", "Show object IDs").Short('o').BoolVar(&c.showOID)
cmd.Flag("error-summary", "Emit error summary").Default("true").BoolVar(&c.errorSummary)
cmd.Arg("object-path", "Path").Required().StringVar(&c.path)
cmd.Action(svc.repositoryReaderAction(c.run))
c.out.setup(svc)
}
func (c *commandList) run(ctx context.Context, rep repo.Repository) error {
dir, err := snapshotfs.FilesystemDirectoryFromIDWithPath(ctx, rep, c.path, false)
if err != nil {
return errors.Wrap(err, "unable to get filesystem directory entry")
}
var prefix string
if !c.long {
prefix = c.path
if !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
}
return c.listDirectory(ctx, dir, prefix, "")
}
func (c *commandList) listDirectory(ctx context.Context, d fs.Directory, prefix, indent string) error {
iter, err := d.Iterate(ctx)
if err != nil {
return err //nolint:wrapcheck
}
defer iter.Close()
e, err := iter.Next(ctx)
for e != nil {
if err2 := c.printDirectoryEntry(ctx, e, prefix, indent); err2 != nil {
return err2
}
e, err = iter.Next(ctx)
}
if err != nil {
return err //nolint:wrapcheck
}
if dws, ok := d.(fs.DirectoryWithSummary); ok && c.errorSummary {
if ds, _ := dws.Summary(ctx); ds != nil && ds.FatalErrorCount > 0 {
errorColor.Fprintf(c.out.stderr(), "\nNOTE: Encountered %v errors while snapshotting this directory:\n\n", ds.FatalErrorCount) //nolint:errcheck
for _, e := range ds.FailedEntries {
errorColor.Fprintf(c.out.stderr(), "- Error in \"%v\": %v\n", e.EntryPath, e.Error) //nolint:errcheck
}
}
}
return nil
}
func (c *commandList) printDirectoryEntry(ctx context.Context, e fs.Entry, prefix, indent string) error {
hoid, ok := e.(object.HasObjectID)
if !ok {
return errors.Errorf("entry without object ID")
}
objectID := hoid.ObjectID()
oid := objectID.String()
col := defaultColor
var (
errorSummary string
info string
)
if dws, ok := e.(fs.DirectoryWithSummary); ok && c.errorSummary {
if ds, _ := dws.Summary(ctx); ds != nil && ds.FatalErrorCount > 0 {
errorSummary = fmt.Sprintf(" (%v errors)", ds.FatalErrorCount)
col = errorColor
}
}
switch {
case c.long:
info = fmt.Sprintf(
"%v %12d %v %-34v %v%v",
e.Mode(),
e.Size(),
formatTimestamp(e.ModTime().Local()),
oid,
c.nameToDisplay(prefix, e),
errorSummary,
)
case c.showOID:
info = fmt.Sprintf("%-34v %v%v", oid, c.nameToDisplay(prefix, e), errorSummary)
default:
info = fmt.Sprintf("%v%v", c.nameToDisplay(prefix, e), errorSummary)
}
col.Fprintln(c.out.stdout(), info) //nolint:errcheck
if c.recursive {
if subdir, ok := e.(fs.Directory); ok {
if listerr := c.listDirectory(ctx, subdir, prefix+e.Name()+"/", indent+" "); listerr != nil {
return listerr
}
}
}
return nil
}
func (c *commandList) nameToDisplay(prefix string, e fs.Entry) string {
suffix := ""
if e.IsDir() {
suffix = "/"
}
if c.long || c.recursive {
return prefix + e.Name() + suffix
}
return e.Name()
}