-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodel.go
More file actions
224 lines (193 loc) · 4.01 KB
/
model.go
File metadata and controls
224 lines (193 loc) · 4.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
package main
import (
"os"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/dariubs/socool/scanner"
)
type appState int
const (
stateMenu appState = iota
stateRunning
stateResult
stateConfirmDelete
)
type mode int
const (
modeFiles mode = iota
modeDirs
modeDups
)
type (
tickMsg time.Time
filesResultMsg struct {
files []scanner.FileEntry
err error
}
dirsResultMsg struct {
dirs []scanner.FileEntry
err error
}
dupResultMsg struct {
groups []scanner.DupGroup
err error
}
)
type menuItem struct {
label string
description string
}
var menuItems = []menuItem{
{"Biggest Files", "Find the largest files on your system"},
{"Largest Dirs", "Find the largest directories on your system"},
{"Duplicate Files", "Find duplicate files and wasted space"},
}
type model struct {
state appState
mode mode
cursor int
listCursor int
shineTick int
result []scanner.FileEntry
dupResult []scanner.DupGroup
err error
statusMsg string
}
func initialModel() model { return model{} }
func tickCmd() tea.Cmd {
return tea.Tick(110*time.Millisecond, func(t time.Time) tea.Msg {
return tickMsg(t)
})
}
func (m model) Init() tea.Cmd { return tickCmd() }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c":
return m, tea.Quit
case "q", "esc":
switch m.state {
case stateMenu:
return m, tea.Quit
case stateResult:
m.state = stateMenu
m.result = nil
m.dupResult = nil
m.err = nil
m.statusMsg = ""
m.listCursor = 0
case stateConfirmDelete:
m.state = stateResult
}
case "up", "k":
switch m.state {
case stateMenu:
if m.cursor > 0 {
m.cursor--
}
case stateResult:
if m.listCursor > 0 {
m.listCursor--
}
}
case "down", "j":
switch m.state {
case stateMenu:
if m.cursor < len(menuItems)-1 {
m.cursor++
}
case stateResult:
limit := len(m.result) - 1
if m.mode == modeDups {
limit = len(m.dupResult) - 1
}
if m.listCursor < limit {
m.listCursor++
}
}
case "enter":
switch m.state {
case stateMenu:
return m.startScan()
case stateResult:
if m.mode == modeFiles && len(m.result) > 0 {
m.statusMsg = ""
m.state = stateConfirmDelete
}
}
case " ":
if m.state == stateMenu {
return m.startScan()
}
case "y", "Y":
if m.state == stateConfirmDelete {
path := m.result[m.listCursor].Path
if err := os.Remove(path); err != nil {
m.statusMsg = "Delete failed: " + err.Error()
} else {
m.result = append(m.result[:m.listCursor], m.result[m.listCursor+1:]...)
if m.listCursor >= len(m.result) && m.listCursor > 0 {
m.listCursor--
}
m.statusMsg = ""
}
m.state = stateResult
}
case "n", "N":
if m.state == stateConfirmDelete {
m.state = stateResult
}
}
case tickMsg:
m.shineTick++
return m, tickCmd()
case filesResultMsg:
m.state = stateResult
m.result = msg.files
m.err = msg.err
case dirsResultMsg:
m.state = stateResult
m.result = msg.dirs
m.err = msg.err
case dupResultMsg:
m.state = stateResult
m.dupResult = msg.groups
m.err = msg.err
}
return m, nil
}
func (m model) startScan() (model, tea.Cmd) {
m.state = stateRunning
m.listCursor = 0
switch m.cursor {
case 0:
m.mode = modeFiles
return m, scanFiles()
case 1:
m.mode = modeDirs
return m, scanDirs()
case 2:
m.mode = modeDups
return m, scanDups()
}
return m, nil
}
func scanFiles() tea.Cmd {
return func() tea.Msg {
files, err := scanner.FindLargestFiles("/", scanner.TopN)
return filesResultMsg{files: files, err: err}
}
}
func scanDirs() tea.Cmd {
return func() tea.Msg {
dirs, err := scanner.FindLargestDirs("/", scanner.TopN)
return dirsResultMsg{dirs: dirs, err: err}
}
}
func scanDups() tea.Cmd {
return func() tea.Msg {
groups, err := scanner.FindDuplicateFiles("/", scanner.TopN)
return dupResultMsg{groups: groups, err: err}
}
}