diff --git a/README.md b/README.md index cade8c5..f59d9ab 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ type TaskInfo struct { Steps []Step // steps the task implements (nil = all) Options []TaskInstanceOption // task options set by the task itself InitError error // initialization error - NotRun func(ctx context.Context, cause error) // called if Run returns without running any step + Skipped func(ctx context.Context, cause error) // called if Run returns without running any step } ``` diff --git a/build.go b/build.go index 6de4d87..029ab82 100644 --- a/build.go +++ b/build.go @@ -24,7 +24,7 @@ type TaskBuildFunc func(ctx context.Context) error // BuildTask creates a task from callback functions. // // It is also the way to decorate an existing task: use [WithParent] to forward all steps not set here to it, -// and [WithName], [WithTaskOptions] and [WithNotRun] to add to its [TaskInfo]. To customize how the steps are +// and [WithName], [WithTaskOptions] and [WithSkipped] to add to its [TaskInfo]. To customize how the steps are // called, use [WithHandler] in [Manager.AddTask]. func BuildTask(options ...TaskBuildOption) TaskBuild { return newTaskBuild(options...) @@ -68,7 +68,7 @@ func WithTeardown(f TaskBuildFunc) TaskBuildOption { // WithParent sets a parent task. Any step not set in the built task will be forwarded to it. // Its [TaskInfo] is merged into the built task one: its name is used if one is not set, its options are applied -// before the built task ones, its initialization error is returned, and its [TaskInfo.NotRun] is called. +// before the built task ones, its initialization error is returned, and its [TaskInfo.Skipped] is called. func WithParent(parent Task) TaskBuildOption { return func(build *taskBuild) { if parent == nil { @@ -86,11 +86,11 @@ func WithTaskOptions(options ...TaskInstanceOption) TaskBuildOption { } } -// WithNotRun adds a callback for [TaskInfo.NotRun]. All callbacks are called in order, before the parent one. -func WithNotRun(f func(ctx context.Context, cause error)) TaskBuildOption { +// WithSkipped adds a callback for [TaskInfo.Skipped]. All callbacks are called in order, before the parent one. +func WithSkipped(f func(ctx context.Context, cause error)) TaskBuildOption { return func(build *taskBuild) { if f != nil { - build.notRun = append(build.notRun, f) + build.skipped = append(build.skipped, f) } } } @@ -102,7 +102,7 @@ type taskBuild struct { parent atomic.Pointer[Task] state atomic.Pointer[taskBuildState] options []TaskInstanceOption - notRun []func(ctx context.Context, cause error) + skipped []func(ctx context.Context, cause error) name string } @@ -137,13 +137,13 @@ func (t *taskBuild) TaskInfo() TaskInfo { Options: slices.Concat(parentInfo.Options, t.options), InitError: state.initError, } - if len(t.notRun) > 0 || parentInfo.NotRun != nil { - ret.NotRun = func(ctx context.Context, cause error) { - for _, f := range t.notRun { + if len(t.skipped) > 0 || parentInfo.Skipped != nil { + ret.Skipped = func(ctx context.Context, cause error) { + for _, f := range t.skipped { f(ctx, cause) } - if parentInfo.NotRun != nil { - parentInfo.NotRun(ctx, cause) + if parentInfo.Skipped != nil { + parentInfo.Skipped(ctx, cause) } } } diff --git a/build_test.go b/build_test.go index 212ecec..7caf91c 100644 --- a/build_test.go +++ b/build_test.go @@ -132,28 +132,28 @@ func TestBuildTaskParentInitError(t *testing.T) { assert.ErrorIs(t, err, ErrNilTask) } -func TestBuildTaskNotRun(t *testing.T) { +func TestBuildTaskSkipped(t *testing.T) { var calls []string errCause := errors.New("cause") task := BuildTask( WithParent(BuildTask( WithStart(func(ctx context.Context) error { return nil }), - WithNotRun(func(ctx context.Context, cause error) { + WithSkipped(func(ctx context.Context, cause error) { assert.Check(t, errors.Is(cause, errCause)) calls = append(calls, "parent") }), )), - WithNotRun(func(ctx context.Context, cause error) { calls = append(calls, "task1") }), - WithNotRun(func(ctx context.Context, cause error) { calls = append(calls, "task2") }), + WithSkipped(func(ctx context.Context, cause error) { calls = append(calls, "task1") }), + WithSkipped(func(ctx context.Context, cause error) { calls = append(calls, "task2") }), ) - notRun := task.TaskInfo().NotRun - assert.Assert(t, notRun != nil) - notRun(t.Context(), errCause) + skipped := task.TaskInfo().Skipped + assert.Assert(t, skipped != nil) + skipped(t.Context(), errCause) assert.DeepEqual(t, []string{"task1", "task2", "parent"}, calls) - assert.Assert(t, BuildTask(WithStart(func(ctx context.Context) error { return nil })).TaskInfo().NotRun == nil) + assert.Assert(t, BuildTask(WithStart(func(ctx context.Context) error { return nil })).TaskInfo().Skipped == nil) } type testTaskInfo struct { diff --git a/defs.go b/defs.go index 7a9f106..4f95ff3 100644 --- a/defs.go +++ b/defs.go @@ -19,7 +19,7 @@ var ( ErrAlreadyInitialized = errors.New("already initialized") ErrNotInitialized = errors.New("not initialized") ErrDuplicateStep = errors.New("duplicate step") - ErrTaskNotRun = errors.New("task not run") + ErrTaskSkipped = errors.New("task skipped") ) const ( diff --git a/futuretask/task.go b/futuretask/task.go index 3705478..72ff8b5 100644 --- a/futuretask/task.go +++ b/futuretask/task.go @@ -13,7 +13,7 @@ import ( // setupFunc must not be nil: unlike [instancetask.Build], there would be no data to resolve the future // with. // If the task never runs, for example because a previous stage failed, the future is resolved with -// [svcinit.ErrTaskNotRun], so waiters don't block forever. +// [svcinit.ErrTaskSkipped], so waiters don't block forever. func New[T any](setupFunc instancetask.BuildSetupFunc[T], options ...instancetask.BuildOption[T]) *Task[T] { future := svcinit.NewFuture[T]() if setupFunc == nil { @@ -32,16 +32,16 @@ func New[T any](setupFunc instancetask.BuildSetupFunc[T], options ...instancetas } future.Resolve(data) return data, nil - }, append(slices.Clip(options), instancetask.WithNotRun[T](func(_ context.Context, cause error) { + }, append(slices.Clip(options), instancetask.WithSkipped[T](func(_ context.Context, cause error) { select { case <-future.Done(): return // resolving twice panics. default: } if cause != nil { - future.ResolveError(fmt.Errorf("%w: %w", svcinit.ErrTaskNotRun, cause)) + future.ResolveError(fmt.Errorf("%w: %w", svcinit.ErrTaskSkipped, cause)) } else { - future.ResolveError(svcinit.ErrTaskNotRun) + future.ResolveError(svcinit.ErrTaskSkipped) } }))...), future: future, diff --git a/futuretask/task_test.go b/futuretask/task_test.go index 9af643b..becefe6 100644 --- a/futuretask/task_test.go +++ b/futuretask/task_test.go @@ -105,7 +105,7 @@ func TestInitError(t *testing.T) { assert.ErrorIs(t, err, svcinit.ErrNilTask) } -func TestNotRunAfterSetupError(t *testing.T) { +func TestSkippedAfterSetupError(t *testing.T) { synctest.Test(t, func(t *testing.T) { errSetup := errors.New("setup error") @@ -136,12 +136,12 @@ func TestNotRunAfterSetupError(t *testing.T) { err = sinit.Run(t.Context()) assert.ErrorIs(t, err, errSetup) - assert.ErrorIs(t, valueErr, svcinit.ErrTaskNotRun) + assert.ErrorIs(t, valueErr, svcinit.ErrTaskSkipped) assert.ErrorIs(t, valueErr, errSetup) }) } -func TestNotRunInitError(t *testing.T) { +func TestSkippedInitError(t *testing.T) { sinit, err := svcinit.New() assert.NilError(t, err) @@ -156,12 +156,12 @@ func TestNotRunInitError(t *testing.T) { assert.ErrorIs(t, err, svcinit.ErrNilTask) _, err = future.Value(svcinit.WithoutFutureWait()) - assert.ErrorIs(t, err, svcinit.ErrTaskNotRun) + assert.ErrorIs(t, err, svcinit.ErrTaskSkipped) } -// TestNotRunDecorated is a regression test: decorating a future task with svcinit.BuildTask used to drop its -// "not run" notification, so the future was never resolved and its waiters blocked forever. -func TestNotRunDecorated(t *testing.T) { +// TestSkippedDecorated is a regression test: decorating a future task with svcinit.BuildTask used to drop its +// "skipped" notification, so the future was never resolved and its waiters blocked forever. +func TestSkippedDecorated(t *testing.T) { for _, tc := range []struct { name string decorate func(task svcinit.Task) svcinit.Task @@ -203,7 +203,7 @@ func TestNotRunDecorated(t *testing.T) { assert.ErrorIs(t, err, errSetup) _, err = future.Value(svcinit.WithoutFutureWait()) - assert.ErrorIs(t, err, svcinit.ErrTaskNotRun) + assert.ErrorIs(t, err, svcinit.ErrTaskSkipped) assert.ErrorIs(t, err, errSetup) }) }) diff --git a/instancetask/build.go b/instancetask/build.go index 03bf49d..66e3b10 100644 --- a/instancetask/build.go +++ b/instancetask/build.go @@ -141,9 +141,9 @@ func WithTaskOptions[T any](options ...svcinit.TaskInstanceOption) BuildOption[T return withBuildOption[T](svcinit.WithTaskOptions(options...)) } -// WithNotRun adds a callback for [svcinit.TaskInfo.NotRun]. See [svcinit.WithNotRun]. -func WithNotRun[T any](f func(ctx context.Context, cause error)) BuildOption[T] { - return withBuildOption[T](svcinit.WithNotRun(f)) +// WithSkipped adds a callback for [svcinit.TaskInfo.Skipped]. See [svcinit.WithSkipped]. +func WithSkipped[T any](f func(ctx context.Context, cause error)) BuildOption[T] { + return withBuildOption[T](svcinit.WithSkipped(f)) } // internal diff --git a/instancetask/build_test.go b/instancetask/build_test.go index 8e68dad..26c174f 100644 --- a/instancetask/build_test.go +++ b/instancetask/build_test.go @@ -94,12 +94,12 @@ func TestBuildTaskInfoFromParent(t *testing.T) { svcinit.WithName("parent"), svcinit.WithStart(func(ctx context.Context) error { return nil }), svcinit.WithTaskOptions(svcinit.WithCancelContext(true)), - svcinit.WithNotRun(func(ctx context.Context, cause error) { calls = append(calls, "parent") }), + svcinit.WithSkipped(func(ctx context.Context, cause error) { calls = append(calls, "parent") }), ) task := Build[int](func(ctx context.Context) (int, error) { return 1, nil }, WithParent[int](parent), - WithNotRun[int](func(ctx context.Context, cause error) { calls = append(calls, "task") }), + WithSkipped[int](func(ctx context.Context, cause error) { calls = append(calls, "task") }), ) info := svcinit.GetTaskInfo(task) @@ -108,8 +108,8 @@ func TestBuildTaskInfoFromParent(t *testing.T) { assert.DeepEqual(t, []svcinit.Step{svcinit.StepSetup, svcinit.StepStart}, info.Steps, cmpopts.SortSlices(cmp.Less[svcinit.Step])) assert.Equal(t, 1, len(info.Options)) - assert.Assert(t, info.NotRun != nil) - info.NotRun(t.Context(), nil) + assert.Assert(t, info.Skipped != nil) + info.Skipped(t.Context(), nil) assert.DeepEqual(t, []string{"task", "parent"}, calls) } diff --git a/run.go b/run.go index 8467a9f..6c5f763 100644 --- a/run.go +++ b/run.go @@ -37,10 +37,10 @@ func (m *Manager) runWithStopErrors(ctx context.Context, options ...RunOption) ( } // if returning before any stage runs, notify all tasks that they will never run. - notRunNotified := false + skippedNotified := false defer func() { - if !notRunNotified { - m.notifyTasksNotRun(ctx, m.stages, cause) + if !skippedNotified { + m.notifyTasksSkipped(ctx, m.stages, cause) } }() @@ -106,8 +106,8 @@ func (m *Manager) runWithStopErrors(ctx context.Context, options ...RunOption) ( // stages after a setup failure will never run any step, notify their tasks now so anything waiting on // them (like a Future) is released before shutdown starts waiting for tasks to finish. - notRunNotified = true - m.notifyTasksNotRun(ctx, m.stages[stagesRun:], setupErr) + skippedNotified = true + m.notifyTasksSkipped(ctx, m.stages[stagesRun:], setupErr) if setupErr == nil { m.logger.InfoContext(ctx, "waiting for first task to return") @@ -515,17 +515,17 @@ func (m *Manager) runStageStep(ctx, taskDoneCtx context.Context, loggerStage *sl return int(taskCount.Load()) } -// notifyTasksNotRun calls [TaskInfo.NotRun] for all tasks of the passed stages, which must be stages +// notifyTasksSkipped calls [TaskInfo.Skipped] for all tasks of the passed stages, which must be stages // that will never run any step. -func (m *Manager) notifyTasksNotRun(ctx context.Context, stages []string, cause error) { +func (m *Manager) notifyTasksSkipped(ctx context.Context, stages []string, cause error) { ctx = context.WithoutCancel(ctx) for _, stage := range stages { for tw := range m.tasks.stageTasks(stage) { - if notRun := GetTaskInfo(tw.task).NotRun; notRun != nil { - m.logger.Log(ctx, slog2.LevelTrace, "notifying task not run", + if skipped := GetTaskInfo(tw.task).Skipped; skipped != nil { + m.logger.Log(ctx, slog2.LevelTrace, "notifying task skipped", "stage", stage, "task", GetTaskDescription(tw.task)) - notRun(ctx, cause) + skipped(ctx, cause) } } } diff --git a/task.go b/task.go index 513ace5..59e496f 100644 --- a/task.go +++ b/task.go @@ -46,10 +46,10 @@ type TaskInfo struct { // InitError is a task initialization error. If not nil, [Manager.AddTask] won't add the task, and // [Manager.Run] will return the error. InitError error - // NotRun is called when [Manager.Run] returns without having run any of the task steps, for example because + // Skipped is called when [Manager.Run] returns without having run any of the task steps, for example because // a setup step of a previous stage failed. cause is the error returned from Run, and may be nil. // It can be used to release anything waiting on the task, like an unresolved [Future]. - NotRun func(ctx context.Context, cause error) + Skipped func(ctx context.Context, cause error) } // TaskWithInfo allows a task to describe its optional metadata and behavior.