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  }