mirror of
https://github.com/krau/SaveAny-Bot.git
synced 2026-08-19 11:23:57 +08:00
89 lines
2.3 KiB
Go
89 lines
2.3 KiB
Go
// Package taskevent provides a decoupled, context-scoped event bus for task
|
|
// lifecycle progress. Producers (task implementations) emit events via Emit;
|
|
// consumers (e.g. the API progress store, the Telegram message editor) register
|
|
// as Sinks and are injected through context. This keeps the task layer free of
|
|
// any concrete progress-display dependency, so new task types gain progress
|
|
// reporting for free and new observers can be added without touching tasks.
|
|
package taskevent
|
|
|
|
import "context"
|
|
|
|
// Phase marks a stage in a task's lifecycle.
|
|
type Phase int
|
|
|
|
const (
|
|
PhaseStart Phase = iota
|
|
PhaseProgress
|
|
PhaseDone
|
|
)
|
|
|
|
func (p Phase) String() string {
|
|
switch p {
|
|
case PhaseStart:
|
|
return "start"
|
|
case PhaseProgress:
|
|
return "progress"
|
|
case PhaseDone:
|
|
return "done"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// Event describes a single progress observation for a task. Byte fields are
|
|
// populated by byte-stream tasks; file-count fields by count-based tasks. A
|
|
// task may fill whichever subset it has; observers ignore zero values.
|
|
type Event struct {
|
|
TaskID string
|
|
Phase Phase
|
|
TotalBytes int64
|
|
DownloadedBytes int64
|
|
TotalFiles int
|
|
DownloadedFiles int
|
|
Err error
|
|
}
|
|
|
|
// Sink receives task events. Implementations must be safe for concurrent use.
|
|
type Sink interface {
|
|
Emit(Event)
|
|
}
|
|
|
|
// SinkFunc is a function adapter for Sink.
|
|
type SinkFunc func(Event)
|
|
|
|
func (f SinkFunc) Emit(e Event) { f(e) }
|
|
|
|
type sinkKey struct{}
|
|
|
|
// WithSink returns a ctx carrying the given sinks. Multiple sinks can be passed
|
|
// and all will receive every emitted event. Sinks already present in ctx are
|
|
// preserved.
|
|
func WithSink(ctx context.Context, sinks ...Sink) context.Context {
|
|
if len(sinks) == 0 {
|
|
return ctx
|
|
}
|
|
var existing []Sink
|
|
if v, ok := ctx.Value(sinkKey{}).([]Sink); ok {
|
|
existing = v
|
|
}
|
|
merged := make([]Sink, 0, len(existing)+len(sinks))
|
|
merged = append(merged, existing...)
|
|
merged = append(merged, sinks...)
|
|
return context.WithValue(ctx, sinkKey{}, merged)
|
|
}
|
|
|
|
// Emit broadcasts an event to all sinks carried by ctx. It is a no-op when no
|
|
// sink is attached, so producers can call it unconditionally.
|
|
func Emit(ctx context.Context, e Event) {
|
|
if ctx == nil {
|
|
return
|
|
}
|
|
sinks, ok := ctx.Value(sinkKey{}).([]Sink)
|
|
if !ok {
|
|
return
|
|
}
|
|
for _, s := range sinks {
|
|
s.Emit(e)
|
|
}
|
|
}
|