Skip to content

Commit

Permalink
Merge pull request #29 from thushan/report-output
Browse files Browse the repository at this point in the history
Refactor App Run and tweaking reporting
  • Loading branch information
thushan authored Nov 24, 2023
2 parents 005cbfb + 70d6e55 commit 2850a34
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 77 deletions.
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ require (
github.com/alphadose/haxmap v1.3.0
github.com/cespare/xxhash v1.1.0
github.com/dustin/go-humanize v1.0.1
github.com/pterm/pterm v0.12.70
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72
github.com/pterm/pterm v0.12.71
github.com/spaolacci/murmur3 v1.1.0
github.com/spf13/cobra v1.8.0
github.com/thediveo/enumflag/v2 v2.0.5
golang.org/x/term v0.14.0
Expand All @@ -26,7 +26,7 @@ require (
github.com/rivo/uniseg v0.4.4 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
)
11 changes: 6 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
github.com/pterm/pterm v0.12.70 h1:8W0oBICz0xXvUeB8v9Pcfr2wNtsm7zfSb+FJzIbFB5w=
github.com/pterm/pterm v0.12.70/go.mod h1:SUAcoZjRt+yjPWlWba+/Fd8zJJ2lSXBQWf0Z0HbFiIQ=
github.com/pterm/pterm v0.12.71 h1:KcEJ98EiVCbzDkFbktJ2gMlr4pn8IzyGb9bwK6ffkuA=
github.com/pterm/pterm v0.12.71/go.mod h1:SUAcoZjRt+yjPWlWba+/Fd8zJJ2lSXBQWf0Z0HbFiIQ=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
Expand All @@ -80,8 +80,9 @@ github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
Expand All @@ -102,8 +103,8 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJu
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
Expand Down
3 changes: 2 additions & 1 deletion internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ var (
Use: "smash [flags] [locations-to-smash]",
Short: "Find duplicates fast!",
Long: "",
Version: smash.Version,
SilenceUsage: true,
RunE: runE,
}
Expand All @@ -42,8 +41,10 @@ func init() {
flags.IntVarP(&af.MaxWorkers, "max-workers", "w", bestMaxWorkers(), "Maximum workers to utilise when smashing.")
flags.BoolVarP(&af.DisableSlicing, "disable-slicing", "", false, "Disable slicing (hashes full file).")
flags.BoolVarP(&af.IgnoreEmptyFiles, "ignore-emptyfiles", "", false, "Ignore & don't report on empty/zero byte files.")
flags.StringVarP(&af.OutputFile, "output-file", "o", "", "Export as JSON")
flags.BoolVarP(&af.Silent, "silent", "q", false, "Run in silent mode.")
flags.BoolVarP(&af.Verbose, "verbose", "", false, "Run in verbose mode.")
flags.BoolVarP(&af.ShowVersion, "version", "v", false, "Show version information.")
}
func bestMaxWorkers() int {
cpus := runtime.NumCPU()
Expand Down
6 changes: 6 additions & 0 deletions internal/report/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package report

type ReportOutput struct {
Summary *RunSummary
Duplicates []SmashFile
}
95 changes: 62 additions & 33 deletions internal/smash/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import (
"sync/atomic"
"time"

"github.com/thushan/smash/internal/report"

"golang.org/x/term"

"github.com/thushan/smash/internal/report"

"github.com/pterm/pterm"
"github.com/thushan/smash/internal/theme"

Expand All @@ -25,6 +25,8 @@ import (
type App struct {
Flags *Flags
Session *AppSession
Runtime *AppRuntime
Summary *report.RunSummary
Args []string
Locations []string
}
Expand All @@ -35,48 +37,62 @@ type AppSession struct {
StartTime int64
EndTime int64
}
type AppRuntime struct {
Slicer *slicer.Slicer
SlicerOptions *slicer.SlicerOptions
IndexerConfig *indexer.IndexerConfig
Files chan indexer.FileFS
}

func (app *App) Run() error {

var emptyFiles []report.SmashFile
if !app.Flags.Silent {
PrintVersionInfo(app.Flags.ShowVersion)
if app.Flags.ShowVersion {
return nil
}
app.printConfiguration()
}

session := AppSession{
app.Session = &AppSession{
Dupes: haxmap.New[string, []report.SmashFile](),
Fails: haxmap.New[string, error](),
Empty: &emptyFiles,
Empty: &[]report.SmashFile{},
StartTime: time.Now().UnixMilli(),
EndTime: -1,
}
app.Session = &session
app.setMaxThreads()

locations := app.Locations
excludeDirs := app.Flags.ExcludeDir
excludeFiles := app.Flags.ExcludeFile
disableSlicing := app.Flags.DisableSlicing

updateTicker := int64(1000)

if !term.IsTerminal(int(os.Stdout.Fd())) {
pterm.DisableColor()
pterm.DisableStyling()
sl := slicer.New(algorithms.Algorithm(app.Flags.Algorithm))
wk := indexer.NewConfigured(app.Flags.ExcludeDir, app.Flags.ExcludeFile)
slo := slicer.SlicerOptions{
DisableSlicing: app.Flags.DisableSlicing,
DisableMeta: false, // TODO: Flag this
DisableFileDetection: false, // TODO: Flag this
}

if !app.Flags.Silent {
PrintVersionInfo(false)
app.printConfiguration()
app.Runtime = &AppRuntime{
Slicer: &sl,
SlicerOptions: &slo,
IndexerConfig: wk,
Files: make(chan indexer.FileFS),
}

files := make(chan indexer.FileFS)
app.setMaxThreads()
app.checkTerminal()

sl := slicer.New(algorithms.Algorithm(app.Flags.Algorithm))
wk := indexer.NewConfigured(excludeDirs, excludeFiles)
return app.Exec()
}
func (app *App) Exec() error {

slo := slicer.SlicerOptions{
DisableSlicing: disableSlicing,
DisableMeta: false, // TODO: Flag this
DisableFileDetection: false, // TODO: Flag this
}
session := app.Session

wk := app.Runtime.IndexerConfig
sl := app.Runtime.Slicer
slo := app.Runtime.SlicerOptions

files := app.Runtime.Files
locations := app.Locations
isVerbose := app.Flags.Verbose && !app.Flags.Silent

pap := theme.MultiWriter()
psi, _ := theme.IndexingSpinner().WithWriter(pap.NewWriter()).Start("Indexing locations...")
Expand All @@ -92,7 +108,7 @@ func (app *App) Run() error {
err := wk.WalkDirectory(os.DirFS(location), location, files)

if err != nil {
if app.Flags.Verbose {
if isVerbose {
theme.WarnSkipWithContext(location, err)
}
_, _ = session.Fails.GetOrSet(location, err)
Expand All @@ -101,6 +117,7 @@ func (app *App) Run() error {
}()

totalFiles := int64(0)
updateTicker := int64(1000)

pss, _ := theme.SmashingSpinner().WithWriter(pap.NewWriter()).Start("Finding duplicates...")

Expand All @@ -119,11 +136,11 @@ func (app *App) Run() error {
}

startTime := time.Now().UnixMilli()
stats, err := sl.SliceFS(file.FileSystem, file.Path, &slo)
stats, err := sl.SliceFS(file.FileSystem, file.Path, slo)
elapsedMs := time.Now().UnixMilli() - startTime

if err != nil {
if app.Flags.Verbose {
if isVerbose {
theme.WarnSkipWithContext(file.FullName, err)
}
_, _ = session.Fails.GetOrSet(sf, err)
Expand All @@ -136,10 +153,22 @@ func (app *App) Run() error {
wg.Wait()

pss.Success("Finding duplicates...Done!")

psr, _ := theme.FinaliseSpinner().WithWriter(pap.NewWriter()).Start("Finding smash hits...")
app.generateRunSummary(totalFiles)
psr.Success("Finding smash hits...Done!")

pap.Stop()

summary := app.generateRunSummary(totalFiles)
report.PrintRunSummary(summary, app.Flags.IgnoreEmptyFiles)
app.PrintRunAnalysis(app.Flags.IgnoreEmptyFiles)
report.PrintRunSummary(*app.Summary, app.Flags.IgnoreEmptyFiles)

return nil
}

func (app *App) checkTerminal() {
if !term.IsTerminal(int(os.Stdout.Fd())) {
pterm.DisableColor()
pterm.DisableStyling()
}
}
75 changes: 48 additions & 27 deletions internal/smash/formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,53 @@ func (app *App) printVerbose(message ...any) {
}
}

func (app *App) PrintRunAnalysis(ignoreEmptyFiles bool) {
duplicates := app.Session.Dupes
emptyFiles := *app.Session.Empty

totalDuplicates := app.Summary.DuplicateFiles

theme.StyleHeading.Println("---| Duplicates (", totalDuplicates, ")")

if duplicates.Len() == 0 {
theme.Println(theme.ColourSuccess("No duplicates found :-)"))
} else {

duplicates.ForEach(func(hash string, files []report.SmashFile) bool {
duplicateFiles := len(files) - 1
if duplicateFiles != 0 {
root := files[0]
dupes := files[1:]
theme.Println(theme.ColourFilename(root.Filename), " ", theme.ColourFileSize(root.FileSizeF), " ", theme.ColourHash(root.Hash))
printSmashHits(dupes)
}
return true
})

}

if !ignoreEmptyFiles && len(emptyFiles) != 0 {
theme.StyleHeading.Println("---| Empty Files (", len(emptyFiles), ")")
printSmashHits(emptyFiles)
}

}

func printSmashHits(files []report.SmashFile) {
lastIndex := len(files) - 1
for index, file := range files {
var subTree string
if index < lastIndex {
subTree = TreeNextChild
} else {
subTree = TreeLastChild
}
theme.Println(theme.ColourFolderHierarchy(subTree), theme.ColourFilenameA(file.Filename))
}
}

// generateRunSummary Generates the smash hits of duplicates and returns the total size of duplicates.
func (app *App) generateRunSummary(totalFiles int64) report.RunSummary {
func (app *App) generateRunSummary(totalFiles int64) {
session := *app.Session
duplicates := session.Dupes
emptyFiles := *session.Empty
Expand All @@ -32,7 +77,6 @@ func (app *App) generateRunSummary(totalFiles int64) report.RunSummary {
totalFailFileCount := int64(session.Fails.Len())
totalEmptyFileCount := int64(len(emptyFiles))

theme.StyleHeading.Println("---| Duplicates")
duplicates.ForEach(func(hash string, files []report.SmashFile) bool {
duplicateFiles := len(files) - 1
if duplicateFiles == 0 {
Expand All @@ -41,24 +85,13 @@ func (app *App) generateRunSummary(totalFiles int64) report.RunSummary {
} else {
root := files[0]
dupes := files[1:]
theme.Println(theme.ColourFilename(root.Filename), " ", theme.ColourFileSize(humanize.Bytes(root.FileSize)), " ", theme.ColourHash(root.Hash))
printSmashHits(dupes)
totalDuplicates += len(dupes)
totalDuplicateSize += root.FileSize * uint64(duplicateFiles)
}
return true
})

if totalDuplicates == 0 {
theme.Println(theme.ColourSuccess("No duplicates found :-)"))
}

if !app.Flags.IgnoreEmptyFiles && totalEmptyFileCount != 0 {
theme.StyleHeading.Println("---| Empty Files (", totalEmptyFileCount, ")")
printSmashHits(emptyFiles)
}

return report.RunSummary{
summary := report.RunSummary{
TotalFiles: totalFiles,
TotalFileErrors: totalFailFileCount,
UniqueFiles: totalUniqueFiles,
Expand All @@ -68,17 +101,5 @@ func (app *App) generateRunSummary(totalFiles int64) report.RunSummary {
DuplicateFileSizeF: humanize.Bytes(totalDuplicateSize),
ElapsedTime: time.Now().UnixMilli() - app.Session.StartTime,
}
}

func printSmashHits(files []report.SmashFile) {
lastIndex := len(files) - 1
for index, file := range files {
var subTree string
if index < lastIndex {
subTree = TreeNextChild
} else {
subTree = TreeLastChild
}
theme.Println(theme.ColourFolderHierarchy(subTree), theme.ColourFilenameA(file.Filename))
}
app.Summary = &summary
}
2 changes: 2 additions & 0 deletions internal/smash/structs.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package smash

type Flags struct {
OutputFile string `yaml:"output"`
Base []string `yaml:"base"`
ExcludeDir []string `yaml:"exclude-dir"`
ExcludeFile []string `yaml:"exclude-file"`
Expand All @@ -9,6 +10,7 @@ type Flags struct {
MaxWorkers int `yaml:"max-workers"`
DisableSlicing bool `yaml:"disable-slicing"`
IgnoreEmptyFiles bool `yaml:"ignore-emptyfiles"`
ShowVersion bool `yaml:"show-version"`
Silent bool `yaml:"silent"`
Verbose bool `yaml:"verbose"`
}
Loading

0 comments on commit 2850a34

Please sign in to comment.