savestate.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. package itashi
  2. import (
  3. "bytes"
  4. "fmt"
  5. "log"
  6. "os"
  7. "strconv"
  8. "strings"
  9. "text/template"
  10. "time"
  11. tea "github.com/charmbracelet/bubbletea"
  12. )
  13. const SHELF_LINE_DELIM = "\n---- ---- ---- ----\n"
  14. const SHELF_COL_DELIM = " "
  15. const TIME_FORMAT = "2006-01-02T15:04:05 -07:00:00"
  16. const FS_SAVE_LOCATION = "./todo.ta"
  17. const SHELF_TEMPLATE = "{{.Id}} {{.Title}} {{.Desc}} {{.Due}} {{.Done}} {{.Priority}}"
  18. type Task struct {
  19. Id int
  20. Title string
  21. Desc string
  22. Due time.Time
  23. Done bool
  24. Priority int
  25. }
  26. func GetDefualtSave() string {
  27. return fmt.Sprintf("%s/.config/itashi/todo.ta", os.Getenv("HOME"))
  28. }
  29. // lets make Task implement the tea.Model interface
  30. func (t Task) Init() tea.Cmd {
  31. return nil
  32. }
  33. func (t Task) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
  34. return t, nil
  35. }
  36. func (t Task) View() string {
  37. return ""
  38. }
  39. type TaskShelf interface {
  40. // Modify the due date of an existing task
  41. ModifyDue(id int, due time.Time)
  42. // Modify the description field of an existing task
  43. ModifyDesc(id int, desc string)
  44. // Modify the priority of an existing task
  45. ModifyPriority(id int, pri int)
  46. // modify the title of an existing task
  47. ModifyTitle(id int, title string)
  48. // delete an existing task from the shelf
  49. DeleteTask(id int)
  50. // Mark a task as complete
  51. MarkDone(id int)
  52. // hopefully you dont need to call this! ;)
  53. ResetDone(id int)
  54. // Add a task to the shelf
  55. AddTask(t Task)
  56. // Retrieve all tasks in the shelf
  57. GetAll() []Task
  58. // Render a task to a task template
  59. RenderTask(task Task) string
  60. }
  61. /*
  62. Retrieve all the tasks from the designated TaskShelf
  63. */
  64. func GetTaskList(taskio TaskShelf) []Task {
  65. return taskio.GetAll()
  66. }
  67. /*
  68. Grab all of the names of the tasks from the TaskShelf
  69. :param t: a list of Task structs
  70. :returns: A list of the task names
  71. */
  72. func GetTaskNames(t []Task) []string {
  73. var taskn []string
  74. for i := range t {
  75. taskn = append(taskn, t[i].Title)
  76. }
  77. return taskn
  78. }
  79. type FilesystemShelf struct {
  80. SaveLocation string
  81. Template *template.Template
  82. TaskTempl *template.Template
  83. Tasks []Task
  84. }
  85. func (t *FilesystemShelf) RenderTask(task Task) string {
  86. var bw bytes.Buffer
  87. err := t.TaskTempl.Execute(&bw, task)
  88. if err != nil {
  89. log.Fatal("Had a problem rendering this task.", task, err)
  90. }
  91. return bw.String()
  92. }
  93. /*
  94. Create a new filesystem shelf struct to reflect the filesystem shelf
  95. :param save: the save location to store the shelf in
  96. :returns: a pointer to a FilesystemShelf struct
  97. */
  98. func NewFilesystemShelf(save string) *FilesystemShelf {
  99. tmpl, err := template.New("task").Parse(SHELF_TEMPLATE)
  100. if err != nil {
  101. log.Fatal("Could not parse the shelf template! ", err)
  102. }
  103. tasktmpl, err := template.New("task").Parse(TASK_ITEM)
  104. if err != nil {
  105. log.Fatal("Couldnt parse task template. ", err)
  106. }
  107. shelf := &FilesystemShelf{
  108. SaveLocation: save,
  109. Template: tmpl,
  110. TaskTempl: tasktmpl,
  111. Tasks: []Task{},
  112. }
  113. shelf.Tasks = shelf.GetAll()
  114. return shelf
  115. }
  116. // Retrieve all the tasks from the filesystem shelf
  117. func (f *FilesystemShelf) GetAll() []Task {
  118. b, err := os.ReadFile(f.SaveLocation)
  119. if err != nil {
  120. log.Fatal(err)
  121. }
  122. return parseFilesystemShelf(b)
  123. }
  124. /*
  125. Add a task to the filesystem shelf
  126. :param t: the Task struct to add
  127. */
  128. func (f *FilesystemShelf) AddTask(t Task) {
  129. f.Tasks = append(f.Tasks, t)
  130. err := os.WriteFile(f.SaveLocation, marshalTaskToShelf(f.Tasks, f.Template), os.ModePerm)
  131. if err != nil {
  132. log.Fatal("Need to fix later, error writing to fs ", err)
  133. }
  134. }
  135. // Boiler plate so i can implement later
  136. func (f *FilesystemShelf) ModifyDue(id int, due time.Time) {}
  137. func (f *FilesystemShelf) ModifyDesc(id int, desc string) {}
  138. func (f *FilesystemShelf) ModifyPriority(id int, pri int) {}
  139. func (f *FilesystemShelf) ModifyTitle(id int, title string) {}
  140. func (f *FilesystemShelf) DeleteTask(id int) {}
  141. func (f *FilesystemShelf) MarkDone(id int) {}
  142. func (f *FilesystemShelf) ResetDone(id int) {}
  143. // private function for parsing the byte stream from the filesystem
  144. func parseFilesystemShelf(data []byte) []Task {
  145. var filestring string
  146. filestring = string(data)
  147. items := strings.Split(filestring, SHELF_LINE_DELIM)
  148. var shelf []Task
  149. for i := range items {
  150. sect := strings.Split(items[i], SHELF_COL_DELIM)
  151. if len(sect) < 6 {
  152. continue
  153. }
  154. var id int
  155. var due time.Time
  156. var done bool
  157. var pri int
  158. var err error
  159. id, err = strconv.Atoi(sect[0])
  160. due, err = time.Parse(TIME_FORMAT, sect[3])
  161. done, err = strconv.ParseBool(sect[4])
  162. pri, err = strconv.Atoi(sect[5])
  163. if err != nil {
  164. log.Fatal("Couldnt parse from filesystem shelf", err)
  165. }
  166. shelf = append(shelf, Task{
  167. Id: id,
  168. Title: sect[1],
  169. Desc: sect[2],
  170. Due: due,
  171. Done: done,
  172. Priority: pri,
  173. })
  174. }
  175. return shelf
  176. }
  177. /*
  178. Helper function to marshal the tasks to the custom format
  179. :param tasks: an array of Task structs
  180. :returns: a byte array
  181. */
  182. func marshalTaskToShelf(tasks []Task, templ *template.Template) []byte {
  183. var bw bytes.Buffer
  184. for i := range tasks {
  185. err := templ.Execute(&bw, tasks[i])
  186. if err != nil {
  187. log.Fatal("Error parsing data into template: ", err)
  188. }
  189. // dynamically allocating, no need for the read delim
  190. _, err = bw.Write([]byte(SHELF_LINE_DELIM))
  191. if err != nil {
  192. log.Fatal("Error parsing data into template: ", err)
  193. }
  194. }
  195. return bw.Bytes()
  196. }