@@ -3,12 +3,18 @@ package choice
33import (
44 "bufio"
55 "bytes"
6+ "errors"
67 "fmt"
78 "os"
89 "strconv"
910 "strings"
1011)
1112
13+ // Description interface defines a method for obtaining the description of a choice.
14+ type Description interface {
15+ Description () string
16+ }
17+
1218func choiceStr (choice any ) string {
1319 if v , ok := choice .(Description ); ok {
1420 return v .Description ()
@@ -18,7 +24,11 @@ func choiceStr(choice any) string {
1824 return fmt .Sprint (choice )
1925}
2026
21- func Menu [E Choice ](choices []E ) string {
27+ // Menu function is used to generate a string representation of the menu, associating choices with numbers.
28+ func Menu [E any ](choices []E ) string {
29+ if len (choices ) == 0 {
30+ return ""
31+ }
2232 var digit int
2333 for n := len (choices ); n != 0 ; digit ++ {
2434 n /= 10
@@ -28,32 +38,78 @@ func Menu[E Choice](choices []E) string {
2838 for i , choice := range choices {
2939 fmt .Fprintf (& b , "%s. %s\n " , fmt .Sprintf (option , i + 1 ), choiceStr (choice ))
3040 }
41+ fmt .Fprintf (& b , "%s. Quit\n " , fmt .Sprintf (fmt .Sprintf ("%%%ds" , digit ), "q" ))
3142 return b .String ()
3243}
3344
34- func choose [E Choice ](choice string , choices []E ) (res any , err error ) {
45+ // ErrBadChoice defines an error that represents an invalid choice made by the user.
46+ var ErrBadChoice = errors .New ("bad choice" )
47+
48+ var _ error = choiceError ("" )
49+
50+ type choiceError string
51+
52+ func (err choiceError ) Error () string {
53+ return "bad choice: " + string (err )
54+ }
55+
56+ func (choiceError ) Unwrap () error {
57+ return ErrBadChoice
58+ }
59+
60+ func choose [E any ](choice string , choices []E ) (res E , err error ) {
3561 n , err := strconv .Atoi (choice )
3662 if err != nil {
37- return nil , choiceError (choice )
63+ err = choiceError (choice )
64+ return
3865 }
3966 if length := len (choices ); n < 1 || n > length {
40- return nil , choiceError (fmt .Sprintf ("out of range(1-%d): %d" , length , n ))
67+ err = choiceError (fmt .Sprintf ("out of range(1-%d): %d" , length , n ))
68+ return
4169 }
42- return choices [n - 1 ].Run ()
70+ return choices [n - 1 ], nil
71+ }
72+
73+ // Choose function allows the user to make a choice from the given options with no default value.
74+ func Choose [E any ](choices []E ) (choice bool , res E , err error ) {
75+ return ChooseWithDefault (choices , 0 )
4376}
4477
45- func Choose [E Choice ](choices []E ) (choice string , res any , err error ) {
46- if length := len (choices ); length == 0 {
78+ // ChooseWithDefault function allows the user to make a choice from the given options with an optional default value.
79+ func ChooseWithDefault [E any ](choices []E , def int ) (choice bool , res E , err error ) {
80+ if n := len (choices ); n == 0 {
81+ err = errors .New ("no choices" )
82+ return
83+ } else if def > n {
84+ err = errors .New ("invalid default choice" )
4785 return
4886 }
49- fmt .Print (Menu (choices ))
50- fmt .Print ("\n Please choose: " )
87+ var prompt string
88+ if def > 0 {
89+ prompt = fmt .Sprintf ("Please choose (default: %d): " , def )
90+ } else {
91+ prompt = "Please choose: "
92+ }
5193 scanner := bufio .NewScanner (os .Stdin )
52- scanner .Scan ()
53- b := bytes .TrimSpace (scanner .Bytes ())
54- if bytes .EqualFold (b , []byte ("q" )) || bytes .Contains (b , []byte {27 }) {
94+ var b []byte
95+ if def <= 0 {
96+ for len (b ) == 0 {
97+ fmt .Print (prompt )
98+ scanner .Scan ()
99+ b = bytes .TrimSpace (scanner .Bytes ())
100+ }
101+ } else {
102+ fmt .Print (prompt )
103+ scanner .Scan ()
104+ b = bytes .TrimSpace (scanner .Bytes ())
105+ if len (b ) == 0 {
106+ return true , choices [def - 1 ], nil
107+ }
108+ }
109+ if bytes .EqualFold (b , []byte ("q" )) {
55110 return
56111 }
112+ choice = true
57113 res , err = choose (string (b ), choices )
58114 return
59115}
0 commit comments