Skip to content

Commit 0854081

Browse files
sanialescmaglie
authored andcommitted
Fixes #77 and adds a beautiful loading spinner 80s style 🌎
1 parent e1faa83 commit 0854081

File tree

2 files changed

+180
-38
lines changed

2 files changed

+180
-38
lines changed

‎cmd/arduino_sketch.go

Lines changed: 102 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,18 @@ import (
3838
"os"
3939
"path/filepath"
4040
"strings"
41+
"time"
4142

4243
"github.com/bcmi-labs/arduino-cli/auth"
4344
"github.com/bcmi-labs/arduino-cli/create_client_helpers"
4445
"github.com/bgentry/go-netrc/netrc"
46+
"github.com/briandowns/spinner"
4547
homedir "github.com/mitchellh/go-homedir"
4648

4749
"github.com/bcmi-labs/arduino-modules/sketches"
4850

4951
"github.com/bcmi-labs/arduino-cli/cmd/formatter"
52+
"github.com/bcmi-labs/arduino-cli/cmd/output"
5053
"github.com/bcmi-labs/arduino-cli/common"
5154

5255
"github.com/spf13/cobra"
@@ -86,12 +89,58 @@ func executeSketchSyncCommand(cmd *cobra.Command, args []string) {
8689
os.Exit(errCoreConfig)
8790
}
8891

92+
priority := arduinoSketchSyncFlags.Priority
93+
94+
if priority == "ask-once" {
95+
if !formatter.IsCurrentFormat("text") {
96+
formatter.PrintErrorMessage("ask mode for this command is only supported using text format")
97+
os.Exit(errBadCall)
98+
}
99+
firstAsk := true
100+
for priority != "pull-remote" &&
101+
priority != "push-local" &&
102+
priority != "skip" {
103+
if !firstAsk {
104+
formatter.Print("Invalid option: " + priority)
105+
}
106+
formatter.Print("What should I do when I detect a conflict? [pull-remote | push-local | skip]")
107+
fmt.Scanln(&priority)
108+
firstAsk = false
109+
}
110+
}
111+
112+
//loader
113+
isTextMode := formatter.IsCurrentFormat("text")
114+
115+
var loader *spinner.Spinner
116+
117+
if isTextMode {
118+
loader = spinner.New(spinner.CharSets[39], 100*time.Millisecond)
119+
loader.Prefix = "Syncing Sketches... "
120+
121+
loader.Start()
122+
}
123+
124+
stopSpinner := func() {
125+
if isTextMode {
126+
loader.Stop()
127+
}
128+
}
129+
130+
startSpinner := func() {
131+
if isTextMode {
132+
loader.Start()
133+
}
134+
}
135+
89136
username, bearerToken, err := login()
90137
if err != nil {
91138
if GlobalFlags.Verbose == 0 {
92139
formatter.PrintErrorMessage("Cannot login automatically: try arduino login the run again this command")
93140
} else {
141+
stopSpinner()
94142
formatter.PrintError(err)
143+
os.Exit(errNetwork)
95144
}
96145
}
97146

@@ -101,13 +150,15 @@ func executeSketchSyncCommand(cmd *cobra.Command, args []string) {
101150
tok := "Bearer " + bearerToken
102151
resp, err := client.SearchSketches(context.Background(), createClient.SearchSketchesPath(), nil, &username, &tok)
103152
if err != nil {
153+
stopSpinner()
104154
formatter.PrintErrorMessage("Cannot get create sketches, sync failed")
105155
os.Exit(errNetwork)
106156
}
107157
defer resp.Body.Close()
108158

109159
onlineSketches, err := client.DecodeArduinoCreateSketches(resp)
110160
if err != nil {
161+
stopSpinner()
111162
formatter.PrintErrorMessage("Cannot unmarshal response from create, sync failed")
112163
os.Exit(errGeneric)
113164
}
@@ -117,32 +168,25 @@ func executeSketchSyncCommand(cmd *cobra.Command, args []string) {
117168
onlineSketchesMap[*item.Name] = item
118169
}
119170

120-
priority := arduinoSketchSyncFlags.Priority
171+
maxLength := len(sketchMap) + len(onlineSketchesMap)
121172

122-
if priority == "ask-once" {
123-
if !formatter.IsCurrentFormat("text") {
124-
formatter.PrintErrorMessage("ask mode for this command is only supported using text format")
125-
os.Exit(errBadCall)
126-
}
127-
firstAsk := true
128-
for priority != "pull-remote" &&
129-
priority != "push-local" &&
130-
priority != "skip" {
131-
if !firstAsk {
132-
formatter.Print("Invalid option: " + priority)
133-
}
134-
formatter.Print("What should I do when I detect a conflict? [pull-remote | push-local | skip]")
135-
fmt.Scanln(&priority)
136-
firstAsk = false
137-
}
173+
// create output result struct with empty arrays.
174+
result := output.SketchSyncResult{
175+
PushedSketches: make([]string, 0, maxLength),
176+
PulledSketches: make([]string, 0, maxLength),
177+
SkippedSketches: make([]string, 0, maxLength),
178+
Errors: make([]output.SketchSyncError, 0, maxLength),
138179
}
180+
139181
for _, item := range sketchMap {
140182

141183
itemOnline, hasConflict := onlineSketchesMap[item.Name]
142184
if hasConflict {
143185
item.ID = itemOnline.ID.String()
144186
//solve conflicts
145187
if priority == "ask-always" {
188+
stopSpinner()
189+
146190
if !formatter.IsCurrentFormat("text") {
147191
formatter.PrintErrorMessage("ask mode for this command is only supported using text format")
148192
os.Exit(errBadCall)
@@ -158,43 +202,50 @@ func executeSketchSyncCommand(cmd *cobra.Command, args []string) {
158202
fmt.Scanln(&priority)
159203
firstAsk = false
160204
}
205+
206+
startSpinner()
161207
}
162208
switch priority {
163209
case "push-local":
164-
if GlobalFlags.Verbose > 0 {
165-
formatter.Print("pushing edits of sketch: " + item.Name)
166-
}
167210
err := editSketch(*item, sketchbook, bearerToken)
168211
if err != nil {
169-
formatter.PrintError(err)
212+
result.Errors = append(result.Errors, output.SketchSyncError{
213+
Sketch: item.Name,
214+
Error: err,
215+
})
216+
} else {
217+
result.PushedSketches = append(result.PushedSketches, item.Name)
170218
}
171219
break
172220
case "pull-remote":
173-
if GlobalFlags.Verbose > 0 {
174-
formatter.Print("pulling " + item.Name)
175-
}
176221
err := pullSketch(itemOnline, sketchbook, bearerToken)
177222
if err != nil {
178-
formatter.PrintError(err)
223+
result.Errors = append(result.Errors, output.SketchSyncError{
224+
Sketch: item.Name,
225+
Error: err,
226+
})
227+
} else {
228+
result.PulledSketches = append(result.PulledSketches, item.Name)
179229
}
180230
break
181231
case "skip":
182-
if GlobalFlags.Verbose > 0 {
183-
formatter.Print("skipping " + item.Name)
184-
}
232+
result.SkippedSketches = append(result.SkippedSketches, item.Name)
185233
break
186234
default:
187235
priority = "skip"
188-
if GlobalFlags.Verbose > 0 {
189-
formatter.Print("Priority not recognized, using skipping by default")
190-
formatter.Print("skipping " + item.Name)
191-
}
192-
236+
result.SkippedSketches = append(result.SkippedSketches, item.Name)
193237
}
194238

195239
} else { //only local, push
196-
formatter.Print("pushing " + item.Name)
197-
pushSketch(*item, sketchbook, bearerToken)
240+
err := pushSketch(*item, sketchbook, bearerToken)
241+
if err != nil {
242+
result.Errors = append(result.Errors, output.SketchSyncError{
243+
Sketch: item.Name,
244+
Error: err,
245+
})
246+
} else {
247+
result.PushedSketches = append(result.PushedSketches, item.Name)
248+
}
198249
}
199250
}
200251
for _, item := range onlineSketches.Sketches {
@@ -203,13 +254,26 @@ func executeSketchSyncCommand(cmd *cobra.Command, args []string) {
203254
continue
204255
}
205256
//only online, pull
206-
formatter.Print("pulling " + *item.Name)
207257
err := pullSketch(item, sketchbook, bearerToken)
208258
if err != nil {
209-
formatter.PrintError(err)
259+
result.Errors = append(result.Errors, output.SketchSyncError{
260+
Sketch: *item.Name,
261+
Error: err,
262+
})
263+
} else {
264+
result.PulledSketches = append(result.PulledSketches, *item.Name)
210265
}
211266
}
212-
formatter.PrintResult("Sync Completed") // Issue # : Provide output struct to print the result in a prettier way.
267+
268+
stopSpinner()
269+
270+
// for text mode, show full info (aka String()) only if verbose > 0.
271+
// for other formats always print full info.
272+
if GlobalFlags.Verbose > 0 || !formatter.IsCurrentFormat("text") {
273+
formatter.Print(result)
274+
} else {
275+
formatter.PrintResult("Sync Completed")
276+
}
213277
}
214278

215279
func pushSketch(sketch sketches.Sketch, sketchbook string, bearerToken string) error {

‎cmd/output/sketch_structs.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* This file is part of arduino-cli.
3+
*
4+
* arduino-cli is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*
18+
* As a special exception, you may use this file as part of a free software
19+
* library without restriction. Specifically, if other files instantiate
20+
* templates or use macros or inline functions from this file, or you compile
21+
* this file and link it with other files to produce an executable, this
22+
* file does not by itself cause the resulting executable to be covered by
23+
* the GNU General Public License. This exception does not however
24+
* invalidate any other reasons why the executable file might be covered by
25+
* the GNU General Public License.
26+
*
27+
* Copyright 2017 ARDUINO AG (http://www.arduino.cc/)
28+
*/
29+
30+
package output
31+
32+
import "fmt"
33+
34+
// SketchSyncResult contains the result of an `arduino sketch sync` operation.
35+
type SketchSyncResult struct {
36+
PushedSketches []string `json:"pushedSketches,required"`
37+
PulledSketches []string `json:"pulledSketches,required"`
38+
SkippedSketches []string `json:"skippedSketches,required"`
39+
Errors []SketchSyncError `json:"errors,required"`
40+
}
41+
42+
// SketchSyncError represents an error during a `arduino sketch sync` operation.
43+
type SketchSyncError struct {
44+
Sketch string `json:"sketch,required"`
45+
Error error `json:"error,required"`
46+
}
47+
48+
// String returns a string representation of the object. For this object
49+
// it is used to provide verbose info of a sync process.
50+
func (ssr SketchSyncResult) String() string {
51+
totalSketches := len(ssr.SkippedSketches) + len(ssr.PushedSketches) + len(ssr.PulledSketches) + len(ssr.Errors)
52+
//this function iterates an array and if it's not empty pretty prints it.
53+
iterate := func(array []string, header string) string {
54+
ret := ""
55+
if len(array) > 0 {
56+
ret += fmt.Sprintln(header)
57+
for _, item := range array {
58+
ret += fmt.Sprintln(" -", item)
59+
}
60+
}
61+
return ret
62+
}
63+
64+
ret := fmt.Sprintf("%d sketches synced:\n", totalSketches)
65+
66+
ret += iterate(ssr.PushedSketches, "Pushed Sketches:")
67+
ret += iterate(ssr.PulledSketches, "Pulled Sketches:")
68+
ret += iterate(ssr.SkippedSketches, "Skipped Sketches:")
69+
70+
if len(ssr.Errors) > 0 {
71+
ret += fmt.Sprintln("Errors:")
72+
for _, item := range ssr.Errors {
73+
ret += fmt.Sprintf(" - Sketch %s : %s\n", item.Sketch, item.Error)
74+
}
75+
}
76+
77+
return ret
78+
}

0 commit comments

Comments
 (0)