feat: add progress bar for shredding operations
parent
1c0587409d
commit
e900c4089f
23
cmd/root.go
23
cmd/root.go
|
@ -2,6 +2,7 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/sundowndev/covermyass/v2/build"
|
||||
|
@ -10,6 +11,7 @@ import (
|
|||
"github.com/sundowndev/covermyass/v2/lib/output"
|
||||
"github.com/sundowndev/covermyass/v2/lib/shred"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RootCmdOptions struct {
|
||||
|
@ -68,9 +70,27 @@ covermyass --write -z -n 5
|
|||
a.Write(os.Stdout)
|
||||
|
||||
if opts.Write {
|
||||
output.Printf("\n")
|
||||
bar := progressbar.NewOptions64(
|
||||
-1,
|
||||
progressbar.OptionSetDescription("Shredding files..."),
|
||||
progressbar.OptionSetWriter(output.GetPrinter()),
|
||||
progressbar.OptionShowBytes(true),
|
||||
progressbar.OptionSetWidth(10),
|
||||
progressbar.OptionThrottle(65*time.Millisecond),
|
||||
progressbar.OptionShowCount(),
|
||||
progressbar.OptionOnCompletion(func() {
|
||||
_, _ = fmt.Fprint(output.GetPrinter(), "\n")
|
||||
}),
|
||||
progressbar.OptionSpinnerType(11),
|
||||
progressbar.OptionFullWidth(),
|
||||
progressbar.OptionSetRenderBlankState(true),
|
||||
)
|
||||
|
||||
shredOptions := &shred.ShredderOptions{
|
||||
Zero: opts.Zero,
|
||||
Iterations: opts.Iterations,
|
||||
Bar: bar,
|
||||
}
|
||||
s := shred.New(shredOptions)
|
||||
for _, result := range a.Results() {
|
||||
|
@ -81,7 +101,8 @@ covermyass --write -z -n 5
|
|||
return fmt.Errorf("error writing file %s: %s", result.Path, err)
|
||||
}
|
||||
}
|
||||
output.Printf("\nShredded %d files %d times\n", len(a.Results()), opts.Iterations)
|
||||
_ = bar.Finish()
|
||||
output.Printf("\nSuccessfully shredded %d files %d times\n", len(a.Results()), opts.Iterations)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
7
go.mod
7
go.mod
|
@ -4,6 +4,7 @@ go 1.18
|
|||
|
||||
require (
|
||||
github.com/bmatcuk/doublestar/v4 v4.2.0
|
||||
github.com/schollz/progressbar/v3 v3.13.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.6.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
|
@ -12,9 +13,13 @@ require (
|
|||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.3 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
golang.org/x/sys v0.4.0 // indirect
|
||||
golang.org/x/term v0.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
18
go.sum
18
go.sum
|
@ -6,9 +6,20 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
|
||||
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/schollz/progressbar/v3 v3.13.0 h1:9TeeWRcjW2qd05I8Kf9knPkW4vLM/hYoa6z9ABvxje8=
|
||||
github.com/schollz/progressbar/v3 v3.13.0/go.mod h1:ZBYnSuLAX2LU8P8UiKN/KgF2DY58AJC8yfVYLPC8Ly4=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI=
|
||||
|
@ -19,13 +30,18 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -8,10 +8,15 @@ import (
|
|||
type Printer interface {
|
||||
Printf(string, ...interface{})
|
||||
Println(string)
|
||||
Write([]byte) (int, error)
|
||||
}
|
||||
|
||||
var globalPrinter Printer = &VoidPrinter{}
|
||||
|
||||
func GetPrinter() Printer {
|
||||
return globalPrinter
|
||||
}
|
||||
|
||||
func ChangePrinter(printer Printer) {
|
||||
globalPrinter = printer
|
||||
}
|
||||
|
@ -38,8 +43,16 @@ func (c *ConsolePrinter) Println(format string) {
|
|||
_, _ = fmt.Fprintln(os.Stdout, format)
|
||||
}
|
||||
|
||||
func (c *ConsolePrinter) Write(b []byte) (int, error) {
|
||||
return os.Stdout.Write(b)
|
||||
}
|
||||
|
||||
type VoidPrinter struct{}
|
||||
|
||||
func (v *VoidPrinter) Printf(_ string, _ ...interface{}) {}
|
||||
|
||||
func (v *VoidPrinter) Println(_ string) {}
|
||||
|
||||
func (v *VoidPrinter) Write(b []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
|
|
@ -26,9 +26,16 @@ type File interface {
|
|||
Close() error
|
||||
}
|
||||
|
||||
type ProgressBar interface {
|
||||
Add(int) error
|
||||
Write([]byte) (int, error)
|
||||
Finish() error
|
||||
}
|
||||
|
||||
type ShredderOptions struct {
|
||||
Zero bool
|
||||
Iterations int
|
||||
Bar ProgressBar
|
||||
}
|
||||
|
||||
type Shredder struct {
|
||||
|
@ -90,6 +97,9 @@ func (s *Shredder) shred(fstat FileInfo, file File) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add written bytes to progress bar
|
||||
_, _ = s.options.Bar.Write(junkBuf)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -74,7 +74,7 @@ func TestShredder_shred(t *testing.T) {
|
|||
cases := []struct {
|
||||
name string
|
||||
options ShredderOptions
|
||||
mocks func(*mocks.FileInfo, *mocks.File)
|
||||
mocks func(*mocks.FileInfo, *mocks.File, *mocks.ProgressBar)
|
||||
wantError error
|
||||
}{
|
||||
{
|
||||
|
@ -83,7 +83,7 @@ func TestShredder_shred(t *testing.T) {
|
|||
Zero: false,
|
||||
Iterations: 3,
|
||||
},
|
||||
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File) {
|
||||
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File, fakeBar *mocks.ProgressBar) {
|
||||
fakeFileInfo.On("Size").Return(int64(0)).Times(1)
|
||||
},
|
||||
},
|
||||
|
@ -93,13 +93,17 @@ func TestShredder_shred(t *testing.T) {
|
|||
Zero: false,
|
||||
Iterations: 3,
|
||||
},
|
||||
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File) {
|
||||
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File, fakeBar *mocks.ProgressBar) {
|
||||
fakeFileInfo.On("Size").Return(int64(64)).Times(2)
|
||||
|
||||
fakeFile.On("Sync").Return(nil).Times(3)
|
||||
fakeFile.On("WriteAt", mock.MatchedBy(func(b []byte) bool {
|
||||
return len(b) == 64
|
||||
}), int64(0)).Return(0, nil).Times(3)
|
||||
|
||||
fakeBar.On("Write", mock.MatchedBy(func(b []byte) bool {
|
||||
return len(b) == 64
|
||||
})).Return(1, nil).Times(3)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -108,13 +112,17 @@ func TestShredder_shred(t *testing.T) {
|
|||
Zero: false,
|
||||
Iterations: 10,
|
||||
},
|
||||
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File) {
|
||||
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File, fakeBar *mocks.ProgressBar) {
|
||||
fakeFileInfo.On("Size").Return(int64(2000000)).Times(2)
|
||||
|
||||
fakeFile.On("Sync").Return(nil).Times(10)
|
||||
fakeFile.On("WriteAt", mock.MatchedBy(func(b []byte) bool {
|
||||
return len(b) == 2000000
|
||||
}), int64(0)).Return(0, nil).Times(10)
|
||||
|
||||
fakeBar.On("Write", mock.MatchedBy(func(b []byte) bool {
|
||||
return len(b) == 2000000
|
||||
})).Return(1, nil).Times(10)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -123,7 +131,7 @@ func TestShredder_shred(t *testing.T) {
|
|||
Zero: false,
|
||||
Iterations: 3,
|
||||
},
|
||||
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File) {
|
||||
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File, fakeBar *mocks.ProgressBar) {
|
||||
fakeFileInfo.On("Size").Return(int64(2000)).Times(2)
|
||||
|
||||
fakeFile.On("WriteAt", mock.MatchedBy(func(b []byte) bool {
|
||||
|
@ -136,11 +144,14 @@ func TestShredder_shred(t *testing.T) {
|
|||
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fakeBar := &mocks.ProgressBar{}
|
||||
tt.options.Bar = fakeBar
|
||||
|
||||
s := New(&tt.options)
|
||||
|
||||
fakeFileInfo := &mocks.FileInfo{}
|
||||
fakeFile := &mocks.File{}
|
||||
tt.mocks(fakeFileInfo, fakeFile)
|
||||
tt.mocks(fakeFileInfo, fakeFile, fakeBar)
|
||||
|
||||
err := s.shred(fakeFileInfo, fakeFile)
|
||||
if tt.wantError == nil {
|
||||
|
@ -151,6 +162,7 @@ func TestShredder_shred(t *testing.T) {
|
|||
|
||||
fakeFileInfo.AssertExpectations(t)
|
||||
fakeFile.AssertExpectations(t)
|
||||
fakeBar.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
// Code generated by mockery v2.16.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
// ProgressBar is an autogenerated mock type for the ProgressBar type
|
||||
type ProgressBar struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Add provides a mock function with given fields: _a0
|
||||
func (_m *ProgressBar) Add(_a0 int) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(int) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Finish provides a mock function with given fields:
|
||||
func (_m *ProgressBar) Finish() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Write provides a mock function with given fields: _a0
|
||||
func (_m *ProgressBar) Write(_a0 []byte) (int, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func([]byte) int); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func([]byte) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewProgressBar interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// NewProgressBar creates a new instance of ProgressBar. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewProgressBar(t mockConstructorTestingTNewProgressBar) *ProgressBar {
|
||||
mock := &ProgressBar{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
Loading…
Reference in New Issue