1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5package planning
6
7import (
8 "crypto/sha256"
9 "fmt"
10 "strings"
11 "time"
12)
13
14// TaskStatus represents the status of a task
15type TaskStatus int
16
17const (
18 StatusPending TaskStatus = iota
19 StatusInProgress
20 StatusCompleted
21 StatusFailed
22 StatusCancelled
23)
24
25// String returns the emoji representation of the task status
26func (s TaskStatus) String() string {
27 switch s {
28 case StatusPending:
29 return "☐"
30 case StatusInProgress:
31 return "⟳"
32 case StatusCompleted:
33 return "☑"
34 case StatusFailed:
35 return "☒"
36 case StatusCancelled:
37 return "⊗"
38 default:
39 return "☐"
40 }
41}
42
43// ParseStatus converts a string status to TaskStatus enum
44func ParseStatus(status string) TaskStatus {
45 switch strings.ToLower(status) {
46 case "pending":
47 return StatusPending
48 case "in_progress":
49 return StatusInProgress
50 case "completed":
51 return StatusCompleted
52 case "failed":
53 return StatusFailed
54 case "cancelled":
55 return StatusCancelled
56 default:
57 return StatusPending
58 }
59}
60
61// Goal represents the overarching goal
62type Goal struct {
63 Text string `json:"text"`
64 UpdatedAt time.Time `json:"updated_at"`
65}
66
67// Task represents a single task
68type Task struct {
69 ID string `json:"id"`
70 Title string `json:"title"`
71 Description string `json:"description"`
72 Status TaskStatus `json:"status"`
73 CreatedAt time.Time `json:"created_at"`
74 UpdatedAt time.Time `json:"updated_at"`
75}
76
77// NewTask creates a new task with a deterministic ID
78func NewTask(title, description string) *Task {
79 // Generate deterministic ID based on title and description
80 id := generateTaskID(title, description)
81
82 return &Task{
83 ID: id,
84 Title: title,
85 Description: description,
86 Status: StatusPending,
87 CreatedAt: time.Now(),
88 UpdatedAt: time.Now(),
89 }
90}
91
92// UpdateStatus updates the task status and timestamp
93func (t *Task) UpdateStatus(status TaskStatus) {
94 t.Status = status
95 t.UpdatedAt = time.Now()
96}
97
98// UpdateContent updates the task title and/or description and regenerates ID
99func (t *Task) UpdateContent(title, description string) {
100 if title != "" {
101 t.Title = title
102 }
103 if description != "" {
104 t.Description = description
105 }
106 // Regenerate ID based on new content
107 t.ID = generateTaskID(t.Title, t.Description)
108 t.UpdatedAt = time.Now()
109}
110
111// generateTaskID creates a deterministic 8-character ID based on task content
112func generateTaskID(title, description string) string {
113 content := fmt.Sprintf("%s:%s", title, description)
114 hash := sha256.Sum256([]byte(content))
115 return fmt.Sprintf("%x", hash[:4]) // 8 hex characters
116}