Skip to content

Commit e08f852

Browse files
committed
feat: improve UI exp for rescan command
1 parent 0630605 commit e08f852

2 files changed

Lines changed: 71 additions & 30 deletions

File tree

cmd/rescan.go

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
package cmd
66

77
import (
8+
"fmt"
89
"log"
910
"regexp"
1011
"strings"
11-
"sync"
12-
"time"
1312

13+
tea "github.com/charmbracelet/bubbletea"
1414
"github.com/saferwall/cli/internal/util"
1515
"github.com/saferwall/cli/internal/webapi"
1616
"github.com/spf13/cobra"
@@ -29,25 +29,13 @@ func init() {
2929
"Preferred OS for detonation, choice(win-7 | win-10)")
3030
}
3131

32-
// reScanFile re-scans a list of SHA256.
32+
// reScanFile re-scans a list of SHA256 with a TUI progress display.
3333
func reScanFile(web webapi.Service, shaList []string, token string) error {
34-
sem := make(chan struct{}, parallelFlag)
35-
var wg sync.WaitGroup
36-
37-
for _, sha256 := range shaList {
38-
sem <- struct{}{}
39-
wg.Add(1)
40-
go func() {
41-
defer func() { <-sem; wg.Done() }()
42-
log.Printf("rescanning %s", sha256)
43-
err := web.Rescan(sha256, token, osFlag, enableDetonationFlag, timeoutFlag)
44-
if err != nil {
45-
log.Printf("failed to rescan file: %v", sha256)
46-
}
47-
time.Sleep(2 * time.Second)
48-
}()
34+
model := newRescanModel(shaList, web, token, parallelFlag)
35+
p := tea.NewProgram(model)
36+
if _, err := p.Run(); err != nil {
37+
return fmt.Errorf("TUI error: %w", err)
4938
}
50-
wg.Wait()
5139
return nil
5240
}
5341

cmd/scanui.go

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type scanModel struct {
4646
token string
4747
parallel int
4848
done bool
49+
isRescan bool // true when running in rescan mode (no upload, just rescan API + poll)
4950
}
5051

5152
// --- Messages ---
@@ -130,6 +131,16 @@ func delayedPollCmd(index int, web webapi.Service, sha256 string) tea.Cmd {
130131
})
131132
}
132133

134+
func rescanFileCmd(index int, web webapi.Service, sha256, token string) tea.Cmd {
135+
return func() tea.Msg {
136+
err := web.Rescan(sha256, token, osFlag, enableDetonationFlag, timeoutFlag)
137+
if err != nil {
138+
return fileUploadedMsg{index: index, err: fmt.Errorf("rescan: %w", err)}
139+
}
140+
return fileUploadedMsg{index: index, sha256: sha256}
141+
}
142+
}
143+
133144
// --- Model interface ---
134145

135146
func newScanModel(files []string, web webapi.Service, token string, parallel int) scanModel {
@@ -154,20 +165,51 @@ func newScanModel(files []string, web webapi.Service, token string, parallel int
154165
}
155166
}
156167

168+
func newRescanModel(sha256List []string, web webapi.Service, token string, parallel int) scanModel {
169+
if parallel < 1 {
170+
parallel = 1
171+
}
172+
rows := make([]fileRow, len(sha256List))
173+
for i, sha := range sha256List {
174+
s := spinner.New()
175+
s.Spinner = spinner.Dot
176+
rows[i] = fileRow{
177+
filename: sha,
178+
sha256: sha,
179+
state: statePending,
180+
spinner: s,
181+
}
182+
}
183+
return scanModel{
184+
files: rows,
185+
web: web,
186+
token: token,
187+
parallel: parallel,
188+
isRescan: true,
189+
}
190+
}
191+
157192
func (m scanModel) Init() tea.Cmd {
158193
if len(m.files) == 0 {
159194
return tea.Quit
160195
}
161196

162-
// Launch up to m.parallel uploads concurrently.
197+
// Launch up to m.parallel operations concurrently.
163198
n := min(m.parallel, len(m.files))
164199
var cmds []tea.Cmd
165200
for i := range n {
166201
m.files[i].state = stateUploading
167-
cmds = append(cmds,
168-
uploadFileCmd(i, m.web, m.files[i].filename, m.token),
169-
m.files[i].spinner.Tick,
170-
)
202+
if m.isRescan {
203+
cmds = append(cmds,
204+
rescanFileCmd(i, m.web, m.files[i].sha256, m.token),
205+
m.files[i].spinner.Tick,
206+
)
207+
} else {
208+
cmds = append(cmds,
209+
uploadFileCmd(i, m.web, m.files[i].filename, m.token),
210+
m.files[i].spinner.Tick,
211+
)
212+
}
171213
}
172214
return tea.Batch(cmds...)
173215
}
@@ -260,10 +302,17 @@ func (m *scanModel) maybeQuitOrNext() tea.Cmd {
260302
}
261303
if m.files[i].state == statePending {
262304
m.files[i].state = stateUploading
263-
cmds = append(cmds,
264-
uploadFileCmd(i, m.web, m.files[i].filename, m.token),
265-
m.files[i].spinner.Tick,
266-
)
305+
if m.isRescan {
306+
cmds = append(cmds,
307+
rescanFileCmd(i, m.web, m.files[i].sha256, m.token),
308+
m.files[i].spinner.Tick,
309+
)
310+
} else {
311+
cmds = append(cmds,
312+
uploadFileCmd(i, m.web, m.files[i].filename, m.token),
313+
m.files[i].spinner.Tick,
314+
)
315+
}
267316
inFlight++
268317
}
269318
}
@@ -292,11 +341,15 @@ func (m scanModel) View() string {
292341
s += styleDim.Render(" "+name) + "\n"
293342

294343
case stateUploading:
295-
s += f.spinner.View() + styleLabel.Render(" Uploading ") + name + " ...\n"
344+
label := " Uploading "
345+
if m.isRescan {
346+
label = " Rescanning "
347+
}
348+
s += f.spinner.View() + styleLabel.Render(label) + name + " ...\n"
296349

297350
case stateScanning:
298351
sha := truncSha(f.sha256)
299-
s += f.spinner.View() + styleLabel.Render(" Scanning ") + name + " " + styleDim.Render(sha) + "\n"
352+
s += f.spinner.View() + styleLabel.Render(" Scanning ") + name + " " + styleDim.Render(sha) + "\n"
300353

301354
case stateDone:
302355
sha := truncSha(f.sha256)

0 commit comments

Comments
 (0)