code-intelligence.com/cifuzz@v0.40.0/pkg/dialog/dialog.go (about) 1 package dialog 2 3 import ( 4 "os" 5 "sort" 6 "strings" 7 8 "atomicgo.dev/keyboard/keys" 9 "github.com/pkg/errors" 10 "github.com/pterm/pterm" 11 "golang.org/x/exp/maps" 12 13 "code-intelligence.com/cifuzz/internal/config" 14 "code-intelligence.com/cifuzz/pkg/log" 15 ) 16 17 // Select offers the user a list of items (label:value) to select from and returns the value of the selected item 18 func Select(message string, items map[string]string, sorted bool) (string, error) { 19 options := maps.Keys(items) 20 if sorted { 21 sort.Strings(options) 22 } 23 prompt := pterm.DefaultInteractiveSelect.WithOptions(options) 24 prompt.DefaultText = message 25 26 result, err := prompt.Show() 27 if err != nil { 28 return "", errors.WithStack(err) 29 } 30 31 return items[result], nil 32 } 33 34 // MultiSelect offers the user a list of items (label:value) to select from and returns the values of the selected items 35 func MultiSelect(message string, items map[string]string) ([]string, error) { 36 options := maps.Keys(items) 37 sort.Strings(options) 38 39 prompt := pterm.DefaultInteractiveMultiselect.WithOptions(options) 40 prompt.DefaultText = message 41 prompt.Filter = false 42 prompt.KeyConfirm = keys.Enter 43 prompt.KeySelect = keys.Space 44 45 selectedOptions, err := prompt.Show() 46 if err != nil { 47 return nil, errors.WithStack(err) 48 } 49 sort.Strings(selectedOptions) 50 51 var result []string 52 for _, option := range selectedOptions { 53 result = append(result, items[option]) 54 } 55 56 return result, nil 57 } 58 59 func Confirm(message string, defaultValue bool) (bool, error) { 60 var confirmText, rejectText string 61 if defaultValue { 62 confirmText = "Y" 63 rejectText = "n" 64 } else { 65 confirmText = "y" 66 rejectText = "N" 67 } 68 res, err := pterm.InteractiveConfirmPrinter{ 69 DefaultValue: defaultValue, 70 DefaultText: message, 71 TextStyle: &pterm.ThemeDefault.PrimaryStyle, 72 ConfirmText: confirmText, 73 ConfirmStyle: &pterm.ThemeDefault.PrimaryStyle, 74 RejectText: rejectText, 75 RejectStyle: &pterm.ThemeDefault.PrimaryStyle, 76 SuffixStyle: &pterm.ThemeDefault.SecondaryStyle, 77 }.Show() 78 return res, errors.WithStack(err) 79 } 80 81 func Input(message string) (string, error) { 82 input := pterm.DefaultInteractiveTextInput.WithDefaultText(message) 83 result, err := input.Show() 84 if err != nil { 85 return "", errors.WithStack(err) 86 } 87 return result, nil 88 } 89 90 // ReadSecret reads a secret from the user and prints * characters instead of 91 // the actual secret. 92 func ReadSecret(message string) (string, error) { 93 secret, err := pterm.DefaultInteractiveTextInput.WithMask("*").Show(message) 94 if err != nil { 95 return "", errors.WithStack(err) 96 } 97 return secret, nil 98 } 99 100 // askToPersistProjectChoice asks the user if they want to persist their 101 // project choice. If they do, it adds the project to the cifuzz.yaml file. 102 func AskToPersistProjectChoice(projectName string) error { 103 persist, err := Confirm(`Do you want to persist your choice? 104 This will add a 'project' entry to your cifuzz.yaml. 105 You can change these values later by editing the file.`, false) 106 if err != nil { 107 return err 108 } 109 110 if persist { 111 project := strings.TrimPrefix(projectName, "projects/") 112 113 contents, err := os.ReadFile(config.ProjectConfigFile) 114 if err != nil { 115 return errors.WithStack(err) 116 } 117 updatedContents := config.EnsureProjectEntry(string(contents), project) 118 119 err = os.WriteFile(config.ProjectConfigFile, []byte(updatedContents), 0o644) 120 if err != nil { 121 return errors.WithStack(err) 122 } 123 log.Notef("Your choice has been persisted in cifuzz.yaml.") 124 } 125 return nil 126 }