ls: unify printer implementations

This commit is contained in:
Michael Eischer 2024-01-20 23:59:33 +01:00
parent 1b008c92d3
commit a2fe337610
1 changed files with 73 additions and 53 deletions

View File

@ -69,6 +69,18 @@ func init() {
flags.BoolVar(&lsOptions.Ncdu, "ncdu", false, "output NCDU save format (pipe into 'ncdu -f -')") flags.BoolVar(&lsOptions.Ncdu, "ncdu", false, "output NCDU save format (pipe into 'ncdu -f -')")
} }
type lsPrinter interface {
Snapshot(sn *restic.Snapshot)
Node(path string, node *restic.Node)
LeaveDir(path string)
Close()
}
type jsonLsPrinter struct {
enc *json.Encoder
}
func (p *jsonLsPrinter) Snapshot(sn *restic.Snapshot) {
type lsSnapshot struct { type lsSnapshot struct {
*restic.Snapshot *restic.Snapshot
ID *restic.ID `json:"id"` ID *restic.ID `json:"id"`
@ -76,7 +88,25 @@ type lsSnapshot struct {
StructType string `json:"struct_type"` // "snapshot" StructType string `json:"struct_type"` // "snapshot"
} }
err := p.enc.Encode(lsSnapshot{
Snapshot: sn,
ID: sn.ID(),
ShortID: sn.ID().Str(),
StructType: "snapshot",
})
if err != nil {
Warnf("JSON encode failed: %v\n", err)
}
}
// Print node in our custom JSON format, followed by a newline. // Print node in our custom JSON format, followed by a newline.
func (p *jsonLsPrinter) Node(path string, node *restic.Node) {
err := lsNodeJSON(p.enc, path, node)
if err != nil {
Warnf("JSON encode failed: %v\n", err)
}
}
func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error { func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error {
n := &struct { n := &struct {
Name string `json:"name"` Name string `json:"name"`
@ -118,7 +148,10 @@ func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error {
return enc.Encode(n) return enc.Encode(n)
} }
type ncduPrinter struct { func (p *jsonLsPrinter) LeaveDir(path string) {}
func (p *jsonLsPrinter) Close() {}
type ncduLsPrinter struct {
out io.Writer out io.Writer
depth int depth int
} }
@ -126,7 +159,7 @@ type ncduPrinter struct {
// lsSnapshotNcdu prints a restic snapshot in Ncdu save format. // lsSnapshotNcdu prints a restic snapshot in Ncdu save format.
// It opens the JSON list. Nodes are added with lsNodeNcdu and the list is closed by lsCloseNcdu. // It opens the JSON list. Nodes are added with lsNodeNcdu and the list is closed by lsCloseNcdu.
// Format documentation: https://dev.yorhel.nl/ncdu/jsonfmt // Format documentation: https://dev.yorhel.nl/ncdu/jsonfmt
func (p *ncduPrinter) ProcessSnapshot(sn *restic.Snapshot) { func (p *ncduLsPrinter) Snapshot(sn *restic.Snapshot) {
const NcduMajorVer = 1 const NcduMajorVer = 1
const NcduMinorVer = 2 const NcduMinorVer = 2
@ -138,7 +171,7 @@ func (p *ncduPrinter) ProcessSnapshot(sn *restic.Snapshot) {
fmt.Fprintf(p.out, "[%d, %d, %s", NcduMajorVer, NcduMinorVer, string(snapshotBytes)) fmt.Fprintf(p.out, "[%d, %d, %s", NcduMajorVer, NcduMinorVer, string(snapshotBytes))
} }
func (p *ncduPrinter) ProcessNode(path string, node *restic.Node) { func (p *ncduLsPrinter) Node(path string, node *restic.Node) {
type NcduNode struct { type NcduNode struct {
Name string `json:"name"` Name string `json:"name"`
Asize uint64 `json:"asize"` Asize uint64 `json:"asize"`
@ -180,15 +213,31 @@ func (p *ncduPrinter) ProcessNode(path string, node *restic.Node) {
} }
} }
func (p *ncduPrinter) LeaveDir(path string) { func (p *ncduLsPrinter) LeaveDir(path string) {
p.depth-- p.depth--
fmt.Fprintf(p.out, "\n%s]", strings.Repeat(" ", p.depth)) fmt.Fprintf(p.out, "\n%s]", strings.Repeat(" ", p.depth))
} }
func (p *ncduPrinter) Close() { func (p *ncduLsPrinter) Close() {
fmt.Fprint(p.out, "\n]\n") fmt.Fprint(p.out, "\n]\n")
} }
type textLsPrinter struct {
dirs []string
ListLong bool
HumanReadable bool
}
func (p *textLsPrinter) Snapshot(sn *restic.Snapshot) {
Verbosef("%v filtered by %v:\n", sn, p.dirs)
}
func (p *textLsPrinter) Node(path string, node *restic.Node) {
Printf("%s\n", formatNode(path, node, p.ListLong, p.HumanReadable))
}
func (p *textLsPrinter) LeaveDir(path string) {}
func (p *textLsPrinter) Close() {}
func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []string) error { func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []string) error {
if len(args) == 0 { if len(args) == 0 {
return errors.Fatal("no snapshot ID specified, specify snapshot ID or use special ID 'latest'") return errors.Fatal("no snapshot ID specified, specify snapshot ID or use special ID 'latest'")
@ -254,48 +303,21 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
return err return err
} }
var ( var printer lsPrinter
printSnapshot func(sn *restic.Snapshot)
printNode func(path string, node *restic.Node)
printLeaveNode func(path string)
printClose func()
)
if gopts.JSON { if gopts.JSON {
enc := json.NewEncoder(globalOptions.stdout) printer = &jsonLsPrinter{
enc: json.NewEncoder(globalOptions.stdout),
printSnapshot = func(sn *restic.Snapshot) {
err := enc.Encode(lsSnapshot{
Snapshot: sn,
ID: sn.ID(),
ShortID: sn.ID().Str(),
StructType: "snapshot",
})
if err != nil {
Warnf("JSON encode failed: %v\n", err)
}
}
printNode = func(path string, node *restic.Node) {
err := lsNodeJSON(enc, path, node)
if err != nil {
Warnf("JSON encode failed: %v\n", err)
}
} }
} else if opts.Ncdu { } else if opts.Ncdu {
ncdu := &ncduPrinter{ printer = &ncduLsPrinter{
out: globalOptions.stdout, out: globalOptions.stdout,
} }
printSnapshot = ncdu.ProcessSnapshot
printNode = ncdu.ProcessNode
printLeaveNode = ncdu.LeaveDir
printClose = ncdu.Close
} else { } else {
printSnapshot = func(sn *restic.Snapshot) { printer = &textLsPrinter{
Verbosef("%v filtered by %v:\n", sn, dirs) dirs: dirs,
} ListLong: opts.ListLong,
printNode = func(path string, node *restic.Node) { HumanReadable: opts.HumanReadable,
Printf("%s\n", formatNode(path, node, opts.ListLong, opts.HumanReadable))
} }
} }
@ -313,7 +335,7 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
return err return err
} }
printSnapshot(sn) printer.Snapshot(sn)
processNode := func(_ restic.ID, nodepath string, node *restic.Node, err error) error { processNode := func(_ restic.ID, nodepath string, node *restic.Node, err error) error {
if err != nil { if err != nil {
@ -325,7 +347,7 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
if withinDir(nodepath) { if withinDir(nodepath) {
// if we're within a dir, print the node // if we're within a dir, print the node
printNode(nodepath, node) printer.Node(nodepath, node)
// if recursive listing is requested, signal the walker that it // if recursive listing is requested, signal the walker that it
// should continue walking recursively // should continue walking recursively
@ -351,8 +373,9 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
err = walker.Walk(ctx, repo, *sn.Tree, walker.WalkVisitor{ err = walker.Walk(ctx, repo, *sn.Tree, walker.WalkVisitor{
ProcessNode: processNode, ProcessNode: processNode,
LeaveDir: func(path string) { LeaveDir: func(path string) {
if printLeaveNode != nil && withinDir(path) && path != "/" { // the root path `/` has no corresponding node and is thus also skipped by processNode
printLeaveNode(path) if withinDir(path) && path != "/" {
printer.LeaveDir(path)
} }
}, },
}) })
@ -361,9 +384,6 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
return err return err
} }
if printClose != nil { printer.Close()
printClose()
}
return nil return nil
} }