deb-bootc-image-builder-new/bib/internal/ux/progress.go
2025-09-05 07:10:12 -07:00

208 lines
5.2 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package ux
import (
"fmt"
"io"
"time"
)
// ProgressReporter handles progress reporting for long operations
type ProgressReporter struct {
writer io.Writer
verbose bool
startTime time.Time
steps []ProgressStep
current int
}
// ProgressStep represents a step in a long operation
type ProgressStep struct {
Name string
Description string
Duration time.Duration
Status StepStatus
}
// StepStatus represents the status of a progress step
type StepStatus string
const (
StepStatusPending StepStatus = "pending"
StepStatusRunning StepStatus = "running"
StepStatusCompleted StepStatus = "completed"
StepStatusFailed StepStatus = "failed"
StepStatusSkipped StepStatus = "skipped"
)
// NewProgressReporter creates a new progress reporter
func NewProgressReporter(writer io.Writer, verbose bool) *ProgressReporter {
return &ProgressReporter{
writer: writer,
verbose: verbose,
startTime: time.Now(),
steps: make([]ProgressStep, 0),
current: -1,
}
}
// AddStep adds a new step to the progress reporter
func (p *ProgressReporter) AddStep(name, description string) {
p.steps = append(p.steps, ProgressStep{
Name: name,
Description: description,
Status: StepStatusPending,
})
}
// StartStep marks the current step as running
func (p *ProgressReporter) StartStep(stepIndex int) {
if stepIndex < 0 || stepIndex >= len(p.steps) {
return
}
p.current = stepIndex
p.steps[stepIndex].Status = StepStatusRunning
if p.verbose {
fmt.Fprintf(p.writer, "🔄 Starting: %s\n", p.steps[stepIndex].Description)
} else {
fmt.Fprintf(p.writer, "🔄 %s... ", p.steps[stepIndex].Name)
}
}
// CompleteStep marks the current step as completed
func (p *ProgressReporter) CompleteStep(stepIndex int) {
if stepIndex < 0 || stepIndex >= len(p.steps) {
return
}
p.steps[stepIndex].Status = StepStatusCompleted
p.steps[stepIndex].Duration = time.Since(p.startTime)
if p.verbose {
fmt.Fprintf(p.writer, "✅ Completed: %s (took %v)\n",
p.steps[stepIndex].Description, p.steps[stepIndex].Duration)
} else {
fmt.Fprintf(p.writer, "✅\n")
}
}
// FailStep marks the current step as failed
func (p *ProgressReporter) FailStep(stepIndex int, err error) {
if stepIndex < 0 || stepIndex >= len(p.steps) {
return
}
p.steps[stepIndex].Status = StepStatusFailed
p.steps[stepIndex].Duration = time.Since(p.startTime)
if p.verbose {
fmt.Fprintf(p.writer, "❌ Failed: %s (took %v) - %v\n",
p.steps[stepIndex].Description, p.steps[stepIndex].Duration, err)
} else {
fmt.Fprintf(p.writer, "❌ (%v)\n", err)
}
}
// SkipStep marks the current step as skipped
func (p *ProgressReporter) SkipStep(stepIndex int, reason string) {
if stepIndex < 0 || stepIndex >= len(p.steps) {
return
}
p.steps[stepIndex].Status = StepStatusSkipped
if p.verbose {
fmt.Fprintf(p.writer, "⏭️ Skipped: %s - %s\n",
p.steps[stepIndex].Description, reason)
} else {
fmt.Fprintf(p.writer, "⏭️ (%s)\n", reason)
}
}
// PrintSummary prints a summary of all steps
func (p *ProgressReporter) PrintSummary() {
totalDuration := time.Since(p.startTime)
fmt.Fprintf(p.writer, "\n📊 Build Summary:\n")
fmt.Fprintf(p.writer, " Total time: %v\n", totalDuration)
fmt.Fprintf(p.writer, " Steps completed: %d/%d\n",
p.getCompletedCount(), len(p.steps))
if p.verbose {
fmt.Fprintf(p.writer, "\n📋 Step Details:\n")
for i, step := range p.steps {
status := getStatusEmoji(step.Status)
fmt.Fprintf(p.writer, " %d. %s %s", i+1, status, step.Name)
if step.Duration > 0 {
fmt.Fprintf(p.writer, " (%v)", step.Duration)
}
fmt.Fprintf(p.writer, "\n")
}
}
}
// getCompletedCount returns the number of completed steps
func (p *ProgressReporter) getCompletedCount() int {
count := 0
for _, step := range p.steps {
if step.Status == StepStatusCompleted {
count++
}
}
return count
}
// getStatusEmoji returns an emoji for the step status
func getStatusEmoji(status StepStatus) string {
switch status {
case StepStatusPending:
return "⏳"
case StepStatusRunning:
return "🔄"
case StepStatusCompleted:
return "✅"
case StepStatusFailed:
return "❌"
case StepStatusSkipped:
return "⏭️"
default:
return "❓"
}
}
// Simple progress indicators for quick operations
// PrintProgress prints a simple progress indicator
func PrintProgress(writer io.Writer, message string) {
fmt.Fprintf(writer, "🔄 %s...\n", message)
}
// PrintSuccess prints a success message
func PrintSuccess(writer io.Writer, message string) {
fmt.Fprintf(writer, "✅ %s\n", message)
}
// PrintWarning prints a warning message
func PrintWarning(writer io.Writer, message string) {
fmt.Fprintf(writer, "⚠️ %s\n", message)
}
// PrintInfo prints an info message
func PrintInfo(writer io.Writer, message string) {
fmt.Fprintf(writer, " %s\n", message)
}
// PrintError prints an error message
func PrintError(writer io.Writer, message string) {
fmt.Fprintf(writer, "❌ %s\n", message)
}
// PrintStep prints a step message with optional details
func PrintStep(writer io.Writer, step, message string, verbose bool) {
if verbose {
fmt.Fprintf(writer, "🔄 [%s] %s\n", step, message)
} else {
fmt.Fprintf(writer, "🔄 %s\n", message)
}
}