2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-25 06:07:44 +00:00

ls: rework ncdu output to use walker.LeaveDir

This commit is contained in:
Michael Eischer 2024-01-20 23:37:54 +01:00
parent 9ecbda059c
commit 1b008c92d3

View File

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"path/filepath"
"strings" "strings"
"time" "time"
@ -67,7 +66,7 @@ func init() {
flags.BoolVarP(&lsOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode") flags.BoolVarP(&lsOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode")
flags.BoolVar(&lsOptions.Recursive, "recursive", false, "include files in subfolders of the listed directories") flags.BoolVar(&lsOptions.Recursive, "recursive", false, "include files in subfolders of the listed directories")
flags.BoolVar(&lsOptions.HumanReadable, "human-readable", false, "print sizes in human readable format") flags.BoolVar(&lsOptions.HumanReadable, "human-readable", false, "print sizes in human readable format")
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 lsSnapshot struct { type lsSnapshot struct {
@ -119,10 +118,15 @@ func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error {
return enc.Encode(n) return enc.Encode(n)
} }
type ncduPrinter struct {
out io.Writer
depth int
}
// 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 lsSnapshotNcdu(stdout io.Writer, depth *int, sn *restic.Snapshot) { func (p *ncduPrinter) ProcessSnapshot(sn *restic.Snapshot) {
const NcduMajorVer = 1 const NcduMajorVer = 1
const NcduMinorVer = 2 const NcduMinorVer = 2
@ -130,11 +134,11 @@ func lsSnapshotNcdu(stdout io.Writer, depth *int, sn *restic.Snapshot) {
if err != nil { if err != nil {
Warnf("JSON encode failed: %v\n", err) Warnf("JSON encode failed: %v\n", err)
} }
*depth++ p.depth++
fmt.Fprintf(stdout, "[%d, %d, %s", NcduMajorVer, NcduMinorVer, string(snapshotBytes)) fmt.Fprintf(p.out, "[%d, %d, %s", NcduMajorVer, NcduMinorVer, string(snapshotBytes))
} }
func lsNodeNcdu(stdout io.Writer, depth *int, currentPath *string, path string, node *restic.Node) { func (p *ncduPrinter) ProcessNode(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"`
@ -168,30 +172,21 @@ func lsNodeNcdu(stdout io.Writer, depth *int, currentPath *string, path string,
Warnf("JSON encode failed: %v\n", err) Warnf("JSON encode failed: %v\n", err)
} }
thisPath := filepath.Dir(path)
for thisPath != *currentPath {
*depth--
if *depth < 0 {
panic("cannot find suitable parent directory")
}
fmt.Fprintf(stdout, "\n%s]", strings.Repeat(" ", *depth))
*currentPath = filepath.Dir(*currentPath)
}
if node.Type == "dir" { if node.Type == "dir" {
*currentPath = path p.depth++
*depth++ fmt.Fprintf(p.out, ", [\n%s%s", strings.Repeat(" ", p.depth), string(outJson))
fmt.Fprintf(stdout, ", [\n%s%s", strings.Repeat(" ", *depth), string(outJson))
} else { } else {
fmt.Fprintf(stdout, ",\n%s%s", strings.Repeat(" ", *depth), string(outJson)) fmt.Fprintf(p.out, ",\n%s%s", strings.Repeat(" ", p.depth), string(outJson))
} }
} }
func lsCloseNcdu(stdout io.Writer, depth *int) { func (p *ncduPrinter) LeaveDir(path string) {
for *depth > 0 { p.depth--
fmt.Fprintf(stdout, "%s]\n", strings.Repeat(" ", *depth)) fmt.Fprintf(p.out, "\n%s]", strings.Repeat(" ", p.depth))
*depth-- }
}
func (p *ncduPrinter) Close() {
fmt.Fprint(p.out, "\n]\n")
} }
func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []string) error { func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []string) error {
@ -260,8 +255,10 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
} }
var ( var (
printSnapshot func(sn *restic.Snapshot) printSnapshot func(sn *restic.Snapshot)
printNode func(path string, node *restic.Node) printNode func(path string, node *restic.Node)
printLeaveNode func(path string)
printClose func()
) )
if gopts.JSON { if gopts.JSON {
@ -286,13 +283,13 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
} }
} }
} else if opts.Ncdu { } else if opts.Ncdu {
var depth int ncdu := &ncduPrinter{
var currentPath = "/" out: globalOptions.stdout,
printSnapshot = func(sn *restic.Snapshot) { lsSnapshotNcdu(globalOptions.stdout, &depth, sn) }
printNode = func(path string, node *restic.Node) {
lsNodeNcdu(globalOptions.stdout, &depth, &currentPath, path, node)
} }
defer lsCloseNcdu(globalOptions.stdout, &depth) printSnapshot = ncdu.ProcessSnapshot
printNode = ncdu.ProcessNode
printLeaveNode = ncdu.LeaveDir
printClose = ncdu.Close
} else { } else {
printSnapshot = func(sn *restic.Snapshot) { printSnapshot = func(sn *restic.Snapshot) {
Verbosef("%v filtered by %v:\n", sn, dirs) Verbosef("%v filtered by %v:\n", sn, dirs)
@ -353,11 +350,20 @@ 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) {
if printLeaveNode != nil && withinDir(path) && path != "/" {
printLeaveNode(path)
}
},
}) })
if err != nil { if err != nil {
return err return err
} }
if printClose != nil {
printClose()
}
return nil return nil
} }