Skip to content

Commit b435489

Browse files
authored
feat: Add optional metrics; Add load spinner while solving puzzle (#45)
* feat: Add optional metrics; Add load spinner while solving puzzle * fix: Linter warnings * fix: Linter warnings * chore: Remove deadcode
1 parent 5c73886 commit b435489

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+4153
-125
lines changed

cmd/aoc-cli/main.go

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import (
88
"fmt"
99
"os"
1010
"strings"
11+
"time"
1112

13+
"github.com/briandowns/spinner"
1214
"github.com/manifoldco/promptui"
1315
log "github.com/obalunenko/logger"
1416
"github.com/obalunenko/version"
@@ -42,6 +44,8 @@ func main() {
4244
app.Version = version.GetVersion()
4345
app.Email = "[email protected]"
4446

47+
app.Flags = flags()
48+
4549
app.Action = menu(ctx)
4650
app.Before = printVersion
4751
app.After = onExit
@@ -55,6 +59,41 @@ func main() {
5559
}
5660
}
5761

62+
const (
63+
flagElapsed = "elapsed"
64+
flagShortElapsed = "e"
65+
flagBenchmark = "bench"
66+
flagShortBenchmark = "b"
67+
)
68+
69+
func flags() []cli.Flag {
70+
var res []cli.Flag
71+
72+
elapsed := cli.BoolFlag{
73+
Name: fmt.Sprintf("%s, %s", flagElapsed, flagShortElapsed),
74+
Usage: "Enables elapsed time metric",
75+
EnvVar: "",
76+
FilePath: "",
77+
Required: false,
78+
Hidden: false,
79+
Destination: nil,
80+
}
81+
82+
benchmark := cli.BoolFlag{
83+
Name: fmt.Sprintf("%s, %s", flagBenchmark, flagShortBenchmark),
84+
Usage: "Enables benchmark metric",
85+
EnvVar: "",
86+
FilePath: "",
87+
Required: false,
88+
Hidden: false,
89+
Destination: nil,
90+
}
91+
92+
res = append(res, elapsed, benchmark)
93+
94+
return res
95+
}
96+
5897
func onExit(_ *cli.Context) error {
5998
fmt.Println("Exit...")
6099

@@ -63,6 +102,8 @@ func onExit(_ *cli.Context) error {
63102

64103
func menu(ctx context.Context) cli.ActionFunc {
65104
return func(c *cli.Context) error {
105+
ctx = contextWithOptions(ctx, optionsFromCli(c))
106+
66107
years := puzzles.GetYears()
67108

68109
prompt := promptui.Select{
@@ -156,13 +197,19 @@ func handlePuzzleChoices(ctx context.Context, year string, opt promptui.Select)
156197
return nil
157198
}
158199

159-
res, err := run(year, choice)
200+
stopSpinner := setSpinner()
201+
202+
res, err := run(ctx, year, choice)
160203
if err != nil {
161204
log.WithError(ctx, err).Error("Puzzle run failed")
162205

206+
stopSpinner()
207+
163208
continue
164209
}
165210

211+
stopSpinner()
212+
166213
fmt.Println(res.String())
167214
}
168215
}
@@ -179,7 +226,44 @@ func isBack(in string) bool {
179226
return strings.EqualFold(back, in)
180227
}
181228

182-
func run(year, day string) (puzzles.Result, error) {
229+
func optionsFromCli(c *cli.Context) []puzzles.RunOption {
230+
const optsnum = 2
231+
232+
options := make([]puzzles.RunOption, 0, optsnum)
233+
234+
if c.GlobalBool(flagElapsed) || c.GlobalBool(flagShortElapsed) {
235+
options = append(options, puzzles.WithElapsed())
236+
}
237+
238+
if c.GlobalBool(flagBenchmark) || c.GlobalBool(flagShortBenchmark) {
239+
options = append(options, puzzles.WithBenchmark())
240+
}
241+
242+
return options
243+
}
244+
245+
type optsCtxKey struct{}
246+
247+
func contextWithOptions(ctx context.Context, opts []puzzles.RunOption) context.Context {
248+
if len(opts) == 0 {
249+
return ctx
250+
}
251+
252+
return context.WithValue(ctx, optsCtxKey{}, opts)
253+
}
254+
255+
func optionsFromContext(ctx context.Context) []puzzles.RunOption {
256+
v := ctx.Value(optsCtxKey{})
257+
258+
opts, ok := v.([]puzzles.RunOption)
259+
if !ok {
260+
return []puzzles.RunOption{}
261+
}
262+
263+
return opts
264+
}
265+
266+
func run(ctx context.Context, year, day string) (puzzles.Result, error) {
183267
s, err := puzzles.GetSolver(year, day)
184268
if err != nil {
185269
return puzzles.Result{}, fmt.Errorf("failed to get solver: %w", err)
@@ -195,10 +279,34 @@ func run(year, day string) (puzzles.Result, error) {
195279
return puzzles.Result{}, fmt.Errorf("failed to open input data: %w", err)
196280
}
197281

198-
res, err := puzzles.Run(s, bytes.NewReader(asset))
282+
opts := optionsFromContext(ctx)
283+
284+
res, err := puzzles.Run(s, bytes.NewReader(asset), opts...)
199285
if err != nil {
200286
return puzzles.Result{}, fmt.Errorf("failed to run [%s]: %w", fullName, err)
201287
}
202288

203289
return res, nil
204290
}
291+
292+
// setSpinner runs the displaying of spinner to handle long time operations. Returns stop func.
293+
func setSpinner() func() {
294+
const delayms = 100
295+
296+
s := spinner.New(
297+
spinner.CharSets[62],
298+
delayms*time.Millisecond,
299+
spinner.WithFinalMSG("Solved!"),
300+
spinner.WithHiddenCursor(true),
301+
spinner.WithColor("yellow"),
302+
spinner.WithWriter(os.Stderr),
303+
)
304+
305+
s.Prefix = "Solving in progress..."
306+
307+
s.Start()
308+
309+
return func() {
310+
s.Stop()
311+
}
312+
}

cmd/aoc-cli/main_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"context"
45
"testing"
56

67
"github.com/stretchr/testify/assert"
@@ -23,6 +24,8 @@ type testcase struct {
2324

2425
// Regression tests for all puzzles. Check that answers still correct.
2526
func Test_run(t *testing.T) {
27+
ctx := context.Background()
28+
2629
var tests []testcase
2730

2831
tests = append(tests, invalid()...)
@@ -35,7 +38,7 @@ func Test_run(t *testing.T) {
3538

3639
for _, tt := range tests {
3740
t.Run(tt.name, func(t *testing.T) {
38-
got, err := run(tt.args.year, tt.args.name)
41+
got, err := run(ctx, tt.args.year, tt.args.name)
3942
if tt.wantErr {
4043
require.Error(t, err)
4144

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/obalunenko/advent-of-code
33
go 1.17
44

55
require (
6+
github.com/briandowns/spinner v1.13.0
67
github.com/manifoldco/promptui v0.9.0
78
github.com/obalunenko/logger v0.1.0
89
github.com/obalunenko/version v1.1.0
@@ -16,7 +17,10 @@ require (
1617
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
1718
github.com/davecgh/go-spew v1.1.1 // indirect
1819
github.com/evalphobia/logrus_sentry v0.8.2 // indirect
20+
github.com/fatih/color v1.7.0 // indirect
1921
github.com/getsentry/raven-go v0.2.0 // indirect
22+
github.com/mattn/go-colorable v0.1.2 // indirect
23+
github.com/mattn/go-isatty v0.0.8 // indirect
2024
github.com/pkg/errors v0.9.1 // indirect
2125
github.com/pmezard/go-difflib v1.0.0 // indirect
2226
github.com/russross/blackfriday/v2 v2.1.0 // indirect

go.sum

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2+
github.com/briandowns/spinner v1.13.0 h1:q/Y9LtpwtvL0CRzXrAMj0keVXqNhBYUFg6tBOUiY8ek=
3+
github.com/briandowns/spinner v1.13.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
24
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s=
35
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
46
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
@@ -15,10 +17,16 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
1517
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1618
github.com/evalphobia/logrus_sentry v0.8.2 h1:dotxHq+YLZsT1Bb45bB5UQbfCh3gM/nFFetyN46VoDQ=
1719
github.com/evalphobia/logrus_sentry v0.8.2/go.mod h1:pKcp+vriitUqu9KiWj/VRFbRfFNUwz95/UkgG8a6MNc=
20+
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
21+
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
1822
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
1923
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
2024
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
2125
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
26+
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
27+
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
28+
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
29+
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
2230
github.com/obalunenko/logger v0.1.0 h1:CSdZZPXznz8DJQ1zFoy/uwCyynAVsZHuYdNLmtC/gSg=
2331
github.com/obalunenko/logger v0.1.0/go.mod h1:09zbNCrGvrIzuiKLQFy2yWWDh3R/lXkz3TZUtUa6FRg=
2432
github.com/obalunenko/version v1.1.0 h1:yVua7OHnK3+MJpendeMmAlfzVmq7R1h8MO3Ufz7HEec=
@@ -40,6 +48,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
4048
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
4149
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
4250
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
51+
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
4352
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
4453
golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3 h1:T6tyxxvHMj2L1R2kZg0uNMpS8ZhB9lRa9XRGTCSA65w=
4554
golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

internal/puzzles/constants.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,10 @@ const (
5454

5555
yearSentinel
5656
)
57+
58+
const (
59+
unknown = "unknown"
60+
unsolved = "not solved"
61+
undefined = "undefined"
62+
inProgress = "in progress"
63+
)

internal/puzzles/errors.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package puzzles
2+
3+
import "errors"
4+
5+
var (
6+
// ErrInvalidPuzzleName means that such puzzle not exist.
7+
ErrInvalidPuzzleName = errors.New("invalid puzzle name")
8+
// ErrInvalidYear means that such year not exist.
9+
ErrInvalidYear = errors.New("invalid year")
10+
// ErrNotImplemented signal that puzzle in not implemented yet.
11+
ErrNotImplemented = errors.New("not implemented")
12+
)

0 commit comments

Comments
 (0)