1package survey 2 3import ( 4 "errors" 5 6 "gopkg.in/AlecAivazis/survey.v1/core" 7) 8 9// PageSize is the default maximum number of items to show in select/multiselect prompts 10var PageSize = 7 11 12// Validator is a function passed to a Question after a user has provided a response. 13// If the function returns an error, then the user will be prompted again for another 14// response. 15type Validator func(ans interface{}) error 16 17// Transformer is a function passed to a Question after a user has provided a response. 18// The function can be used to implement a custom logic that will result to return 19// a different representation of the given answer. 20// 21// Look `TransformString`, `ToLower` `Title` and `ComposeTransformers` for more. 22type Transformer func(ans interface{}) (newAns interface{}) 23 24// Question is the core data structure for a survey questionnaire. 25type Question struct { 26 Name string 27 Prompt Prompt 28 Validate Validator 29 Transform Transformer 30} 31 32// Prompt is the primary interface for the objects that can take user input 33// and return a response. 34type Prompt interface { 35 Prompt() (interface{}, error) 36 Cleanup(interface{}) error 37 Error(error) error 38} 39 40/* 41AskOne performs the prompt for a single prompt and asks for validation if required. 42Response types should be something that can be casted from the response type designated 43in the documentation. For example: 44 45 name := "" 46 prompt := &survey.Input{ 47 Message: "name", 48 } 49 50 survey.AskOne(prompt, &name, nil) 51 52*/ 53func AskOne(p Prompt, response interface{}, v Validator) error { 54 err := Ask([]*Question{{Prompt: p, Validate: v}}, response) 55 if err != nil { 56 return err 57 } 58 59 return nil 60} 61 62/* 63Ask performs the prompt loop, asking for validation when appropriate. The response 64type can be one of two options. If a struct is passed, the answer will be written to 65the field whose name matches the Name field on the corresponding question. Field types 66should be something that can be casted from the response type designated in the 67documentation. Note, a survey tag can also be used to identify a Otherwise, a 68map[string]interface{} can be passed, responses will be written to the key with the 69matching name. For example: 70 71 qs := []*survey.Question{ 72 { 73 Name: "name", 74 Prompt: &survey.Input{Message: "What is your name?"}, 75 Validate: survey.Required, 76 Transform: survey.Title, 77 }, 78 } 79 80 answers := struct{ Name string }{} 81 82 83 err := survey.Ask(qs, &answers) 84*/ 85func Ask(qs []*Question, response interface{}) error { 86 87 // if we weren't passed a place to record the answers 88 if response == nil { 89 // we can't go any further 90 return errors.New("cannot call Ask() with a nil reference to record the answers") 91 } 92 93 // go over every question 94 for _, q := range qs { 95 // grab the user input and save it 96 ans, err := q.Prompt.Prompt() 97 // if there was a problem 98 if err != nil { 99 return err 100 } 101 102 // if there is a validate handler for this question 103 if q.Validate != nil { 104 // wait for a valid response 105 for invalid := q.Validate(ans); invalid != nil; invalid = q.Validate(ans) { 106 err := q.Prompt.Error(invalid) 107 // if there was a problem 108 if err != nil { 109 return err 110 } 111 112 // ask for more input 113 ans, err = q.Prompt.Prompt() 114 // if there was a problem 115 if err != nil { 116 return err 117 } 118 } 119 } 120 121 if q.Transform != nil { 122 // check if we have a transformer available, if so 123 // then try to acquire the new representation of the 124 // answer, if the resulting answer is not nil. 125 if newAns := q.Transform(ans); newAns != nil { 126 ans = newAns 127 } 128 } 129 130 // tell the prompt to cleanup with the validated value 131 q.Prompt.Cleanup(ans) 132 133 // if something went wrong 134 if err != nil { 135 // stop listening 136 return err 137 } 138 139 // add it to the map 140 err = core.WriteAnswer(response, q.Name, ans) 141 // if something went wrong 142 if err != nil { 143 return err 144 } 145 146 } 147 // return the response 148 return nil 149} 150 151// paginate returns a single page of choices given the page size, the total list of 152// possible choices, and the current selected index in the total list. 153func paginate(page int, choices []string, sel int) ([]string, int) { 154 // the number of elements to show in a single page 155 var pageSize int 156 // if the select has a specific page size 157 if page != 0 { 158 // use the specified one 159 pageSize = page 160 // otherwise the select does not have a page size 161 } else { 162 // use the package default 163 pageSize = PageSize 164 } 165 166 var start, end, cursor int 167 168 if len(choices) < pageSize { 169 // if we dont have enough options to fill a page 170 start = 0 171 end = len(choices) 172 cursor = sel 173 174 } else if sel < pageSize/2 { 175 // if we are in the first half page 176 start = 0 177 end = pageSize 178 cursor = sel 179 180 } else if len(choices)-sel-1 < pageSize/2 { 181 // if we are in the last half page 182 start = len(choices) - pageSize 183 end = len(choices) 184 cursor = sel - start 185 186 } else { 187 // somewhere in the middle 188 above := pageSize / 2 189 below := pageSize - above 190 191 cursor = pageSize / 2 192 start = sel - above 193 end = sel + below 194 } 195 196 // return the subset we care about and the index 197 return choices[start:end], cursor 198} 199