task

package
v0.1.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 6, 2026 License: MIT Imports: 4 Imported by: 1

Documentation

Overview

Package task provides task lifecycle management for long-running operations across all protocols.

This package enables tracking of task state, progress, and results regardless of the underlying protocol (MCP, A2A, ACP).

Ecosystem Position

task manages long-running operations with progress tracking:

┌─────────────────────────────────────────────────────────────────┐
│                      Task Management Flow                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Protocol              task                  Subscriber        │
│   ┌─────────┐        ┌───────────┐         ┌─────────┐         │
│   │ Request │────────│  Create   │─────────│ Handler │         │
│   │         │        │           │         │         │         │
│   └─────────┘        │ ┌───────┐ │         └─────────┘         │
│        │             │ │Manager│ │              │               │
│        │             │ │       │ │              │               │
│        ▼             │ └───────┘ │              │               │
│   ┌─────────┐        │     │     │              │               │
│   │ Update  │────────│─────┼─────│──────────────▼               │
│   │Progress │        │     │     │         ┌─────────┐         │
│   └─────────┘        │     ▼     │         │Subscribe│         │
│        │             │ ┌───────┐ │         │ Channel │         │
│        ▼             │ │ Store │ │         └─────────┘         │
│   ┌─────────┐        │ │(memory)│ │              ▲               │
│   │Complete │────────│ └───────┘ │──────────────┘               │
│   │/Fail    │        └───────────┘   notify                     │
│   └─────────┘                                                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

State Machine

Tasks follow a state machine with the following states and transitions:

                ┌─────────────────────────────────────┐
                │          State Machine              │
                └─────────────────────────────────────┘

 ┌─────────┐      Update       ┌─────────┐
 │ pending │─────────────────▶│ running │
 └────┬────┘                   └────┬────┘
      │                              │
      │ Cancel                       │ Complete/Fail/Cancel
      │                              │
      ▼                              ▼
┌───────────┐                  ┌───────────┐
│ cancelled │                  │ complete  │
└───────────┘                  │  failed   │
                               │ cancelled │
                               └───────────┘
                                (terminal)

Terminal states (complete, failed, cancelled) allow no further transitions.

Core Components

  • Task: Task with ID, state, progress, message, result, and timestamps
  • State: Task state constants (pending, running, complete, failed, cancelled)
  • Manager: Interface for task lifecycle (Create/Get/Update/Complete/Fail/Cancel)
  • DefaultManager: Thread-safe Manager implementation with subscription support
  • Store: Interface for task persistence
  • MemoryStore: Thread-safe in-memory Store implementation

Quick Start

// Create a manager
mgr := task.NewManager()

// Create a task
t, err := mgr.Create(ctx, "task-1")
if err != nil {
    return err
}

// Update progress (transitions pending → running)
err = mgr.Update(ctx, "task-1", 0.5, "Processing...")

// Subscribe to updates
ch, _ := mgr.Subscribe(ctx, "task-1")
go func() {
    for update := range ch {
        fmt.Printf("Progress: %.0f%%\n", update.Progress*100)
    }
}()

// Complete the task (closes subscription channel)
err = mgr.Complete(ctx, "task-1", result)

Subscriptions

Subscribe returns a channel that receives task updates:

ch, err := mgr.Subscribe(ctx, "task-1")
if err != nil {
    return err
}

for update := range ch {
    // Process update
    if update.State.IsTerminal() {
        break
    }
}

The channel is closed when:

  • Task reaches terminal state (complete, failed, cancelled)
  • Context is cancelled

Thread Safety

All exported types are safe for concurrent use:

  • DefaultManager: sync.RWMutex protects all operations
  • Get/List: Uses RLock for concurrent reads
  • Create/Update/Complete/Fail/Cancel: Uses Lock for exclusive access
  • MemoryStore: sync.RWMutex protects all operations
  • Task: Not thread-safe; use Manager methods for safe mutations
  • Subscription channels: Buffered (10) for non-blocking sends

Error Handling

Sentinel errors (use errors.Is for checking):

The TaskError type wraps errors with task context:

err := &TaskError{
    TaskID: "task-123",
    Op:     "update",
    Err:    ErrTaskNotFound,
}
// errors.Is(err, ErrTaskNotFound) = true

Configuration Options

DefaultManager supports functional options:

Integration with ApertureStack

task integrates with other ApertureStack packages:

  • session: Tasks may be associated with client sessions
  • stream: Progress updates may be streamed to clients
  • wire: Task status maps to protocol-specific formats (MCP progress, A2A status)

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrTaskNotFound is returned when a task cannot be found.
	ErrTaskNotFound = errors.New("task: not found")

	// ErrTaskExists is returned when creating a task that already exists.
	ErrTaskExists = errors.New("task: already exists")

	// ErrInvalidState is returned for invalid task states.
	ErrInvalidState = errors.New("task: invalid state")

	// ErrInvalidTransition is returned for invalid state transitions.
	ErrInvalidTransition = errors.New("task: invalid transition")

	// ErrEmptyID is returned when a task ID is empty.
	ErrEmptyID = errors.New("task: empty ID")
)

Sentinel errors for task operations. All errors use the "task: " prefix for consistent error identification.

Functions

This section is empty.

Types

type DefaultManager

type DefaultManager struct {
	// contains filtered or unexported fields
}

DefaultManager is the default implementation of Manager.

func NewManager

func NewManager(opts ...Option) *DefaultManager

NewManager creates a new task manager with default configuration.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()

	ctx := context.Background()
	t, err := mgr.Create(ctx, "task-1")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Task created:", t.ID)
	fmt.Println("Initial state:", t.State)
}
Output:
Task created: task-1
Initial state: pending
Example (WithStore)
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	// Use custom store
	store := task.NewMemoryStore()
	mgr := task.NewManager(task.WithStore(store))

	ctx := context.Background()
	t, _ := mgr.Create(ctx, "task-1")

	fmt.Println("Task ID:", t.ID)
}
Output:
Task ID: task-1

func (*DefaultManager) Cancel

func (m *DefaultManager) Cancel(ctx context.Context, id string) error

Cancel cancels the task.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	_, _ = mgr.Create(ctx, "task-1")

	// Cancel the task
	err := mgr.Cancel(ctx, "task-1")
	fmt.Println("Cancel error:", err)

	// Check state
	t, _ := mgr.Get(ctx, "task-1")
	fmt.Println("State:", t.State)
	fmt.Println("Is terminal:", t.State.IsTerminal())
}
Output:
Cancel error: <nil>
State: cancelled
Is terminal: true
Example (Terminal)
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	_, _ = mgr.Create(ctx, "task-1")
	_ = mgr.Complete(ctx, "task-1", nil) // Already complete

	// Try to cancel terminal task
	err := mgr.Cancel(ctx, "task-1")
	fmt.Println("Error is ErrInvalidTransition:", errors.Is(err, task.ErrInvalidTransition))
}
Output:
Error is ErrInvalidTransition: true

func (*DefaultManager) Complete

func (m *DefaultManager) Complete(ctx context.Context, id string, result any) error

Complete marks the task as complete with the given result.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	_, _ = mgr.Create(ctx, "task-1")
	_ = mgr.Update(ctx, "task-1", 0.5, "Processing")

	// Complete the task
	result := map[string]string{"status": "done"}
	err := mgr.Complete(ctx, "task-1", result)
	fmt.Println("Complete error:", err)

	// Check state
	t, _ := mgr.Get(ctx, "task-1")
	fmt.Println("State:", t.State)
	fmt.Println("Progress:", t.Progress)
	fmt.Println("Is terminal:", t.State.IsTerminal())
}
Output:
Complete error: <nil>
State: complete
Progress: 1
Is terminal: true

func (*DefaultManager) Create

func (m *DefaultManager) Create(ctx context.Context, id string) (*Task, error)

Create creates a new task with the given ID.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	t, err := mgr.Create(ctx, "my-task")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("ID:", t.ID)
	fmt.Println("State:", t.State)
	fmt.Println("Progress:", t.Progress)
}
Output:
ID: my-task
State: pending
Progress: 0
Example (Duplicate)
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	_, _ = mgr.Create(ctx, "task-1")
	_, err := mgr.Create(ctx, "task-1")

	fmt.Println("Error is ErrTaskExists:", errors.Is(err, task.ErrTaskExists))
}
Output:
Error is ErrTaskExists: true
Example (EmptyID)
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	_, err := mgr.Create(ctx, "")
	fmt.Println("Error is ErrEmptyID:", errors.Is(err, task.ErrEmptyID))
}
Output:
Error is ErrEmptyID: true

func (*DefaultManager) Fail

func (m *DefaultManager) Fail(ctx context.Context, id string, err error) error

Fail marks the task as failed with the given error.

Example
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	_, _ = mgr.Create(ctx, "task-1")
	_ = mgr.Update(ctx, "task-1", 0.5, "Processing")

	// Fail the task
	err := mgr.Fail(ctx, "task-1", errors.New("something went wrong"))
	fmt.Println("Fail error:", err)

	// Check state
	t, _ := mgr.Get(ctx, "task-1")
	fmt.Println("State:", t.State)
	fmt.Println("Task error:", t.Error)
	fmt.Println("Is terminal:", t.State.IsTerminal())
}
Output:
Fail error: <nil>
State: failed
Task error: something went wrong
Is terminal: true

func (*DefaultManager) Get

func (m *DefaultManager) Get(ctx context.Context, id string) (*Task, error)

Get retrieves a task by ID.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	_, _ = mgr.Create(ctx, "task-1")

	t, err := mgr.Get(ctx, "task-1")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("ID:", t.ID)
	fmt.Println("State:", t.State)
}
Output:
ID: task-1
State: pending
Example (NotFound)
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	_, err := mgr.Get(ctx, "nonexistent")
	fmt.Println("Error is ErrTaskNotFound:", errors.Is(err, task.ErrTaskNotFound))
}
Output:
Error is ErrTaskNotFound: true

func (*DefaultManager) List

func (m *DefaultManager) List(ctx context.Context) ([]*Task, error)

List returns all tasks.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	_, _ = mgr.Create(ctx, "task-1")
	_, _ = mgr.Create(ctx, "task-2")
	_, _ = mgr.Create(ctx, "task-3")

	tasks, err := mgr.List(ctx)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Task count:", len(tasks))
}
Output:
Task count: 3

func (*DefaultManager) Subscribe

func (m *DefaultManager) Subscribe(ctx context.Context, id string) (<-chan *Task, error)

Subscribe returns a channel that receives task updates. The channel is closed when the task reaches a terminal state.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	_, _ = mgr.Create(ctx, "task-1")

	// Subscribe to updates
	ch, err := mgr.Subscribe(ctx, "task-1")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Subscribed:", ch != nil)

	// Complete task (sends update then closes channel)
	_ = mgr.Complete(ctx, "task-1", "done")

	// Drain the channel to get the final update
	count := 0
	for range ch {
		count++
	}
	fmt.Println("Received updates before close:", count > 0)
}
Output:
Subscribed: true
Received updates before close: true

func (*DefaultManager) Update

func (m *DefaultManager) Update(ctx context.Context, id string, progress float64, message string) error

Update updates task progress and message. Transitions pending → running on first update.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	_, _ = mgr.Create(ctx, "task-1")

	// Update progress
	err := mgr.Update(ctx, "task-1", 0.5, "Processing...")
	fmt.Println("Update error:", err)

	// Check state changed to running
	t, _ := mgr.Get(ctx, "task-1")
	fmt.Println("State after update:", t.State)
	fmt.Println("Progress:", t.Progress)
	fmt.Println("Message:", t.Message)
}
Output:
Update error: <nil>
State after update: running
Progress: 0.5
Message: Processing...
Example (NotFound)
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	mgr := task.NewManager()
	ctx := context.Background()

	err := mgr.Update(ctx, "nonexistent", 0.5, "...")
	fmt.Println("Error is ErrTaskNotFound:", errors.Is(err, task.ErrTaskNotFound))
}
Output:
Error is ErrTaskNotFound: true

type Manager

type Manager interface {
	// Create creates a new task with the given ID.
	Create(ctx context.Context, id string) (*Task, error)

	// Get retrieves a task by ID.
	Get(ctx context.Context, id string) (*Task, error)

	// List returns all tasks.
	List(ctx context.Context) ([]*Task, error)

	// Update updates task progress and message.
	// Transitions pending → running on first update.
	Update(ctx context.Context, id string, progress float64, message string) error

	// Complete marks the task as complete with the given result.
	Complete(ctx context.Context, id string, result any) error

	// Fail marks the task as failed with the given error.
	Fail(ctx context.Context, id string, err error) error

	// Cancel cancels the task.
	Cancel(ctx context.Context, id string) error

	// Subscribe returns a channel that receives task updates.
	// The channel is closed when the task reaches a terminal state.
	Subscribe(ctx context.Context, id string) (<-chan *Task, error)
}

Manager manages task lifecycle.

Contract:

  • Concurrency: All methods are safe for concurrent use via sync.RWMutex.
  • Context: All methods accept context but do not currently block on I/O.
  • State Machine: pending→running→complete|failed|cancelled
  • Errors: Returns ErrTaskNotFound, ErrTaskExists, ErrInvalidTransition, ErrEmptyID. Use errors.Is for checking.
  • Ownership: Returned *Task is a copy; internal state is protected.
  • Subscriptions: Channels closed on terminal state or context cancellation.

type MemoryStore

type MemoryStore struct {
	// contains filtered or unexported fields
}

MemoryStore is an in-memory implementation of Store.

func NewMemoryStore

func NewMemoryStore() *MemoryStore

NewMemoryStore creates a new in-memory task store.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	store := task.NewMemoryStore()
	ctx := context.Background()

	// Save a task
	t := &task.Task{ID: "task-1", State: task.StatePending}
	err := store.Save(ctx, t)
	fmt.Println("Save error:", err)

	// Load it back
	loaded, err := store.Load(ctx, "task-1")
	if err != nil {
		fmt.Println("Load error:", err)
		return
	}

	fmt.Println("Loaded ID:", loaded.ID)
}
Output:
Save error: <nil>
Loaded ID: task-1

func (*MemoryStore) Delete

func (s *MemoryStore) Delete(ctx context.Context, id string) error

Delete removes a task by ID.

Example
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	store := task.NewMemoryStore()
	ctx := context.Background()

	_ = store.Save(ctx, &task.Task{ID: "task-1"})

	err := store.Delete(ctx, "task-1")
	fmt.Println("Delete error:", err)

	// Verify deleted
	_, err = store.Load(ctx, "task-1")
	fmt.Println("After delete, error is ErrTaskNotFound:", errors.Is(err, task.ErrTaskNotFound))
}
Output:
Delete error: <nil>
After delete, error is ErrTaskNotFound: true

func (*MemoryStore) Load

func (s *MemoryStore) Load(ctx context.Context, id string) (*Task, error)

Load retrieves a task by ID.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	store := task.NewMemoryStore()
	ctx := context.Background()

	// Save first
	_ = store.Save(ctx, &task.Task{ID: "task-1", State: task.StatePending})

	// Load
	t, err := store.Load(ctx, "task-1")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("ID:", t.ID)
	fmt.Println("State:", t.State)
}
Output:
ID: task-1
State: pending
Example (NotFound)
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	store := task.NewMemoryStore()
	ctx := context.Background()

	_, err := store.Load(ctx, "nonexistent")
	fmt.Println("Error is ErrTaskNotFound:", errors.Is(err, task.ErrTaskNotFound))
}
Output:
Error is ErrTaskNotFound: true

func (*MemoryStore) LoadAll

func (s *MemoryStore) LoadAll(ctx context.Context) ([]*Task, error)

LoadAll retrieves all tasks.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	store := task.NewMemoryStore()
	ctx := context.Background()

	_ = store.Save(ctx, &task.Task{ID: "task-1"})
	_ = store.Save(ctx, &task.Task{ID: "task-2"})

	tasks, err := store.LoadAll(ctx)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Task count:", len(tasks))
}
Output:
Task count: 2

func (*MemoryStore) Save

func (s *MemoryStore) Save(ctx context.Context, task *Task) error

Save saves or updates a task.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	store := task.NewMemoryStore()
	ctx := context.Background()

	t := &task.Task{ID: "task-1", State: task.StatePending}
	err := store.Save(ctx, t)
	fmt.Println("Save error:", err)
}
Output:
Save error: <nil>

type Option

type Option func(*DefaultManager)

Option configures a DefaultManager.

func WithStore

func WithStore(store Store) Option

WithStore configures the manager to use the given store.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	// Create custom store
	store := task.NewMemoryStore()

	// Use it with manager
	mgr := task.NewManager(task.WithStore(store))

	ctx := context.Background()
	_, _ = mgr.Create(ctx, "task-1")

	// Verify task is in our custom store
	t, _ := store.Load(ctx, "task-1")
	fmt.Println("Task in custom store:", t != nil)
}
Output:
Task in custom store: true

type State

type State string

State represents task execution state.

const (
	// StatePending indicates the task is created but not yet started.
	StatePending State = "pending"

	// StateRunning indicates the task is actively executing.
	StateRunning State = "running"

	// StateComplete indicates the task finished successfully.
	StateComplete State = "complete"

	// StateFailed indicates the task finished with an error.
	StateFailed State = "failed"

	// StateCancelled indicates the task was cancelled.
	StateCancelled State = "cancelled"
)

func (State) IsTerminal

func (s State) IsTerminal() bool

IsTerminal returns true if the state is a terminal state (complete, failed, or cancelled) that allows no further transitions.

Example
package main

import (
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	fmt.Println("pending terminal:", task.StatePending.IsTerminal())
	fmt.Println("running terminal:", task.StateRunning.IsTerminal())
	fmt.Println("complete terminal:", task.StateComplete.IsTerminal())
	fmt.Println("failed terminal:", task.StateFailed.IsTerminal())
	fmt.Println("cancelled terminal:", task.StateCancelled.IsTerminal())
}
Output:
pending terminal: false
running terminal: false
complete terminal: true
failed terminal: true
cancelled terminal: true

func (State) String

func (s State) String() string

String returns the string representation of the state.

Example
package main

import (
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	states := []task.State{
		task.StatePending,
		task.StateRunning,
		task.StateComplete,
		task.StateFailed,
		task.StateCancelled,
	}

	for _, s := range states {
		fmt.Println(s.String())
	}
}
Output:
pending
running
complete
failed
cancelled

func (State) Valid

func (s State) Valid() bool

Valid returns true if the state is a known valid state.

Example
package main

import (
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	fmt.Println("pending valid:", task.StatePending.Valid())
	fmt.Println("running valid:", task.StateRunning.Valid())
	fmt.Println("invalid valid:", task.State("unknown").Valid())
}
Output:
pending valid: true
running valid: true
invalid valid: false

type Store

type Store interface {
	// Save saves or updates a task.
	Save(ctx context.Context, task *Task) error

	// Load retrieves a task by ID.
	Load(ctx context.Context, id string) (*Task, error)

	// LoadAll retrieves all tasks.
	LoadAll(ctx context.Context) ([]*Task, error)

	// Delete removes a task by ID.
	Delete(ctx context.Context, id string) error
}

Store provides task persistence.

Contract:

  • Concurrency: Implementations must be safe for concurrent use.
  • Ownership: Save stores a clone; Load returns a clone.
  • Errors: Returns ErrTaskNotFound for missing tasks.

type Task

type Task struct {
	// ID is the unique identifier for the task.
	ID string

	// State is the current execution state.
	State State

	// Progress is the completion percentage (0.0 to 1.0).
	Progress float64

	// Message is the current status message.
	Message string

	// Result is the final result (set when complete).
	Result any

	// Error is the error (set when failed).
	Error error

	// CreatedAt is when the task was created.
	CreatedAt time.Time

	// UpdatedAt is when the task was last updated.
	UpdatedAt time.Time

	// CompletedAt is when the task reached a terminal state.
	CompletedAt *time.Time
}

Task represents a long-running operation.

func (*Task) Clone

func (t *Task) Clone() *Task

Clone returns a deep copy of the task.

Example
package main

import (
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	t := &task.Task{
		ID:       "task-1",
		State:    task.StateRunning,
		Progress: 0.5,
		Message:  "Processing",
	}

	clone := t.Clone()

	fmt.Println("Same ID:", clone.ID == t.ID)
	fmt.Println("Same State:", clone.State == t.State)
	fmt.Println("Same Progress:", clone.Progress == t.Progress)
}
Output:
Same ID: true
Same State: true
Same Progress: true

type TaskError

type TaskError struct {
	// TaskID is the ID of the task that caused the error.
	TaskID string

	// Op is the operation that failed.
	Op string

	// Err is the underlying error.
	Err error
}

TaskError wraps an error with task context.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	err := &task.TaskError{
		TaskID: "task-123",
		Op:     "update",
		Err:    task.ErrTaskNotFound,
	}

	fmt.Println(err.Error())
	fmt.Println("Unwraps to ErrTaskNotFound:", errors.Is(err, task.ErrTaskNotFound))
}
Output:
task task-123: update: task: not found
Unwraps to ErrTaskNotFound: true
Example (NoUnderlying)
package main

import (
	"fmt"

	"github.com/jonwraymond/toolprotocol/task"
)

func main() {
	err := &task.TaskError{
		TaskID: "task-123",
		Op:     "validate",
	}

	fmt.Println(err.Error())
}
Output:
task task-123: validate

func (*TaskError) Error

func (e *TaskError) Error() string

Error returns the error message.

func (*TaskError) Unwrap

func (e *TaskError) Unwrap() error

Unwrap returns the underlying error.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL