From 90616d1e6c5ba967a4fbf3de28f57d207d4fc704 Mon Sep 17 00:00:00 2001 From: Rangel Reale Date: Fri, 2 Oct 2026 07:33:29 -0300 Subject: [PATCH] refactor: rename NotRun to Skipped TaskInfo.NotRun named a condition in the negative, unlike every sibling option, which names what it attaches: WithSetup, WithParent, WithHandler. Skipped names the event instead, and the callback still says why. Renames TaskInfo.NotRun to TaskInfo.Skipped, WithNotRun to WithSkipped in both svcinit and instancetask, and ErrTaskNotRun to ErrTaskSkipped. Internal identifiers and the trace log message follow the same name. No behavior changes: the callback signature, call order and uniqueness guarantees are unchanged. The old exported names are removed without deprecated aliases, so dependents fail to compile rather than silently lose the hook. Co-Authored-By: Claude Opus 5 --- README.md | 2 +- build.go | 22 +++++++++++----------- build_test.go | 16 ++++++++-------- defs.go | 2 +- futuretask/task.go | 8 ++++---- futuretask/task_test.go | 16 ++++++++-------- instancetask/build.go | 6 +++--- instancetask/build_test.go | 8 ++++---- run.go | 20 ++++++++++---------- task.go | 4 ++-- 10 files changed, 52 insertions(+), 52 deletions(-) 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.