axiom-checker/display.go
2026-01-21 04:17:41 +02:00

176 lines
3.7 KiB
Go

package main
import (
"fmt"
"sync"
"time"
)
// ANSI color codes
const (
ColorReset = "\033[0m"
ColorRed = "\033[31m"
ColorGreen = "\033[32m"
ColorYellow = "\033[33m"
ColorBlue = "\033[34m"
ColorPurple = "\033[35m"
ColorCyan = "\033[36m"
ColorWhite = "\033[37m"
ColorBold = "\033[1m"
)
// ProgressTracker tracks checking progress
type ProgressTracker struct {
Total int
Checked int
WithBalance int
TotalValue float64
StartTime time.Time
mu sync.Mutex
}
// NewProgressTracker creates a new progress tracker
func NewProgressTracker(total int) *ProgressTracker {
return &ProgressTracker{
Total: total,
StartTime: time.Now(),
}
}
// Increment increments progress counters
func (pt *ProgressTracker) Increment(balance float64) {
pt.mu.Lock()
defer pt.mu.Unlock()
pt.Checked++
pt.TotalValue += balance
if balance > 0 {
pt.WithBalance++
}
}
// GetStats returns current statistics
func (pt *ProgressTracker) GetStats() (int, int, int, float64, time.Duration) {
pt.mu.Lock()
defer pt.mu.Unlock()
elapsed := time.Since(pt.StartTime)
return pt.Total, pt.Checked, pt.WithBalance, pt.TotalValue, elapsed
}
// CalculateETA calculates estimated time to completion
func (pt *ProgressTracker) CalculateETA() string {
pt.mu.Lock()
defer pt.mu.Unlock()
if pt.Checked == 0 {
return "calculating..."
}
elapsed := time.Since(pt.StartTime)
avgTimePerKey := elapsed / time.Duration(pt.Checked)
remaining := pt.Total - pt.Checked
eta := avgTimePerKey * time.Duration(remaining)
if eta < time.Second {
return "< 1s"
} else if eta < time.Minute {
return fmt.Sprintf("%ds", int(eta.Seconds()))
} else if eta < time.Hour {
mins := int(eta.Minutes())
secs := int(eta.Seconds()) % 60
return fmt.Sprintf("%dm %ds", mins, secs)
} else {
hours := int(eta.Hours())
mins := int(eta.Minutes()) % 60
return fmt.Sprintf("%dh %dm", hours, mins)
}
}
type StatusDisplay struct {
tracker *ProgressTracker
stopChan chan bool
wg sync.WaitGroup
}
// NewStatusDisplay creates new status display
func NewStatusDisplay(tracker *ProgressTracker) *StatusDisplay {
return &StatusDisplay{
tracker: tracker,
stopChan: make(chan bool),
}
}
// Start starts the display update loop
func (sd *StatusDisplay) Start() {
sd.wg.Add(1)
go sd.updateLoop()
}
// Stop stops the display
func (sd *StatusDisplay) Stop() {
sd.stopChan <- true
sd.wg.Wait()
sd.clearLine()
}
func (sd *StatusDisplay) clearLine() {
fmt.Print("\r\033[K")
}
func (sd *StatusDisplay) updateLoop() {
defer sd.wg.Done()
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-sd.stopChan:
return
case <-ticker.C:
sd.update()
}
}
}
func (sd *StatusDisplay) update() {
total, checked, withBalance, totalValue, _ := sd.tracker.GetStats()
eta := sd.tracker.CalculateETA()
percentage := 0
if total > 0 {
percentage = (checked * 100) / total
}
progressColor := ColorYellow
if percentage >= 100 {
progressColor = ColorGreen
} else if percentage >= 75 {
progressColor = ColorCyan
}
status := fmt.Sprintf("%s%s[%d%%]%s %s%d/%d%s checked | %s%d%s with balance | %s$%.2f%s total | ETA: %s%s%s",
ColorBold, progressColor, percentage, ColorReset,
ColorCyan, checked, total, ColorReset,
ColorGreen, withBalance, ColorReset,
ColorYellow, totalValue, ColorReset,
ColorPurple, eta, ColorReset,
)
sd.clearLine()
fmt.Print("\r" + status)
}
func ColorPrint(color, text string) {
fmt.Print(color + text + ColorReset)
}
func ColorPrintln(color, text string) {
fmt.Println(color + text + ColorReset)
}
func colorPrint(color, text string) {
fmt.Print(color + text + ColorReset)
}
func colorPrintln(color, text string) {
fmt.Println(color + text + ColorReset)
}