Files
mapsize/internal/app/modes.go
T
Andy Dixon 0e5b9f7cc3 Add interactive snapshot comparison, CLI tests and treegen
--compare opens the TUI with growth/shrink colouring unless a report is
requested. treegen builds deterministic synthetic trees (Unicode,
hostile names, sparse files). MAPSIZE_DEBUG_PANIC verifies terminal
restoration on UI and scanner panics.
2026-10-02 08:28:52 +01:00

81 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package app
import (
"context"
"fmt"
"github.com/andydixon/mapsize/internal/duplicate"
"github.com/andydixon/mapsize/internal/inventory"
"github.com/andydixon/mapsize/internal/scan"
"github.com/andydixon/mapsize/internal/snapshot"
"github.com/andydixon/mapsize/internal/textutil"
"github.com/andydixon/mapsize/internal/tui"
)
func runTUI(cfg *Config) error {
return tui.Run(tui.Options{
ReadOnly: cfg.ReadOnly, Theme: cfg.Theme, SizeMode: cfg.SizeMode, Mouse: cfg.Mouse, Color: cfg.Color,
Save: cfg.Save, Settings: cfg.Settings,
Start: func(ctx context.Context) (*scan.Scanner, *inventory.Tree, error) { return startSource(ctx, cfg) },
})
}
func loadSnapshot(path string) (*inventory.Tree, error) { return snapshot.LoadFile(path) }
func saveSnapshot(t *inventory.Tree, path string) error { return snapshot.SaveFile(path, t) }
func runCompare(cfg *Config) error {
old, err := snapshot.LoadFile(cfg.Compare[0])
if err != nil {
return fmt.Errorf("%s: %w", cfg.Compare[0], err)
}
nw, err := snapshot.LoadFile(cfg.Compare[1])
if err != nil {
return fmt.Errorf("%s: %w", cfg.Compare[1], err)
}
d := snapshot.Compare(old, nw)
if !cfg.Report() {
return tui.Run(tui.Options{
ReadOnly: true, Theme: cfg.Theme, SizeMode: cfg.SizeMode, Mouse: cfg.Mouse, Color: cfg.Color,
Settings: cfg.Settings, Diff: d,
Start: func(context.Context) (*scan.Scanner, *inventory.Tree, error) { return nil, nw, nil },
})
}
k := cfg.Top
if k <= 0 {
k = 30
}
out := cfg.Stdout
ro, rn := old.Node(0), nw.Node(0)
fmt.Fprintf(out, "%s → %s\n", textutil.Sanitize(old.Stats.FromSnapshot), textutil.Sanitize(nw.Stats.FromSnapshot))
fmt.Fprintf(out, "Total %s → %s (%s)\n\n", textutil.Size(ro.Total(cfg.SizeMode)), textutil.Size(rn.Total(cfg.SizeMode)),
textutil.SignedSize(rn.Total(cfg.SizeMode)-ro.Total(cfg.SizeMode)))
for _, c := range d.Changes(k, cfg.SizeMode) {
fmt.Fprintf(out, "%-10s %14s %s\n", c.Status, textutil.SignedSize(c.Delta), textutil.Sanitize(c.Path))
}
return nil
}
func runDuplicates(ctx context.Context, cfg *Config, t *inventory.Tree) error {
var f duplicate.Finder
groups, err := f.Find(ctx, t, duplicate.Options{MinSize: 1})
if err != nil {
return err
}
out := cfg.Stdout
var wasted int64
t.RLock()
defer t.RUnlock()
for _, g := range groups {
wasted += g.Wasted()
fmt.Fprintf(out, "%s × %d (sha256 %x…)\n", textutil.Size(g.Size), len(g.Files), g.Hash[:6])
for _, id := range g.Files {
fmt.Fprintf(out, " %s\n", textutil.Sanitize(t.Path(id)))
}
}
p := f.Progress()
fmt.Fprintf(out, "\n%d verified duplicate groups, %s reclaimable (%d candidates by size, %d unreadable/changed)\n\n",
len(groups), textutil.Size(wasted), p.Candidates, p.Skipped)
return nil
}