From d31b22aed05e3acca1dd2c076cc2f082b53c785f Mon Sep 17 00:00:00 2001 From: Sung Won Cho Date: Sun, 7 Jan 2018 18:18:47 +1100 Subject: [PATCH] Improve output and edit command (#52) * Refine output * Show note content while editing * v0.2.0-alpha.2 --- Gopkg.lock | 2 +- cmd/add/add.go | 4 +-- cmd/books/books.go | 6 ++-- cmd/edit/edit.go | 76 +++++++++++++++++++++++++---------------- cmd/login/login.go | 7 +++- cmd/ls/ls.go | 6 ++-- cmd/remove/remove.go | 21 +++++++----- cmd/root/root.go | 6 ++-- cmd/sync/sync.go | 27 +++++++++------ cmd/use/use.go | 6 ++-- core/core.go | 2 +- log/log.go | 39 +++++++++++++++++++++ main.go | 2 ++ main_test.go | 80 +++++++++++++++++++++++++++++++++++++++++--- upgrade/upgrade.go | 30 ++++++++++++----- utils/utils.go | 33 +++++++++++++----- 16 files changed, 261 insertions(+), 86 deletions(-) create mode 100644 log/log.go diff --git a/Gopkg.lock b/Gopkg.lock index e0587cc0..6b675797 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -52,6 +52,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "dc231aa7e3a8f7286e2b20dac68d566c430b0c7a3a0de97c3a3534280a6f85b4" + inputs-digest = "a6df84725e42f485baf9261b9d48ad577c4687d5d7f5b90353d1e40606f40e26" solver-name = "gps-cdcl" solver-version = 1 diff --git a/cmd/add/add.go b/cmd/add/add.go index 4e6e803d..391e6438 100644 --- a/cmd/add/add.go +++ b/cmd/add/add.go @@ -1,11 +1,11 @@ package add import ( - "fmt" "time" "github.com/dnote-io/cli/core" "github.com/dnote-io/cli/infra" + "github.com/dnote-io/cli/log" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -69,7 +69,7 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { return errors.Wrap(err, "Failed to write note") } - fmt.Printf("[+] Added to %s\n", bookName) + log.Infof("added to %s\n", bookName) return nil } } diff --git a/cmd/books/books.go b/cmd/books/books.go index 506d9fad..cb4fdc23 100644 --- a/cmd/books/books.go +++ b/cmd/books/books.go @@ -5,6 +5,7 @@ import ( "github.com/dnote-io/cli/core" "github.com/dnote-io/cli/infra" + "github.com/dnote-io/cli/log" "github.com/spf13/cobra" ) @@ -37,13 +38,12 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { for _, book := range books { if book == currentBook { - fmt.Printf("* %v\n", book) + fmt.Printf(" %s\033[%dm%s\033[0m\n", "* ", log.ColorBlue, book) } else { - fmt.Printf(" %v\n", book) + fmt.Printf(" %s%s\n", " ", book) } } return nil } - } diff --git a/cmd/edit/edit.go b/cmd/edit/edit.go index e239f518..23a5b495 100644 --- a/cmd/edit/edit.go +++ b/cmd/edit/edit.go @@ -1,22 +1,28 @@ package edit import ( - "fmt" "strconv" "time" "github.com/dnote-io/cli/core" "github.com/dnote-io/cli/infra" + "github.com/dnote-io/cli/log" + "github.com/dnote-io/cli/utils" "github.com/pkg/errors" "github.com/spf13/cobra" ) +var newContent string + var example = ` * Edit the note by index in the current book - dnote edit 3 "new content" + dnote edit 3 * Edit the note by index in a certain book - dnote edit JS 3 "new content"` + dnote edit js 3 + + * Skip the prompt by providing new content directly + dntoe eidt js 3 -c "new content"` func NewCmd(ctx infra.DnoteCtx) *cobra.Command { cmd := &cobra.Command{ @@ -28,11 +34,14 @@ func NewCmd(ctx infra.DnoteCtx) *cobra.Command { RunE: newRun(ctx), } + f := cmd.Flags() + f.StringVarP(&newContent, "content", "c", "", "The new content for the note") + return cmd } func preRun(cmd *cobra.Command, args []string) error { - if len(args) < 2 { + if len(args) < 1 { return errors.New("Missing argument") } @@ -47,54 +56,63 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { } var targetBookName string - var index int - var content string + var targetIdx int - if len(args) == 2 { + if len(args) == 1 { targetBookName, err = core.GetCurrentBook(ctx) if err != nil { return err } - index, err = strconv.Atoi(args[0]) + targetIdx, err = strconv.Atoi(args[0]) if err != nil { return err } - content = args[1] - } else if len(args) == 3 { + } else if len(args) == 2 { targetBookName = args[0] - index, err = strconv.Atoi(args[1]) + targetIdx, err = strconv.Atoi(args[1]) if err != nil { return err } - content = args[2] } targetBook, exists := dnote[targetBookName] if !exists { - return errors.Errorf("Book with the name '%s' does not exist", targetBookName) + return errors.Errorf("Book %s does not exist", targetBookName) + } + if targetIdx > len(targetBook.Notes)-1 { + return errors.Errorf("Book %s does not have note with index %d", targetBookName, targetIdx) + } + targetNote := targetBook.Notes[targetIdx] + + if newContent == "" { + log.Printf("content: %s\n", targetNote.Content) + log.Printf("new content: ") + + newContent, err = utils.GetInput() + if err != nil { + return errors.Wrap(err, "Failed to get new content") + } } ts := time.Now().Unix() - for i, note := range dnote[targetBookName].Notes { - if i == index { - note.Content = content - note.EditedOn = ts - dnote[targetBookName].Notes[i] = note + targetNote.Content = utils.SanitizeContent(newContent) + targetNote.EditedOn = ts + targetBook.Notes[targetIdx] = targetNote + dnote[targetBookName] = targetBook - err := core.LogActionEditNote(ctx, note.UUID, targetBook.Name, note.Content, ts) - if err != nil { - return errors.Wrap(err, "Failed to log action") - } - - err = core.WriteDnote(ctx, dnote) - fmt.Printf("Edited Note : %d \n", index) - return err - } + err = core.LogActionEditNote(ctx, targetNote.UUID, targetBook.Name, targetNote.Content, ts) + if err != nil { + return errors.Wrap(err, "Failed to log action") } - // If loop finishes without returning, note did not exist - fmt.Println("Error : The note with that index is not found.") + err = core.WriteDnote(ctx, dnote) + if err != nil { + return errors.Wrap(err, "Failed to write dnote") + } + + log.Info("edited the note") + return nil } } diff --git a/cmd/login/login.go b/cmd/login/login.go index e892786e..527b64d0 100644 --- a/cmd/login/login.go +++ b/cmd/login/login.go @@ -5,6 +5,7 @@ import ( "github.com/dnote-io/cli/core" "github.com/dnote-io/cli/infra" + "github.com/dnote-io/cli/log" "github.com/spf13/cobra" ) @@ -24,7 +25,9 @@ func NewCmd(ctx infra.DnoteCtx) *cobra.Command { func newRun(ctx infra.DnoteCtx) core.RunEFunc { return func(cmd *cobra.Command, args []string) error { - fmt.Print("Please enter your APIKey: ") + log.Printf("welcome to the dnote cloud :)\n") + log.Printf("you can get the api key from https://dnote.io\n") + log.Infof("api key: ") var apiKey string fmt.Scanln(&apiKey) @@ -40,6 +43,8 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { return err } + log.Infof("credential configured\n") + return nil } diff --git a/cmd/ls/ls.go b/cmd/ls/ls.go index f5c758f4..b6a0d7ad 100644 --- a/cmd/ls/ls.go +++ b/cmd/ls/ls.go @@ -2,9 +2,9 @@ package ls import ( "fmt" - "github.com/dnote-io/cli/core" "github.com/dnote-io/cli/infra" + "github.com/dnote-io/cli/log" "github.com/spf13/cobra" ) @@ -43,7 +43,7 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { } } - fmt.Printf("On note %s\n", bookName) + log.Infof("on book %s\n", bookName) dnote, err := core.GetDnote(ctx) if err != nil { @@ -53,7 +53,7 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { for k, v := range dnote { if k == bookName { for i, note := range v.Notes { - fmt.Printf("* [%d] - %s\n", i, note.Content) + fmt.Printf(" \033[%dm(%d)\033[0m %s\n", log.ColorYellow, i, note.Content) } } } diff --git a/cmd/remove/remove.go b/cmd/remove/remove.go index 5e74ed42..edcab370 100644 --- a/cmd/remove/remove.go +++ b/cmd/remove/remove.go @@ -6,6 +6,7 @@ import ( "github.com/dnote-io/cli/core" "github.com/dnote-io/cli/infra" + "github.com/dnote-io/cli/log" "github.com/dnote-io/cli/utils" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -38,7 +39,10 @@ func NewCmd(ctx infra.DnoteCtx) *cobra.Command { func newRun(ctx infra.DnoteCtx) core.RunEFunc { return func(cmd *cobra.Command, args []string) error { if targetBookName != "" { - book(ctx, targetBookName) + err := book(ctx, targetBookName) + if err != nil { + return errors.Wrap(err, "Failed to delete the book") + } } else { if len(args) < 2 { return errors.New("Missing argument") @@ -79,13 +83,14 @@ func note(ctx infra.DnoteCtx, index int, bookName string) error { } content := notes[index].Content - fmt.Printf("Deleting note: %s\n", content) + log.Printf("content: \"%s\"\n", content) - ok, err := utils.AskConfirmation("Are you sure?") + ok, err := utils.AskConfirmation("remove this note?") if err != nil { return errors.Wrap(err, "Failed to get confirmation") } if !ok { + log.Warnf("aborted by user\n") return nil } @@ -102,17 +107,18 @@ func note(ctx infra.DnoteCtx, index int, bookName string) error { return errors.Wrap(err, "Failed to write dnote") } - fmt.Printf("Deleted!\n") + log.Infof("removed from %s\n", bookName) return nil } // book deletes a book with the given name func book(ctx infra.DnoteCtx, bookName string) error { - ok, err := utils.AskConfirmation("Are you sure?") + ok, err := utils.AskConfirmation(fmt.Sprintf("delete book '%s' and all its notes?", bookName)) if err != nil { return err } if !ok { + log.Warnf("aborted by user\n") return nil } @@ -134,11 +140,10 @@ func book(ctx infra.DnoteCtx, bookName string) error { return err } - fmt.Printf("[-] Deleted book : %s \n", bookName) + log.Info("removed book\n") return nil } } - fmt.Println("Error : The book with that name is not found.") - return nil + return errors.Errorf("Book '%s' was not found", bookName) } diff --git a/cmd/root/root.go b/cmd/root/root.go index 9338a02f..139d50a7 100644 --- a/cmd/root/root.go +++ b/cmd/root/root.go @@ -10,8 +10,10 @@ import ( ) var root = &cobra.Command{ - Use: "dnote", - Short: "Dnote - Instantly capture what you learn while coding", + Use: "dnote", + Short: "Dnote - Instantly capture what you learn while coding", + SilenceErrors: true, + SilenceUsage: true, } // Register adds a new command diff --git a/cmd/sync/sync.go b/cmd/sync/sync.go index bfd01540..654fd47e 100644 --- a/cmd/sync/sync.go +++ b/cmd/sync/sync.go @@ -10,6 +10,7 @@ import ( "github.com/dnote-io/cli/core" "github.com/dnote-io/cli/infra" + "github.com/dnote-io/cli/log" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -48,23 +49,27 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { if err != nil { return errors.Wrap(err, "Failed to read the timestamp") } + actions, err := core.ReadActionLog(ctx) + if err != nil { + return errors.Wrap(err, "Failed to read the action log") + } if config.APIKey == "" { fmt.Println("Login required. Please run `dnote login`") return nil } - fmt.Println("Compressing dnote...") - payload, err := getPayload(ctx, timestamp) + payload, err := getPayload(actions, timestamp) if err != nil { return errors.Wrap(err, "Failed to get dnote payload") } - fmt.Println("Syncing with the server...") + log.Infof("writing changes (total %d).", len(actions)) resp, err := postActions(ctx, config.APIKey, payload) if err != nil { return errors.Wrap(err, "Failed to post to the server ") } + fmt.Println(" done.") body, err := ioutil.ReadAll(resp.Body) if err != nil { @@ -84,10 +89,12 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { return errors.Wrap(err, "Failed to unmarshal payload") } + log.Infof("resolving delta (total %d).", len(respData.Actions)) err = core.ReduceAll(ctx, respData.Actions) if err != nil { return errors.Wrap(err, "Failed to reduce returned actions") } + fmt.Println(" done.") // Update bookmark ts, err := core.ReadTimestamp(ctx) @@ -98,7 +105,7 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { return errors.Wrap(err, "Failed to update bookmark") } - fmt.Println("Successfully synced all notes") + log.Infof("synced\n") if err := core.ClearActionLog(ctx); err != nil { return errors.Wrap(err, "Failed to clear the action log") } @@ -107,15 +114,15 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { } } -func getPayload(ctx infra.DnoteCtx, timestamp infra.Timestamp) (*bytes.Buffer, error) { - actions, err := compressActions(ctx) +func getPayload(actions []core.Action, timestamp infra.Timestamp) (*bytes.Buffer, error) { + compressedActions, err := compressActions(actions) if err != nil { return &bytes.Buffer{}, errors.Wrap(err, "Failed to compress actions") } payload := syncPayload{ Bookmark: timestamp.Bookmark, - Actions: actions, + Actions: compressedActions, } b, err := json.Marshal(payload) @@ -127,10 +134,10 @@ func getPayload(ctx infra.DnoteCtx, timestamp infra.Timestamp) (*bytes.Buffer, e return ret, nil } -func compressActions(ctx infra.DnoteCtx) ([]byte, error) { - b, err := core.ReadActionLogContent(ctx) +func compressActions(actions []core.Action) ([]byte, error) { + b, err := json.Marshal(&actions) if err != nil { - return nil, errors.Wrap(err, "Failed to read the action log content") + return nil, errors.Wrap(err, "failed to marshal actions into JSON") } var buf bytes.Buffer diff --git a/cmd/use/use.go b/cmd/use/use.go index 851d034e..5159da06 100644 --- a/cmd/use/use.go +++ b/cmd/use/use.go @@ -1,10 +1,9 @@ package use import ( - "fmt" - "github.com/dnote-io/cli/core" "github.com/dnote-io/cli/infra" + "github.com/dnote-io/cli/log" "github.com/spf13/cobra" ) @@ -32,8 +31,7 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { return err } - fmt.Printf("Now using %s\n", targetBookName) + log.Infof("now on book '%s'\n", targetBookName) return nil } - } diff --git a/core/core.go b/core/core.go index e49b84fb..6cfd9c27 100644 --- a/core/core.go +++ b/core/core.go @@ -17,7 +17,7 @@ import ( const ( // Version is the current version of dnote - Version = "0.2.0-alpha" + Version = "0.2.0-alpha.2" // TimestampFilename is the name of the file containing upgrade info TimestampFilename = "timestamps" diff --git a/log/log.go b/log/log.go new file mode 100644 index 00000000..64cdd7db --- /dev/null +++ b/log/log.go @@ -0,0 +1,39 @@ +package log + +import ( + "fmt" +) + +var ( + ColorRed = 31 + ColorGreen = 32 + ColorYellow = 33 + ColorBlue = 34 + ColorGray = 37 +) + +var indent = " " + +func Info(msg string) { + fmt.Printf("%s\033[%dm%s\033[0m %s\n", indent, ColorBlue, "•", msg) +} + +func Infof(msg string, v ...interface{}) { + fmt.Printf("%s\033[%dm%s\033[0m %s", indent, ColorBlue, "•", fmt.Sprintf(msg, v...)) +} + +func Warnf(msg string, v ...interface{}) { + fmt.Printf("%s\033[%dm%s\033[0m %s", indent, ColorRed, "•", fmt.Sprintf(msg, v...)) +} + +func Error(msg string) { + fmt.Printf("%s\033[%dm%s\033[0m %s\n", indent, ColorRed, "⨯", msg) +} + +func Printf(msg string, v ...interface{}) { + fmt.Printf("%s\033[%dm%s\033[0m %s", indent, ColorGray, "•", fmt.Sprintf(msg, v...)) +} + +func WithPrefixf(prefixColor int, prefix, msg string, v ...interface{}) { + fmt.Printf(" \033[%dm%s\033[0m %s\n", prefixColor, prefix, fmt.Sprintf(msg, v...)) +} diff --git a/main.go b/main.go index bd5a1444..da2f62c6 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "github.com/dnote-io/cli/cmd/root" "github.com/dnote-io/cli/core" "github.com/dnote-io/cli/infra" + "github.com/dnote-io/cli/log" "github.com/pkg/errors" // commands @@ -49,6 +50,7 @@ func main() { root.Register(upgrade.NewCmd(ctx)) if err := root.Execute(); err != nil { + log.Error(err.Error()) os.Exit(1) } } diff --git a/main_test.go b/main_test.go index 4d35677b..8465d0c7 100644 --- a/main_test.go +++ b/main_test.go @@ -191,7 +191,77 @@ func TestEdit(t *testing.T) { testutils.WriteFile(ctx, "./testutils/fixtures/dnote2.json", "dnote") // Execute - runDnoteCmd(ctx, "edit", "js", "1", "foo bar") + cmd, stderr, err := newDnoteCmd(ctx, "edit", "js", "1") + if err != nil { + panic(errors.Wrap(err, "Failed to get command")) + } + stdin, err := cmd.StdinPipe() + if err != nil { + panic(errors.Wrap(err, "Failed to get stdin %s")) + } + defer stdin.Close() + + // Start the program + err = cmd.Start() + if err != nil { + panic(errors.Wrap(err, "Failed to start command")) + } + + // enter content + _, err = io.WriteString(stdin, "foo bar\n") + if err != nil { + panic(errors.Wrap(err, "Failed to write to stdin")) + } + + err = cmd.Wait() + if err != nil { + panic(errors.Wrapf(err, "Failed to run command %s", stderr.String())) + } + + // Test + dnote, err := core.GetDnote(ctx) + if err != nil { + t.Fatal(errors.Wrap(err, "Failed to get dnote")) + } + actions, err := core.ReadActionLog(ctx) + if err != nil { + t.Fatal(errors.Wrap(err, "Failed to read actions")) + } + + book := dnote["js"] + action := actions[0] + + var actionData core.EditNoteData + err = json.Unmarshal(action.Data, &actionData) + if err != nil { + log.Fatalln("Failed to unmarshal the action data: %s", err) + } + + testutils.AssertEqual(t, len(actions), 1, "There should be 1 action") + testutils.AssertEqual(t, action.Type, core.ActionEditNote, "action type mismatch") + testutils.AssertEqual(t, actionData.Content, "foo bar", "action data name mismatch") + testutils.AssertEqual(t, actionData.BookName, "js", "action data book_name mismatch") + testutils.AssertEqual(t, actionData.NoteUUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "action data note_uuis mismatch") + testutils.AssertNotEqual(t, action.Timestamp, 0, "action timestamp mismatch") + testutils.AssertEqual(t, len(book.Notes), 2, "Book should have one note") + testutils.AssertEqual(t, book.Notes[0].UUID, "43827b9a-c2b0-4c06-a290-97991c896653", "Note should have UUID") + testutils.AssertEqual(t, book.Notes[0].Content, "Booleans have toString()", "Note content mismatch") + testutils.AssertEqual(t, book.Notes[1].UUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "Note should have UUID") + testutils.AssertEqual(t, book.Notes[1].Content, "foo bar", "Note content mismatch") + testutils.AssertNotEqual(t, book.Notes[1].EditedOn, int64(0), "Note edited_on mismatch") +} +func TestEdit_ContentFlag(t *testing.T) { + // Setup + ctx := testutils.InitCtx("./tmp") + testutils.SetupTmp(ctx) + defer testutils.ClearTmp(ctx) + + // init files by running root command + runDnoteCmd(ctx) + testutils.WriteFile(ctx, "./testutils/fixtures/dnote2.json", "dnote") + + // Execute + runDnoteCmd(ctx, "edit", "js", "1", "-c", "foo bar") // Test dnote, err := core.GetDnote(ctx) @@ -253,8 +323,8 @@ func TestRemoveNote(t *testing.T) { panic(errors.Wrap(err, "Failed to start command")) } - // Hit return to confirm - _, err = io.WriteString(stdin, "\n") + // confirm + _, err = io.WriteString(stdin, "y\n") if err != nil { panic(errors.Wrap(err, "Failed to write to stdin")) } @@ -326,8 +396,8 @@ func TestRemoveBook(t *testing.T) { panic(errors.Wrap(err, "Failed to start command")) } - // Hit return to confirm - _, err = io.WriteString(stdin, "\n") + // confirm + _, err = io.WriteString(stdin, "y\n") if err != nil { panic(errors.Wrap(err, "Failed to write to stdin")) } diff --git a/upgrade/upgrade.go b/upgrade/upgrade.go index 3226027c..6ef0ef7b 100644 --- a/upgrade/upgrade.go +++ b/upgrade/upgrade.go @@ -13,6 +13,7 @@ import ( "github.com/dnote-io/cli/core" "github.com/dnote-io/cli/infra" + "github.com/dnote-io/cli/log" "github.com/dnote-io/cli/utils" "github.com/google/go-github/github" "github.com/pkg/errors" @@ -69,7 +70,7 @@ func AutoUpgrade(ctx infra.DnoteCtx) error { } if shouldCheck { - willCheck, err := utils.AskConfirmation("Would you like to check for an update?") + willCheck, err := utils.AskConfirmation("check for upgrade?") if err != nil { return err } @@ -86,6 +87,8 @@ func AutoUpgrade(ctx infra.DnoteCtx) error { } func Upgrade(ctx infra.DnoteCtx) error { + log.Infof("current version is %s\n", core.Version) + // Fetch the latest version gh := github.NewClient(nil) releases, _, err := gh.Repositories.ListReleases(context.Background(), "dnote-io", "cli", nil) @@ -101,22 +104,31 @@ func Upgrade(ctx infra.DnoteCtx) error { return err } + log.Infof("latest version is %s\n", latestVersion) + // Check if up to date if latestVersion == core.Version { - fmt.Printf("Up-to-date: %s\n", core.Version) - core.InitTimestampFile(ctx) + log.Info("you are up-to-date") + err = touchLastUpgrade(ctx) + if err != nil { + return errors.Wrap(err, "Failed to update the upgrade timestamp") + } + return nil } asset := getAsset(latest) if asset == nil { - core.InitTimestampFile(ctx) - fmt.Printf("Could not find the release for %s %s", runtime.GOOS, runtime.GOARCH) - return nil + err = touchLastUpgrade(ctx) + if err != nil { + return errors.Wrap(err, "Failed to update the upgrade timestamp") + } + + return errors.Errorf("Could not find the release for %s %s", runtime.GOOS, runtime.GOARCH) } // Download temporary file - fmt.Printf("Downloading: %s\n", latestVersion) + log.Infof("Downloading: %s\n", latestVersion) tmpPath := path.Join(os.TempDir(), "dnote_update") out, err := os.Create(tmpPath) @@ -158,7 +170,7 @@ func Upgrade(ctx infra.DnoteCtx) error { return errors.Wrap(err, "Upgrade is done, but failed to update the last_upgrade timestamp.") } - fmt.Printf("Updated: v%s -> v%s\n", core.Version, latestVersion) - fmt.Println("Changelog: https://github.com/dnote-io/cli/releases") + log.Infof("Updated: v%s -> v%s\n", core.Version, latestVersion) + log.Infof("Changelog: https://github.com/dnote-io/cli/releases") return nil } diff --git a/utils/utils.go b/utils/utils.go index 2dfa7d5b..13f0a1f3 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -2,14 +2,15 @@ package utils import ( "bufio" - "fmt" "io" "io/ioutil" "math/rand" "os" "path/filepath" + "strings" "time" + "github.com/dnote-io/cli/log" "github.com/pkg/errors" "github.com/satori/go.uuid" ) @@ -27,18 +28,25 @@ func GenerateUID() string { return uuid.NewV4().String() } -func AskConfirmation(question string) (bool, error) { - fmt.Printf("%s [Y/n]: ", question) - +func GetInput() (string, error) { reader := bufio.NewReader(os.Stdin) - res, err := reader.ReadString('\n') + input, err := reader.ReadString('\n') if err != nil { - return false, err + return "", errors.Wrap(err, "Failed to read stdin") } - ok := res == "y\n" || res == "Y\n" || res == "\n" + return input, nil +} - return ok, nil +func AskConfirmation(question string) (bool, error) { + log.Printf("%s (y/N): ", question) + + res, err := GetInput() + if err != nil { + return false, errors.Wrap(err, "Failed to get user input") + } + + return res == "y\n", nil } // FileExists checks if the file exists at the given path @@ -144,3 +152,12 @@ func CopyDir(src, dest string) error { return nil } + +func SanitizeContent(s string) string { + var ret string + + ret = strings.Replace(s, "\n", "", -1) + ret = strings.Replace(ret, "\r\n", "", -1) + + return ret +}