survey.go 5.0 KB

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