github.com/jchengjr77/canaveral@v1.0.1-0.20200715160102-ea9245d1a2fb/git/gitRemind.go (about) 1 package git 2 3 import ( 4 "bufio" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "reflect" 12 "strconv" 13 14 "github.com/jchengjr77/canaveral/lib" 15 ) 16 17 // * tested 18 func loadReminders() (res map[string]interface{}, finalErr error) { 19 // defer a recover function that returns the thrown error 20 defer func() { 21 if r := recover(); r != nil { 22 finalErr = r.(error) 23 } 24 }() 25 if !lib.FileExists(".remind.json") { 26 return make(map[string]interface{}), nil 27 } 28 remindContents, err := ioutil.ReadFile(".remind.json") 29 lib.Check(err) 30 var reminders map[string]interface{} 31 err = json.Unmarshal(remindContents, &reminders) 32 if err != nil && err.Error() == "unexpected end of JSON input" { 33 // file is empty 34 reminders = make(map[string]interface{}) 35 } else { 36 lib.Check(err) 37 } 38 return reminders, nil 39 } 40 41 // * tested 42 func addReminder(file string, reminder string) (finalErr error) { 43 // defer a recover function that returns the thrown error 44 defer func() { 45 if r := recover(); r != nil { 46 finalErr = r.(error) 47 } 48 }() 49 reminders, err := loadReminders() 50 lib.Check(err) 51 if _, found := reminders[file]; !found { 52 reminders[file] = []string{reminder} 53 } else { 54 stored := reflect.ValueOf(reminders[file]) 55 var new []string = make([]string, stored.Len()+1) 56 for i := 0; i < stored.Len(); i++ { 57 adding := fmt.Sprintf("%v", stored.Index(i)) 58 if adding == reminder { 59 fmt.Printf("\"%s\" is already a stored reminder for this file\n", reminder) 60 return 61 } 62 new[i] = fmt.Sprintf("%v", stored.Index(i)) 63 } 64 new[stored.Len()] = reminder 65 reminders[file] = new 66 } 67 jsonData, err := json.Marshal(reminders) 68 lib.Check(err) 69 fmt.Printf("Will remind you, \"%s\" when you commit %s\n", reminder, file) 70 // This is not that robust of a solution because it rewrites the entire file 71 // ! Research ways to improve this 72 err = ioutil.WriteFile(".remind.json", jsonData, 0644) 73 lib.Check(err) 74 return nil 75 } 76 77 func confirmPrint(stdin io.Reader) (res bool, finalErr error) { 78 // defer a recover function that returns the thrown error 79 defer func() { 80 if r := recover(); r != nil { 81 finalErr = r.(error) 82 } 83 }() 84 fmt.Printf("You have reminders stored for this file. Would you like to display them? ('y' or 'n')> ") 85 reader := bufio.NewReader(stdin) 86 response, err := reader.ReadByte() 87 lib.Check(err) 88 return (response == 'y'), nil 89 } 90 91 // ? untested but low priority because just prints info + manual tests are good 92 func checkReminders(file string, forcePrint bool, reminders map[string]interface{}) (res bool, finalErr error) { 93 // defer a recover function that returns the thrown error 94 defer func() { 95 if r := recover(); r != nil { 96 finalErr = r.(error) 97 } 98 }() 99 var err error 100 if !lib.FileExists(".remind.json") { 101 return false, nil 102 } 103 fetched, found := reminders[file] 104 stored := reflect.ValueOf(fetched) 105 print := false 106 if !forcePrint { 107 if found { 108 print, err = confirmPrint(os.Stdin) 109 lib.Check(err) 110 } 111 } 112 if forcePrint || print { 113 fmt.Printf("=====Printing reminders for %s=====\n", file) 114 if !found || stored.Len() == 0 { 115 fmt.Println("No reminders found for", file) 116 } else { 117 var reminder string 118 for i := 0; i < stored.Len(); i++ { 119 reminder = fmt.Sprintf("%v", stored.Index(i)) 120 fmt.Printf("Reminder (%d): %s\n", i+1, reminder) 121 } 122 } 123 } 124 return forcePrint || print, nil 125 } 126 127 // CheckReminders prints all of the reminders stored for file 128 // * tested 129 func CheckReminders(file string) (finalErr error) { 130 // defer a recover function that returns the thrown error 131 defer func() { 132 if r := recover(); r != nil { 133 finalErr = r.(error) 134 } 135 }() 136 if !lib.FileExists(".remind.json") { 137 return errors.New("You don't have any reminders stored") 138 } 139 if !lib.FileExists(file) { 140 return errors.New("Cannot find file " + file) 141 } 142 arg, err := loadReminders() 143 lib.Check(err) 144 _, err = checkReminders(file, true, arg) 145 lib.Check(err) 146 return nil 147 } 148 149 // AddReminder adds a reminder to be displayed back to the user when committing 150 // to git 151 // * tested 152 func AddReminder(file, reminder string) (finalErr error) { 153 // defer a recover function that returns the thrown error 154 defer func() { 155 if r := recover(); r != nil { 156 finalErr = r.(error) 157 } 158 }() 159 var err error 160 if !lib.DirExists(".git") { 161 return errors.New("Git reminders can only be added in git repos") 162 } 163 if !lib.FileExists(file) { 164 return errors.New("Cannot find " + file) 165 } 166 if reminder == "" { 167 return errors.New("reminder cannot be empty") 168 } 169 if !lib.FileExists(".remind.json") { 170 if !lib.FileExists(".gitignore") { 171 err = lib.CreateFile(".gitignore") 172 lib.Check(err) 173 } 174 err = lib.CreateFile(".remind.json") 175 lib.Check(err) 176 err = Ignore([]string{".remind.json"}, "", "") 177 lib.Check(err) 178 } 179 err = addReminder(file, reminder) 180 lib.Check(err) 181 return nil 182 } 183 184 // * tested 185 func confirmDeleteAll(file string, stdin io.Reader) (res bool, finalErr error) { 186 // defer a recover function that returns the thrown error 187 defer func() { 188 if r := recover(); r != nil { 189 finalErr = r.(error) 190 } 191 }() 192 fmt.Printf("Are you sure you want to delete all reminders for %s ('y' or 'n')>", file) 193 reader := bufio.NewReader(stdin) 194 response, err := reader.ReadByte() 195 lib.Check(err) 196 return (response == 'y'), nil 197 } 198 199 // DelReminder deletes the specified reminder 200 // * tested 201 func DelReminder(file, reminder string) (finalErr error) { 202 // defer a recover function that returns the thrown error 203 defer func() { 204 if r := recover(); r != nil { 205 finalErr = r.(error) 206 } 207 }() 208 if !lib.FileExists(".remind.json") { 209 return errors.New("No reminders found") 210 } 211 reminders, err := loadReminders() 212 lib.Check(err) 213 fetched, found := reminders[file] 214 stored := reflect.ValueOf(fetched) 215 216 if !found || stored.Len() == 0 { 217 return errors.New("Couldn't find reminder: \"" + reminder + "\" for file " + file) 218 } 219 220 if reminder == "" { 221 res, err := confirmDeleteAll(file, os.Stdin) 222 lib.Check(err) 223 if res { 224 delete(reminders, file) 225 jsonData, err := json.Marshal(reminders) 226 // This is not that robust of a solution because it rewrites the entire file 227 // ! Research ways to improve this 228 err = ioutil.WriteFile(".remind.json", jsonData, 0644) 229 lib.Check(err) 230 } 231 return nil 232 } 233 234 if val, err := strconv.Atoi(reminder); err == nil { 235 if val <= 0 { 236 return errors.New("Reminder number must be positive") 237 } 238 if val > stored.Len() { 239 return errors.New(file + " has fewer than " + reminder + " reminders stored.") 240 } 241 reminder = fmt.Sprintf("%v", stored.Index(val-1)) 242 } 243 244 var ret []string 245 found = false 246 247 for i := 0; i < stored.Len(); i++ { 248 curr := fmt.Sprintf("%v", stored.Index(i)) 249 if curr != reminder { 250 ret = append(ret, curr) 251 } else { 252 found = true 253 } 254 } 255 if len(ret) == 0 { 256 delete(reminders, file) 257 jsonData, err := json.Marshal(reminders) 258 // This is not that robust of a solution because it rewrites the entire file 259 // ! Research ways to improve this 260 err = ioutil.WriteFile(".remind.json", jsonData, 0644) 261 lib.Check(err) 262 return nil 263 } 264 reminders[file] = ret 265 if !found { 266 fmt.Printf("Couldn't find reminder \"%s\" for file %s\n", reminder, file) 267 return nil 268 } 269 270 fmt.Printf("Deleting reminder \"%s\" from file %s\n", reminder, file) 271 jsonData, err := json.Marshal(reminders) 272 // This is not that robust of a solution because it rewrites the entire file 273 // ! Research ways to improve this 274 err = ioutil.WriteFile(".remind.json", jsonData, 0644) 275 lib.Check(err) 276 return nil 277 }