package cli import ( "context" "fmt" "github.com/pkg/errors" "github.com/kopia/kopia/internal/stats" "github.com/kopia/kopia/repo" "github.com/kopia/kopia/repo/compression" "github.com/kopia/kopia/repo/content" ) type commandContentList struct { long bool includeDeleted bool deletedOnly bool summary bool human bool compression bool contentRange contentRangeFlags jo jsonOutput out textOutput } func (c *commandContentList) setup(svc appServices, parent commandParent) { cmd := parent.Command("list", "List contents").Alias("ls") cmd.Flag("long", "Long output").Short('l').BoolVar(&c.long) cmd.Flag("compression", "Compression").Short('c').BoolVar(&c.compression) cmd.Flag("deleted", "Include deleted content").BoolVar(&c.includeDeleted) cmd.Flag("deleted-only", "Only show deleted content").BoolVar(&c.deletedOnly) cmd.Flag("summary", "Summarize the list").Short('s').BoolVar(&c.summary) cmd.Flag("human", "Human-readable output").Short('h').BoolVar(&c.human) c.contentRange.setup(cmd) c.jo.setup(svc, cmd) c.out.setup(svc) cmd.Action(svc.directRepositoryReadAction(c.run)) } func (c *commandContentList) run(ctx context.Context, rep repo.DirectRepository) error { var jl jsonList jl.begin(&c.jo) defer jl.end() var totalSize stats.CountSum err := rep.ContentReader().IterateContents( ctx, content.IterateOptions{ Range: c.contentRange.contentIDRange(), IncludeDeleted: c.includeDeleted || c.deletedOnly, }, func(b content.Info) error { if c.deletedOnly && !b.GetDeleted() { return nil } totalSize.Add(int64(b.GetPackedLength())) switch { case c.jo.jsonOutput: jl.emit(b) case c.compression: c.outputCompressed(b) case c.long: c.outputLong(b) default: c.out.printStdout("%v\n", b.GetContentID()) } return nil }) if err != nil { return errors.Wrap(err, "error iterating") } if c.summary { count, sz := totalSize.Approximate() c.out.printStdout("Total: %v contents, %v total size\n", maybeHumanReadableCount(c.human, int64(count)), maybeHumanReadableBytes(c.human, sz)) } return nil } func (c *commandContentList) outputLong(b content.Info) { c.out.printStdout("%v %v %v %v %v+%v%v %v\n", b.GetContentID(), b.GetOriginalLength(), formatTimestamp(b.Timestamp()), b.GetPackBlobID(), b.GetPackOffset(), maybeHumanReadableBytes(c.human, int64(b.GetPackedLength())), c.deletedInfoString(b), c.compressionInfoStringString(b), ) } func (c *commandContentList) outputCompressed(b content.Info) { c.out.printStdout("%v length %v packed %v %v %v\n", b.GetContentID(), maybeHumanReadableBytes(c.human, int64(b.GetOriginalLength())), maybeHumanReadableBytes(c.human, int64(b.GetPackedLength())), c.compressionInfoStringString(b), c.deletedInfoString(b), ) } func (*commandContentList) deletedInfoString(b content.Info) string { if b.GetDeleted() { return " (deleted)" } return "" } func (*commandContentList) compressionInfoStringString(b content.Info) string { h := b.GetCompressionHeaderID() if h == content.NoCompression { return "-" } s := string(compression.HeaderIDToName[h]) if s == "" { s = fmt.Sprintf("compression-%x", h) } if b.GetOriginalLength() > 0 { s += " " + formatCompressionPercentage(int64(b.GetOriginalLength()), int64(b.GetPackedLength())) } return s }