savestate.go 5.2 KB

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