Documentation
¶
Overview ¶
Package progress provides a flexible progress reporting system with handler-based architecture for composable output handling (logging, console, Bubble Tea UI).
The package follows ADR-012 (Structured Output and Shadow Logging) by ensuring all progress events are logged to the audit stream while providing visual feedback through the status stream.
Index ¶
- func IsInteractive(w io.Writer) bool
- func IsStderrInteractive() bool
- func IsStdoutInteractive() bool
- type CompositeHandler
- type ConsoleHandler
- type Event
- func (e Event) IsIndeterminate() bool
- func (e Event) Percentage() float64
- func (e Event) WithError(err error) Event
- func (e Event) WithMeta(key string, value interface{}) Event
- func (e Event) WithPhase(phase string) Event
- func (e Event) WithProgress(current, total int64) Event
- func (e Event) WithTask(task string) Event
- type EventType
- type Handler
- type HandlerFunc
- type LogHandler
- type LogHandlerOption
- type MockHandler
- func (h *MockHandler) EventCount() int
- func (h *MockHandler) EventsOfType(t EventType) []Event
- func (h *MockHandler) GetEvents() []Event
- func (h *MockHandler) HasEventWithMessage(message string) bool
- func (h *MockHandler) LastEvent() (Event, bool)
- func (h *MockHandler) OnProgress(_ context.Context, event Event)
- func (h *MockHandler) Reset()
- type NopHandler
- type Option
- type Reporter
- func (r *Reporter) Complete(ctx context.Context, message string)
- func (r *Reporter) Emit(ctx context.Context, event Event)
- func (r *Reporter) Error(ctx context.Context, err error, message string)
- func (r *Reporter) Phase() string
- func (r *Reporter) Progress(ctx context.Context, current, total int64, message string)
- func (r *Reporter) ProgressFunc(ctx context.Context, message string) func(current, total int64)
- func (r *Reporter) SetHandler(h Handler)
- func (r *Reporter) SetPhase(phase string)
- func (r *Reporter) Start(ctx context.Context, message string)
- func (r *Reporter) Warning(ctx context.Context, message string)
- type Style
- type TeaHandler
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsInteractive ¶
IsInteractive determines if the given writer is an interactive terminal. This is used to decide whether to use Bubble Tea (interactive) or simple console output (non-interactive).
Returns false for: - Piped output - Redirected output - Non-file writers (like bytes.Buffer)
Per ADR-012, this enables automatic switching between: - Interactive TTY: Bubble Tea with animations - Non-interactive (CI/piped): Simple line-based output
func IsStderrInteractive ¶
func IsStderrInteractive() bool
IsStderrInteractive checks if stderr is an interactive terminal. This is a convenience function since progress typically outputs to stderr.
func IsStdoutInteractive ¶
func IsStdoutInteractive() bool
IsStdoutInteractive checks if stdout is an interactive terminal.
Types ¶
type CompositeHandler ¶
type CompositeHandler struct {
// contains filtered or unexported fields
}
CompositeHandler combines multiple handlers into one. Events are dispatched to all handlers sequentially. This enables patterns like: log + render + metrics simultaneously.
Example:
handler := NewCompositeHandler(
NewLogHandler(), // Shadow logging (always)
NewConsoleHandler(os.Stderr), // Simple output
)
func NewCompositeHandler ¶
func NewCompositeHandler(handlers ...Handler) *CompositeHandler
NewCompositeHandler creates a new CompositeHandler with the given handlers.
func (*CompositeHandler) Add ¶
func (h *CompositeHandler) Add(handler Handler)
Add adds a handler to the composite (thread-safe).
func (*CompositeHandler) Handlers ¶
func (h *CompositeHandler) Handlers() []Handler
Handlers returns a copy of the handlers slice (thread-safe).
func (*CompositeHandler) Len ¶
func (h *CompositeHandler) Len() int
Len returns the number of handlers (thread-safe).
func (*CompositeHandler) OnProgress ¶
func (h *CompositeHandler) OnProgress(ctx context.Context, event Event)
OnProgress implements Handler by dispatching to all handlers.
func (*CompositeHandler) RemoveAt ¶
func (h *CompositeHandler) RemoveAt(index int) bool
RemoveAt removes a handler at the given index (thread-safe). Returns true if the handler was removed, false if index out of bounds.
type ConsoleHandler ¶
type ConsoleHandler struct {
// contains filtered or unexported fields
}
ConsoleHandler writes simple progress output to a writer (typically stderr). This provides non-interactive progress feedback for terminals that don't support Bubble Tea or when running in CI/piped environments.
func NewConsoleHandler ¶
func NewConsoleHandler(w io.Writer) *ConsoleHandler
NewConsoleHandler creates a new ConsoleHandler writing to the given writer.
func (*ConsoleHandler) OnProgress ¶
func (h *ConsoleHandler) OnProgress(ctx context.Context, event Event)
OnProgress implements Handler by writing formatted progress to the writer. Respects context cancellation.
type Event ¶
type Event struct {
// Type indicates what kind of event this is.
Type EventType
// Phase identifies the current phase of a multi-phase operation.
// Examples: "downloading", "validating", "installing"
Phase string
// Task identifies the specific task within a phase.
// Examples: "file1.zip", "package.json"
Task string
// Current progress value (0 to Total).
Current int64
// Total expected value (for percentage calculation).
// If Total is 0, progress is indeterminate.
Total int64
// Message is a human-readable description of the current state.
Message string
// Error contains the error if Type is EventError.
Error error
// Timestamp when this event was created.
Timestamp time.Time
// Metadata contains arbitrary key-value pairs for extensibility.
// Examples: {"bytes_per_second": 1024, "eta_seconds": 30}
Metadata map[string]interface{}
}
Event represents a progress event that handlers can observe. Events are immutable once created and should be passed by value.
func NewEvent ¶
NewEvent creates a new Event with the given type and message. Timestamp is set to the current time.
func (Event) IsIndeterminate ¶
IsIndeterminate returns true if progress cannot be calculated.
func (Event) Percentage ¶
Percentage returns the progress as a percentage (0-100). Returns -1 if progress is indeterminate (Total <= 0).
func (Event) WithProgress ¶
WithProgress returns a new Event with current/total progress set.
type EventType ¶
type EventType int
EventType represents the type of progress event.
const ( // EventStart indicates the beginning of an operation. EventStart EventType = iota // EventProgress indicates progress update during an operation. EventProgress // EventComplete indicates successful completion. EventComplete // EventError indicates an error occurred. EventError // EventWarning indicates a non-fatal warning. EventWarning )
type Handler ¶
type Handler interface {
// OnProgress is called for each progress event.
// Context can be used for cancellation or deadline propagation.
OnProgress(ctx context.Context, event Event)
}
Handler is the core interface for receiving progress events. Implementations can render to terminal, log to file, emit metrics, etc.
Design follows the Observer pattern: handlers subscribe to events emitted by the Reporter, decoupling event production from consumption.
Implementations should be safe for concurrent calls from multiple goroutines.
type HandlerFunc ¶
HandlerFunc is an adapter to allow ordinary functions as handlers. This follows the same pattern as http.HandlerFunc.
func (HandlerFunc) OnProgress ¶
func (f HandlerFunc) OnProgress(ctx context.Context, event Event)
OnProgress implements Handler interface by calling the function.
type LogHandler ¶
type LogHandler struct {
// Logger to use (defaults to global log.Logger)
Logger zerolog.Logger
// Component name for log entries
Component string
}
LogHandler writes progress events to the structured log (Audit Stream). This implements ADR-012's shadow logging pattern, ensuring all progress events are recorded for debugging and auditing purposes.
func NewLogHandler ¶
func NewLogHandler(opts ...LogHandlerOption) *LogHandler
NewLogHandler creates a new LogHandler with the given options.
func (*LogHandler) OnProgress ¶
func (h *LogHandler) OnProgress(ctx context.Context, event Event)
OnProgress implements Handler by logging the event to the audit stream. Respects context cancellation.
type LogHandlerOption ¶
type LogHandlerOption func(*LogHandler)
LogHandlerOption configures a LogHandler.
func WithComponent ¶
func WithComponent(name string) LogHandlerOption
WithComponent sets the component name for log entries.
func WithLogger ¶
func WithLogger(l zerolog.Logger) LogHandlerOption
WithLogger sets a custom zerolog.Logger.
type MockHandler ¶
type MockHandler struct {
Events []Event
// contains filtered or unexported fields
}
MockHandler records all events for testing. This follows the same pattern as internal/ui/mock.go (ADR-003 compliant).
func (*MockHandler) EventCount ¶
func (h *MockHandler) EventCount() int
EventCount returns the number of recorded events.
func (*MockHandler) EventsOfType ¶
func (h *MockHandler) EventsOfType(t EventType) []Event
EventsOfType returns all events of a specific type.
func (*MockHandler) GetEvents ¶
func (h *MockHandler) GetEvents() []Event
GetEvents returns a copy of all events (thread-safe).
func (*MockHandler) HasEventWithMessage ¶
func (h *MockHandler) HasEventWithMessage(message string) bool
HasEventWithMessage checks if any event contains the given message.
func (*MockHandler) LastEvent ¶
func (h *MockHandler) LastEvent() (Event, bool)
LastEvent returns the most recent event. Returns empty Event and false if no events recorded.
func (*MockHandler) OnProgress ¶
func (h *MockHandler) OnProgress(_ context.Context, event Event)
OnProgress implements Handler by recording the event.
type NopHandler ¶
type NopHandler struct{}
NopHandler is a handler that does nothing. Useful for testing or when progress reporting should be disabled.
This implements the Null Object pattern to avoid nil checks.
func (*NopHandler) OnProgress ¶
func (h *NopHandler) OnProgress(_ context.Context, _ Event)
OnProgress implements Handler by doing nothing.
type Option ¶
type Option func(*Reporter)
Option configures a Reporter.
func WithHandler ¶
WithHandler sets the handler for the reporter.
func WithInteractive ¶
WithInteractive is a convenience option that uses stderr with the given interactivity setting.
type Reporter ¶
type Reporter struct {
// contains filtered or unexported fields
}
Reporter provides a convenient API for emitting progress events. It manages handlers and provides helper methods for common patterns.
Example usage:
reporter := progress.NewReporter(
progress.WithOutput(cmd.ErrOrStderr()),
progress.WithInteractive(useUI),
)
reporter.Start(ctx, "Processing files")
for i, file := range files {
reporter.Progress(ctx, int64(i+1), int64(len(files)), file.Name)
}
reporter.Complete(ctx, "Done")
func NewReporter ¶
NewReporter creates a new Reporter with the given options. If no handler is provided, a default non-interactive handler is used.
func (*Reporter) ProgressFunc ¶
ProgressFunc returns a function suitable for passing to operations that report progress via callback.
Example:
progressFn := reporter.ProgressFunc(ctx, "Downloading") download(url, progressFn) // calls progressFn(bytesRead, totalBytes)
func (*Reporter) SetHandler ¶
SetHandler changes the handler (thread-safe).
type Style ¶
type Style struct {
// Spinner configuration
SpinnerFrames []string
SpinnerInterval time.Duration
SpinnerStyle lipgloss.Style
// Progress bar configuration
BarWidth int
BarChar string
BarEmptyChar string
BarStyle lipgloss.Style
BarEmptyStyle lipgloss.Style
// Status styles
SuccessStyle lipgloss.Style
ErrorStyle lipgloss.Style
WarningStyle lipgloss.Style
// Text styles
PhaseStyle lipgloss.Style
TaskStyle lipgloss.Style
MessageStyle lipgloss.Style
CounterStyle lipgloss.Style
}
Style defines the visual appearance of progress elements.
func MinimalStyle ¶
func MinimalStyle() *Style
MinimalStyle returns a minimal style with fewer visual elements.
type TeaHandler ¶
type TeaHandler struct {
// contains filtered or unexported fields
}
TeaHandler renders progress events using Bubble Tea. It provides animated spinners, progress bars, and real-time updates.
This handler is designed to be used as part of a CompositeHandler, typically alongside LogHandler for shadow logging (ADR-012 compliance).
func NewTeaHandler ¶
func NewTeaHandler(out io.Writer) *TeaHandler
NewTeaHandler creates a new Bubble Tea based progress handler.
func (*TeaHandler) OnProgress ¶
func (h *TeaHandler) OnProgress(ctx context.Context, event Event)
OnProgress implements Handler by sending events to the Bubble Tea model. Respects context cancellation.