// Package task_service implements the shared developer board. package task_service import ( "context" "strings" "time" "unicode/utf8" "evening_detective_server/internal/repos/tasks_repo" ) var ( ErrInvalidTask = tasks_repo.ErrInvalidTask ErrNotFound = tasks_repo.ErrNotFound ErrConflict = tasks_repo.ErrConflict ErrInvalidAssignee = tasks_repo.ErrInvalidAssignee ) type Task = tasks_repo.Task type Assignee = tasks_repo.Assignee type ITasksRepo interface { List(context.Context) ([]*Task, error) ListAssignees(context.Context) ([]Assignee, error) Create(context.Context, Task) (*Task, error) Update(context.Context, Task) (*Task, error) SetStatus(context.Context, int, int, string) (*Task, error) Delete(context.Context, int, int) error } type TaskService struct{ repo ITasksRepo } func NewTaskService(repo ITasksRepo) *TaskService { return &TaskService{repo: repo} } func (s *TaskService) List(ctx context.Context) ([]*Task, error) { return s.repo.List(ctx) } func (s *TaskService) ListAssignees(ctx context.Context) ([]Assignee, error) { return s.repo.ListAssignees(ctx) } // ParseDueDate preserves calendar dates without a local time zone conversion. func ParseDueDate(value string) (*time.Time, error) { if value == "" { return nil, nil } date, err := time.Parse("2006-01-02", value) if err != nil || date.Year() < 1 { return nil, ErrInvalidTask } return &date, nil } func validStatus(status string) bool { return status == "new" || status == "in_progress" || status == "review" || status == "done" } func validate(task *Task) error { task.Title = strings.TrimSpace(task.Title) if task.Title == "" || strings.ContainsRune(task.Title, 0) || strings.ContainsRune(task.Description, 0) || utf8.RuneCountInString(task.Title) > 255 || utf8.RuneCountInString(task.Description) > 10000 || !validStatus(task.Status) || (task.AssigneeID != nil && *task.AssigneeID <= 0) { return ErrInvalidTask } return nil } func (s *TaskService) Create(ctx context.Context, task Task) (*Task, error) { if task.Status == "" { task.Status = "new" } if err := validate(&task); err != nil { return nil, err } return s.repo.Create(ctx, task) } func (s *TaskService) Update(ctx context.Context, task Task) (*Task, error) { if task.ID <= 0 || task.Version <= 0 { return nil, ErrInvalidTask } if err := validate(&task); err != nil { return nil, err } return s.repo.Update(ctx, task) } func (s *TaskService) SetStatus(ctx context.Context, id, version int, status string) (*Task, error) { if id <= 0 || version <= 0 || !validStatus(status) { return nil, ErrInvalidTask } return s.repo.SetStatus(ctx, id, version, status) } func (s *TaskService) Delete(ctx context.Context, id, version int) error { if id <= 0 || version <= 0 { return ErrInvalidTask } return s.repo.Delete(ctx, id, version) }