Skip to content
This repository has been archived by the owner on Dec 18, 2020. It is now read-only.

Commit

Permalink
Merge pull request #89 from phrase/internal-pkgs-prompt-spinner
Browse files Browse the repository at this point in the history
Move prompt and spinner functionality into their own respective internal packages
  • Loading branch information
tobstarr authored Mar 3, 2017
2 parents ea03e5f + 244c928 commit 62c380c
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 365 deletions.
23 changes: 13 additions & 10 deletions init.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"strings"

"github.com/phrase/phraseapp-client/internal/print"
"github.com/phrase/phraseapp-client/internal/prompt"
"github.com/phrase/phraseapp-client/internal/spinner"
"github.com/phrase/phraseapp-go/phraseapp"
"gopkg.in/yaml.v2"
)
Expand Down Expand Up @@ -110,7 +112,7 @@ func (cmd *InitCommand) askForToken() error {

token := ""
for {
err := prompt("Please enter your API access token (you can generate one in your profile at phraseapp.com): ", &token)
err := prompt.P("Please enter your API access token (you can generate one in your profile at phraseapp.com):", &token)
if err != nil {
continue
}
Expand Down Expand Up @@ -150,12 +152,13 @@ func (cmd *InitCommand) selectProject() error {
return err
}

withSpinner("Loading projects... ", func(taskFinished chan<- struct{}) {
fmt.Print("Loading projects... ")
spinner.While(func() {
projects, err := client.ProjectsList(1, 25)
taskResult <- projects
taskErr <- err
taskFinished <- struct{}{}
})
fmt.Println()

projects := <-taskResult
if err := <-taskErr; err != nil {
Expand All @@ -177,7 +180,7 @@ func (cmd *InitCommand) selectProject() error {

selection := 0
for {
err = prompt(fmt.Sprintf("Select project: (%v-%v) ", 1, len(projects)+1), &selection)
err = prompt.P(fmt.Sprintf("Select project: (%v-%v)", 1, len(projects)+1), &selection)
if err != nil {
continue
}
Expand Down Expand Up @@ -208,7 +211,7 @@ func (cmd *InitCommand) newProject() error {
}

for {
err := prompt("Enter the name of the new project: ", params.Name)
err := prompt.P("Enter the name of the new project:", params.Name)
if err == nil {
break
}
Expand Down Expand Up @@ -253,11 +256,11 @@ func (cmd *InitCommand) selectFormat() error {
if cmd.FileFormat != nil && cmd.FileFormat.Name != "" {
promptText += fmt.Sprintf(" or leave blank to use the default, %s)", cmd.FileFormat.Name)
}
promptText += "): "
promptText += "):"

selection := 0
for {
err = prompt(promptText, &selection)
err = prompt.P(promptText, &selection)
if err != nil {
if cmd.FileFormat != nil && cmd.FileFormat.Name != "" {
break
Expand Down Expand Up @@ -286,7 +289,7 @@ func (cmd *InitCommand) configureSources() error {

pushPath := ""
for {
err := promptWithDefault("Source file path: ", &pushPath, cmd.FileFormat.DefaultFile)
err := prompt.WithDefault("Source file path:", &pushPath, cmd.FileFormat.DefaultFile)
if err != nil {
return err
}
Expand Down Expand Up @@ -317,7 +320,7 @@ func (cmd *InitCommand) configureTargets() error {

pullPath := ""
for {
err := promptWithDefault("Target file path: ", &pullPath, cmd.FileFormat.DefaultFile)
err := prompt.WithDefault("Target file path:", &pullPath, cmd.FileFormat.DefaultFile)
if err != nil {
return err
}
Expand Down Expand Up @@ -373,7 +376,7 @@ func (cmd *InitCommand) writeConfig() error {
fmt.Println()

pushNow := ""
err = promptWithDefault("Do you want to upload your locales now for the first time? (y/n) ", &pushNow, "y")
err = prompt.WithDefault("Do you want to upload your locales now for the first time? (y/n)", &pushNow, "y")
if pushNow == "y" {
err = firstPush()
if err != nil {
Expand Down
18 changes: 9 additions & 9 deletions prompt.go → internal/prompt/prompt.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package main
package prompt

import (
"bufio"
"fmt"
"io"
"os"
)

var stdin = bufio.NewReader(os.Stdin)

// prompt prints msg, then reads a line of user input. The input line is then scanned into the args using fmt.Sscan().
// P prints msg, then reads a line of user input. The input line is then scanned into the args using fmt.Sscan().
//
// This doesn't use fmt.Scanln() because prompt() is often called in a loop (running until user input is valid)
// and Scanln returns two seperate errors for example when scanning into one integer and "a\n" is read from stdin,
// resulting in the prompt message being printed twice.
func prompt(msg string, args ...interface{}) error {
fmt.Print(msg)
func P(msg string, args ...interface{}) error {
fmt.Print(msg + " ")

line, err := stdin.ReadString('\n')
if err != nil {
Expand All @@ -25,12 +26,11 @@ func prompt(msg string, args ...interface{}) error {
return err
}

func promptWithDefault(prompt string, arg *string, defaultValue string) error {
fmt.Print(prompt)
fmt.Printf("[default %v] ", defaultValue)
// WithDefault prints msg, then parses a line of user input into
func WithDefault(msg string, arg *string, defaultValue string) error {
err := P(msg+" "+fmt.Sprintf("[default %v]", defaultValue), arg)

n, err := fmt.Scanln(arg)
if n != 1 {
if err == io.EOF {
*arg = defaultValue
return nil
}
Expand Down
24 changes: 14 additions & 10 deletions spinner.go → internal/spinner/spinner.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
package main
package spinner

import (
"fmt"
"time"
)

func withSpinner(msg string, task func(finished chan<- struct{})) {
// start task
finished := make(chan struct{})
go task(finished)
// While executes f, displays an animated spinner while f runs, and stops when f returns.
func While(f func()) {
c := make(chan struct{})

fmt.Print(msg)
go func(c chan<- struct{}) {
defer close(c)
f()
}(c)

Until(c)
}

// Until displays an animated spinner until reading from c succeeds. It is recommended to simply close c to achieve this.
func Until(c <-chan struct{}) {
// start spinning animation
stop := make(chan struct{})
go spin(stop)

// wait for task to finish, then tell spinner to stop
stop <- <-finished
stop <- <-c
// wait for spinner to stop
<-stop

fmt.Println()
return
}

// spin animates a spinner until it receives something on the stop channel. It then clears the spinning character and closes the stop channel, signaling that it's done.
Expand Down
10 changes: 10 additions & 0 deletions internal/updatechecker/update_checker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ func TestUpdateChecker_GetLatestVersion_withInvalidCache(t *testing.T) {
}
}

func TestUpdateChecker_GetLatestVersion_withError(t *testing.T) {
tuc, _, cleanup := newTestUpateChecker("", "", "", t)
defer cleanup()

_, err := tuc.getLatestVersion()
if err == nil {
t.Errorf("expected an error, but got nil")
}
}

func TestUpdateChecker_CheckForUpdate_withUpdateAvailable(t *testing.T) {
tuc, out, cleanup := newTestUpateChecker("1.1.3", "2.0.0", "", t)
defer cleanup()
Expand Down
6 changes: 4 additions & 2 deletions push.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/jpillora/backoff"
"github.com/phrase/phraseapp-client/internal/print"
"github.com/phrase/phraseapp-client/internal/spinner"
"github.com/phrase/phraseapp-client/internal/stringz"
"github.com/phrase/phraseapp-go/phraseapp"
)
Expand Down Expand Up @@ -109,12 +110,13 @@ func (source *Source) Push(client *phraseapp.Client, waitForResults bool) error
taskResult := make(chan string, 1)
taskErr := make(chan error, 1)

withSpinner("Waiting for your file to be processed... ", func(taskFinished chan<- struct{}) {
fmt.Print("Waiting for your file to be processed... ")
spinner.While(func() {
result, err := getUploadResult(client, source.ProjectID, upload)
taskResult <- result
taskErr <- err
taskFinished <- struct{}{}
})
fmt.Println()

if err := <-taskErr; err != nil {
return err
Expand Down
93 changes: 0 additions & 93 deletions stack_trace.go

This file was deleted.

37 changes: 0 additions & 37 deletions stack_trace_item.go

This file was deleted.

Loading

0 comments on commit 62c380c

Please sign in to comment.