mirror of
https://github.com/onyx-and-iris/lottery-cli.git
synced 2026-08-17 08:44:25 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 12ca7d8fc2 | |||
| 1310447664 | |||
| 3f0af6e5ec | |||
| 5a20a3cf2b | |||
| b364276d5a | |||
| 2d1a024d65 | |||
| ad882d0fc0 | |||
| fb13566543 | |||
| cdee9113de | |||
| 06f47b1f70 |
73
README.md
Normal file
73
README.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
# lottery
|
||||||
|
|
||||||
|
Play National Lottery games from your terminal.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```console
|
||||||
|
go install github.com/onyx-and-iris/lottery-cli/cmd/lottery@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
*flags*
|
||||||
|
|
||||||
|
- --kind/-k: The kind of lottery.
|
||||||
|
- --count/-c: Number of draws to generate.
|
||||||
|
- --count-prompt/-C: Prompt for the number of draws to generate.
|
||||||
|
> Note. If both --count and --count-prompt are passed the count prompt will win.
|
||||||
|
|
||||||
|
*environment variables*
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
export LOTTERY_KIND=lotto
|
||||||
|
export LOTTERY_COUNT=3
|
||||||
|
export LOTTERY_COUNT_PROMPT=false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use
|
||||||
|
|
||||||
|
Run with the selection prompt without prompting for a count:
|
||||||
|
|
||||||
|
```console
|
||||||
|
lottery
|
||||||
|
```
|
||||||
|
|
||||||
|
Run with the selection prompt but also prompt for a count:
|
||||||
|
|
||||||
|
```console
|
||||||
|
lottery --count-prompt
|
||||||
|
```
|
||||||
|
|
||||||
|
Run with the selection prompt but pass in the count directly:
|
||||||
|
|
||||||
|
```console
|
||||||
|
lottery --count=4
|
||||||
|
```
|
||||||
|
|
||||||
|
Run a single draw directly:
|
||||||
|
|
||||||
|
```console
|
||||||
|
lottery -k=euromillions
|
||||||
|
```
|
||||||
|
|
||||||
|
Run multiple draws directly:
|
||||||
|
|
||||||
|
```console
|
||||||
|
lottery -k=euromillions -c=3
|
||||||
|
```
|
||||||
|
|
||||||
|
## Special Thanks
|
||||||
|
|
||||||
|
- [spf13](https://github.com/spf13) for the [cobra](https://github.com/spf13/cobra) and [viper](https://github.com/spf13/viper) packages.
|
||||||
|
- [Charm](https://github.com/charmbracelet) developers for the [fang](https://github.com/charmbracelet/fang), [lipgloss](https://github.com/charmbracelet/lipgloss) and [huh](https://github.com/charmbracelet/huh) packages.
|
||||||
65
Taskfile.yaml
Normal file
65
Taskfile.yaml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
vars:
|
||||||
|
PROGRAM: lottery
|
||||||
|
SHELL: '{{if eq .OS "Windows_NT"}}powershell{{end}}'
|
||||||
|
BIN_DIR: bin
|
||||||
|
VERSION:
|
||||||
|
sh: 'git describe --tags $(git rev-list --tags --max-count=1)'
|
||||||
|
|
||||||
|
WINDOWS: '{{.BIN_DIR}}/{{.PROGRAM}}_windows_amd64.exe'
|
||||||
|
LINUX: '{{.BIN_DIR}}/{{.PROGRAM}}_linux_amd64'
|
||||||
|
MACOS: '{{.BIN_DIR}}/{{.PROGRAM}}_darwin_amd64'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
desc: Build the lottery project
|
||||||
|
cmds:
|
||||||
|
- task: build
|
||||||
|
|
||||||
|
build:
|
||||||
|
desc: Build the lottery project
|
||||||
|
deps: [vet]
|
||||||
|
cmds:
|
||||||
|
- task: build-windows
|
||||||
|
- task: build-linux
|
||||||
|
- task: build-macos
|
||||||
|
|
||||||
|
vet:
|
||||||
|
desc: Vet the code
|
||||||
|
deps: [fmt]
|
||||||
|
cmds:
|
||||||
|
- go vet ./...
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
desc: Fmt the code
|
||||||
|
cmds:
|
||||||
|
- go fmt ./...
|
||||||
|
|
||||||
|
build-windows:
|
||||||
|
desc: Build the lottery project for Windows
|
||||||
|
cmds:
|
||||||
|
- GOOS=windows GOARCH=amd64 go build -o {{.WINDOWS}} -ldflags="-X main.version={{.VERSION}}" ./cmd/{{.PROGRAM}}/
|
||||||
|
internal: true
|
||||||
|
|
||||||
|
build-linux:
|
||||||
|
desc: Build the lottery project for Linux
|
||||||
|
cmds:
|
||||||
|
- GOOS=linux GOARCH=amd64 go build -o {{.LINUX}} -ldflags="-X main.version={{.VERSION}}" ./cmd/{{.PROGRAM}}/
|
||||||
|
internal: true
|
||||||
|
|
||||||
|
build-macos:
|
||||||
|
desc: Build the lottery project for macOS
|
||||||
|
cmds:
|
||||||
|
- GOOS=darwin GOARCH=amd64 go build -o {{.MACOS}} -ldflags="-X main.version={{.VERSION}}" ./cmd/{{.PROGRAM}}/
|
||||||
|
internal: true
|
||||||
|
|
||||||
|
test:
|
||||||
|
desc: Run tests
|
||||||
|
cmds:
|
||||||
|
- go test ./...
|
||||||
|
|
||||||
|
clean:
|
||||||
|
desc: Clean the build artifacts
|
||||||
|
cmds:
|
||||||
|
- '{{.SHELL}} rm -r {{.BIN_DIR}}'
|
||||||
@@ -9,8 +9,10 @@ import (
|
|||||||
|
|
||||||
"charm.land/huh/v2"
|
"charm.land/huh/v2"
|
||||||
"github.com/charmbracelet/fang"
|
"github.com/charmbracelet/fang"
|
||||||
"github.com/onyx-and-iris/lottery-cli"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/onyx-and-iris/lottery-cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string
|
var version string
|
||||||
@@ -30,29 +32,40 @@ func versionFromBuild() string {
|
|||||||
var cmd = &cobra.Command{
|
var cmd = &cobra.Command{
|
||||||
Use: "lottery",
|
Use: "lottery",
|
||||||
Short: "A CLI for National Lottery games.",
|
Short: "A CLI for National Lottery games.",
|
||||||
|
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
kindStr := viper.GetString("kind")
|
||||||
|
if kindStr != "" {
|
||||||
|
_, err := lottery.ParseKind(kindStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
var selected string
|
kindStr := viper.GetString("kind")
|
||||||
|
if kindStr == "" {
|
||||||
form := huh.NewForm(
|
form := huh.NewForm(
|
||||||
huh.NewGroup(
|
huh.NewGroup(
|
||||||
huh.NewSelect[string]().
|
huh.NewSelect[string]().
|
||||||
Title("Pick a lottery.").
|
Title("Pick a lottery.").
|
||||||
Options(
|
Options(
|
||||||
huh.NewOption("Lotto", "lotto"),
|
huh.NewOption("Lotto", "lotto"),
|
||||||
huh.NewOption("EuroMillions", "euromillions"),
|
huh.NewOption("EuroMillions", "euromillions"),
|
||||||
huh.NewOption("SetForLife", "setforlife"),
|
huh.NewOption("Set For Life", "setforlife"),
|
||||||
huh.NewOption("Thunderball", "thunderball"),
|
huh.NewOption("Thunderball", "thunderball"),
|
||||||
huh.NewOption("Powerball", "powerball"),
|
huh.NewOption("Powerball", "powerball"),
|
||||||
).
|
).
|
||||||
Value(&selected),
|
Value(&kindStr),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
err := form.Run()
|
err := form.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kind, err := lottery.ParseKind(selected)
|
kind, err := lottery.ParseKind(kindStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -61,15 +74,62 @@ var cmd = &cobra.Command{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
l.Draw()
|
|
||||||
fmt.Println(renderDraw(l))
|
if countPrompt := viper.GetBool("count-prompt"); countPrompt {
|
||||||
|
var count string
|
||||||
|
countPrompt := huh.NewForm(
|
||||||
|
huh.NewGroup(
|
||||||
|
huh.NewInput().
|
||||||
|
Title("How many draws would you like to generate?").
|
||||||
|
Value(&count),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
err := countPrompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
viper.Set("count", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
count := viper.GetInt("count")
|
||||||
|
includeDrawHeading := count > 1
|
||||||
|
renders := make([]string, 0, count)
|
||||||
|
drawTitle := "Lottery"
|
||||||
|
|
||||||
|
for i := range count {
|
||||||
|
l.Draw()
|
||||||
|
title, entry := renderDrawEntry(l, i+1, includeDrawHeading)
|
||||||
|
drawTitle = title
|
||||||
|
renders = append(renders, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(renders) > 0 {
|
||||||
|
fmt.Println(renderDrawCollection(drawTitle, renders))
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cmd.Flags().StringP("kind", "k", "", "Lottery kind to generate draws for.")
|
||||||
|
cmd.Flags().IntP("count", "c", 1, "Number of draws to generate.")
|
||||||
|
cmd.Flags().BoolP("count-prompt", "C", false, "Prompt for the number of draws to generate.")
|
||||||
|
|
||||||
|
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||||
|
viper.SetEnvPrefix("LOTTERY")
|
||||||
|
viper.AutomaticEnv()
|
||||||
|
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := fang.Execute(context.Background(), cmd, fang.WithVersion(versionFromBuild())); err != nil {
|
if err := fang.Execute(
|
||||||
|
context.Background(),
|
||||||
|
cmd,
|
||||||
|
fang.WithVersion(versionFromBuild()),
|
||||||
|
); err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,28 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"charm.land/lipgloss/v2"
|
"charm.land/lipgloss/v2"
|
||||||
|
|
||||||
"github.com/onyx-and-iris/lottery-cli"
|
"github.com/onyx-and-iris/lottery-cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// nolint:misspell
|
||||||
|
var (
|
||||||
|
titleStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("12"))
|
||||||
|
drawHeadingStyle = lipgloss.NewStyle().
|
||||||
|
Bold(true).
|
||||||
|
Underline(true).
|
||||||
|
Foreground(lipgloss.Color("14"))
|
||||||
|
labelStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("8"))
|
||||||
|
numbersStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("10"))
|
||||||
|
specialStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("11"))
|
||||||
|
separatorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("8"))
|
||||||
|
cardStyle = lipgloss.NewStyle().
|
||||||
|
BorderStyle(lipgloss.RoundedBorder()).
|
||||||
|
BorderForeground(lipgloss.Color("8")).
|
||||||
|
Padding(0, 1).
|
||||||
|
MarginTop(1)
|
||||||
|
)
|
||||||
|
|
||||||
func formatNumberList(numbers []int) string {
|
func formatNumberList(numbers []int) string {
|
||||||
parts := make([]string, len(numbers))
|
parts := make([]string, len(numbers))
|
||||||
for i, n := range numbers {
|
for i, n := range numbers {
|
||||||
@@ -16,46 +35,117 @@ func formatNumberList(numbers []int) string {
|
|||||||
return strings.Join(parts, " ")
|
return strings.Join(parts, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderDraw(l lottery.Lottery) string {
|
func drawTitleAndLines(l lottery.Lottery) (string, []string) {
|
||||||
titleStyle := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("12"))
|
|
||||||
labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("8"))
|
|
||||||
numbersStyle := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("10"))
|
|
||||||
specialStyle := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("11"))
|
|
||||||
|
|
||||||
var title string
|
var title string
|
||||||
var lines []string
|
var lines []string
|
||||||
|
|
||||||
switch game := l.(type) {
|
switch game := l.(type) {
|
||||||
case *lottery.Lotto:
|
case *lottery.Lotto:
|
||||||
title = "Lotto"
|
title = "Lotto"
|
||||||
lines = append(lines, labelStyle.Render("Numbers:")+" "+numbersStyle.Render(formatNumberList(game.Numbers[:])))
|
lines = append(
|
||||||
|
lines,
|
||||||
|
labelStyle.Render(
|
||||||
|
"Numbers:",
|
||||||
|
)+" "+numbersStyle.Render(
|
||||||
|
formatNumberList(game.Numbers[:]),
|
||||||
|
),
|
||||||
|
)
|
||||||
case *lottery.EuroMillions:
|
case *lottery.EuroMillions:
|
||||||
title = "EuroMillions"
|
title = "EuroMillions"
|
||||||
lines = append(lines, labelStyle.Render("Main:")+" "+numbersStyle.Render(formatNumberList(game.Numbers[:])))
|
lines = append(
|
||||||
lines = append(lines, labelStyle.Render("Lucky Stars:")+" "+specialStyle.Render(formatNumberList(game.LuckyStars[:])))
|
lines,
|
||||||
|
labelStyle.Render("Main:")+" "+numbersStyle.Render(formatNumberList(game.Numbers[:])),
|
||||||
|
)
|
||||||
|
lines = append(
|
||||||
|
lines,
|
||||||
|
labelStyle.Render(
|
||||||
|
"Lucky Stars:",
|
||||||
|
)+" "+specialStyle.Render(
|
||||||
|
formatNumberList(game.LuckyStars[:]),
|
||||||
|
),
|
||||||
|
)
|
||||||
case *lottery.SetForLife:
|
case *lottery.SetForLife:
|
||||||
title = "Set For Life"
|
title = "Set For Life"
|
||||||
lines = append(lines, labelStyle.Render("Main:")+" "+numbersStyle.Render(formatNumberList(game.Numbers[:])))
|
lines = append(
|
||||||
lines = append(lines, labelStyle.Render("Life Ball:")+" "+specialStyle.Render(strconv.Itoa(game.LifeBall)))
|
lines,
|
||||||
|
labelStyle.Render("Main:")+" "+numbersStyle.Render(formatNumberList(game.Numbers[:])),
|
||||||
|
)
|
||||||
|
lines = append(
|
||||||
|
lines,
|
||||||
|
labelStyle.Render("Life Ball:")+" "+specialStyle.Render(strconv.Itoa(game.LifeBall)),
|
||||||
|
)
|
||||||
case *lottery.Thunderball:
|
case *lottery.Thunderball:
|
||||||
title = "Thunderball"
|
title = "Thunderball"
|
||||||
lines = append(lines, labelStyle.Render("Main:")+" "+numbersStyle.Render(formatNumberList(game.Numbers[:])))
|
lines = append(
|
||||||
lines = append(lines, labelStyle.Render("Thunderball:")+" "+specialStyle.Render(strconv.Itoa(game.Thunderball)))
|
lines,
|
||||||
|
labelStyle.Render("Main:")+" "+numbersStyle.Render(formatNumberList(game.Numbers[:])),
|
||||||
|
)
|
||||||
|
lines = append(
|
||||||
|
lines,
|
||||||
|
labelStyle.Render(
|
||||||
|
"Thunderball:",
|
||||||
|
)+" "+specialStyle.Render(
|
||||||
|
strconv.Itoa(game.Thunderball),
|
||||||
|
),
|
||||||
|
)
|
||||||
case *lottery.Powerball:
|
case *lottery.Powerball:
|
||||||
title = "Powerball"
|
title = "Powerball"
|
||||||
lines = append(lines, labelStyle.Render("Main:")+" "+numbersStyle.Render(formatNumberList(game.Numbers[:])))
|
lines = append(
|
||||||
lines = append(lines, labelStyle.Render("Powerball:")+" "+specialStyle.Render(strconv.Itoa(game.Powerball)))
|
lines,
|
||||||
|
labelStyle.Render("Main:")+" "+numbersStyle.Render(formatNumberList(game.Numbers[:])),
|
||||||
|
)
|
||||||
|
lines = append(
|
||||||
|
lines,
|
||||||
|
labelStyle.Render("Powerball:")+" "+specialStyle.Render(strconv.Itoa(game.Powerball)),
|
||||||
|
)
|
||||||
default:
|
default:
|
||||||
title = "Lottery"
|
title = "Lottery"
|
||||||
lines = append(lines, "Unknown lottery type")
|
lines = append(lines, "Unknown lottery type")
|
||||||
}
|
}
|
||||||
|
|
||||||
body := strings.Join(lines, "\n")
|
return title, lines
|
||||||
card := lipgloss.NewStyle().
|
}
|
||||||
BorderStyle(lipgloss.RoundedBorder()).
|
|
||||||
BorderForeground(lipgloss.Color("8")).
|
func renderDrawEntry(l lottery.Lottery, drawNumber int, includeHeading bool) (string, string) {
|
||||||
Padding(0, 1).
|
title, lines := drawTitleAndLines(l)
|
||||||
MarginTop(1)
|
|
||||||
|
entry := make([]string, 0, len(lines)+1)
|
||||||
return card.Render(titleStyle.Render(title+" draw") + "\n" + body)
|
if includeHeading {
|
||||||
|
entry = append(entry, drawHeadingStyle.Render("Draw "+strconv.Itoa(drawNumber)))
|
||||||
|
}
|
||||||
|
entry = append(entry, lines...)
|
||||||
|
|
||||||
|
return title, strings.Join(entry, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxLineWidth(blocks []string) int {
|
||||||
|
maxWidth := 0
|
||||||
|
for _, block := range blocks {
|
||||||
|
for line := range strings.SplitSeq(block, "\n") {
|
||||||
|
width := lipgloss.Width(line)
|
||||||
|
if width > maxWidth {
|
||||||
|
maxWidth = width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if maxWidth < 12 {
|
||||||
|
return 12
|
||||||
|
}
|
||||||
|
return maxWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderDrawCollection(title string, entries []string) string {
|
||||||
|
if len(entries) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
separator := separatorStyle.Render(strings.Repeat("─", maxLineWidth(entries)))
|
||||||
|
body := strings.Join(entries, "\n"+separator+"\n")
|
||||||
|
if len(entries) == 1 {
|
||||||
|
title = titleStyle.Render(title + " draw")
|
||||||
|
} else {
|
||||||
|
title = titleStyle.Render(title + " draws")
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardStyle.Render(title + "\n" + body)
|
||||||
}
|
}
|
||||||
|
|||||||
14
go.mod
14
go.mod
@@ -7,6 +7,7 @@ require (
|
|||||||
charm.land/lipgloss/v2 v2.0.5
|
charm.land/lipgloss/v2 v2.0.5
|
||||||
github.com/charmbracelet/fang v1.0.0
|
github.com/charmbracelet/fang v1.0.0
|
||||||
github.com/spf13/cobra v1.10.2
|
github.com/spf13/cobra v1.10.2
|
||||||
|
github.com/spf13/viper v1.21.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -26,6 +27,8 @@ require (
|
|||||||
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.23 // indirect
|
github.com/mattn/go-runewidth v0.0.23 // indirect
|
||||||
@@ -35,10 +38,17 @@ require (
|
|||||||
github.com/muesli/mango-cobra v1.2.0 // indirect
|
github.com/muesli/mango-cobra v1.2.0 // indirect
|
||||||
github.com/muesli/mango-pflag v0.1.0 // indirect
|
github.com/muesli/mango-pflag v0.1.0 // indirect
|
||||||
github.com/muesli/roff v0.1.0 // indirect
|
github.com/muesli/roff v0.1.0 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/spf13/pflag v1.0.9 // indirect
|
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||||
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.46.0 // indirect
|
golang.org/x/sys v0.46.0 // indirect
|
||||||
golang.org/x/text v0.24.0 // indirect
|
golang.org/x/text v0.28.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
42
go.sum
42
go.sum
@@ -53,8 +53,20 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
|
github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
|
||||||
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
|
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
|
||||||
@@ -71,19 +83,37 @@ github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe
|
|||||||
github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0=
|
github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0=
|
||||||
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
|
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
|
||||||
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
|
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||||
|
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
|
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||||
|
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||||
|
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
|
||||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||||
|
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
@@ -91,8 +121,10 @@ golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
|||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
BIN
img/draw.png
Executable file
BIN
img/draw.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
BIN
img/selectionprompt.png
Executable file
BIN
img/selectionprompt.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
5
kinds.go
5
kinds.go
@@ -25,6 +25,9 @@ func ParseKind(kind string) (Kind, error) {
|
|||||||
case "powerball":
|
case "powerball":
|
||||||
return KindPowerball, nil
|
return KindPowerball, nil
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("invalid lottery kind: %s", kind)
|
return "", fmt.Errorf(
|
||||||
|
"invalid lottery kind: %s, must be one of: lotto, euromillions, setforlife, thunderball, powerball",
|
||||||
|
kind,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
util.go
4
util.go
@@ -8,8 +8,8 @@ import (
|
|||||||
// drawUnique returns count unique numbers in the range [1, max].
|
// drawUnique returns count unique numbers in the range [1, max].
|
||||||
// It uses the rand.Perm function to generate a random permutation of numbers and selects the first count numbers from it.
|
// It uses the rand.Perm function to generate a random permutation of numbers and selects the first count numbers from it.
|
||||||
// The result is sorted before being returned.
|
// The result is sorted before being returned.
|
||||||
func drawUnique(count, max int) []int {
|
func drawUnique(count, maxNum int) []int {
|
||||||
perm := rand.Perm(max)
|
perm := rand.Perm(maxNum)
|
||||||
result := make([]int, count)
|
result := make([]int, count)
|
||||||
for i := range result {
|
for i := range result {
|
||||||
result[i] = perm[i] + 1
|
result[i] = perm[i] + 1
|
||||||
|
|||||||
Reference in New Issue
Block a user