mirror of
https://github.com/kopia/kopia.git
synced 2026-01-23 22:07:54 -05:00
* Remove remaining internal uses of Readdir * Remove old helpers and interface functions. * Update tests for updated fs.Directory interface * Fix index out of range error in snapshot walker Record one error if an error occurred and it's not limiting errors * Use helper functions more; exit loops early Follow up on reviewer comments and reduce code duplication, use more targetted functions like Directory.Child, and exit directory iteration early if possible. * Remove fs.Entries type and unused functions Leave some functions dealing with sorting and finding entries in fs package. This retains tests for those functions while still allowing mockfs to access them. * Simplify function return
141 lines
3.5 KiB
Go
141 lines
3.5 KiB
Go
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 {
|
|
if err := d.IterateEntries(ctx, func(innerCtx context.Context, e fs.Entry) error {
|
|
return c.printDirectoryEntry(innerCtx, e, prefix, indent)
|
|
}); 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()
|
|
}
|