package common import ( "context" ) type jobErrorContextKey string const jobErrorContextKeyVal = jobErrorContextKey("job.error") type jobCancelCtx string const JobCancelCtxVal = jobCancelCtx("job.cancel") // JobError returns the job error for current context if any func JobError(ctx context.Context) error { val := ctx.Value(jobErrorContextKeyVal) if val != nil { if container, ok := val.(map[string]error); ok { return container["error"] } } return nil } func SetJobError(ctx context.Context, err error) { ctx.Value(jobErrorContextKeyVal).(map[string]error)["error"] = err } // WithJobErrorContainer adds a value to the context as a container for an error func WithJobErrorContainer(ctx context.Context) context.Context { container := map[string]error{} return context.WithValue(ctx, jobErrorContextKeyVal, container) } func WithJobCancelContext(ctx context.Context, cancelContext context.Context) context.Context { return context.WithValue(ctx, JobCancelCtxVal, cancelContext) } func JobCancelContext(ctx context.Context) context.Context { val := ctx.Value(JobCancelCtxVal) if val != nil { if container, ok := val.(context.Context); ok { return container } } return nil } // EarlyCancelContext returns a new context based on ctx that is canceled when the first of the provided contexts is canceled. func EarlyCancelContext(ctx context.Context) (context.Context, context.CancelFunc) { val := JobCancelContext(ctx) if val != nil { context, cancel := context.WithCancel(ctx) go func() { defer cancel() select { case <-context.Done(): case <-ctx.Done(): case <-val.Done(): } }() return context, cancel } return ctx, func() {} }