github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/interact/query.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package interact
     5  
     6  import (
     7  	"bufio"
     8  	"fmt"
     9  	"io"
    10  	"strings"
    11  )
    12  
    13  // QueryVerify writes a question to w and waits for an answer to be read from
    14  // scanner.  It will pass the answer into the verify function.  Verify, if
    15  // non-nil, should check the answer for validity, returning an error that will
    16  // be written out to errOut, or nil if answer is valid.
    17  //
    18  // This function takes a scanner rather than an io.Reader to avoid the case
    19  // where the scanner reads past the delimiter and thus might lose data.  It is
    20  // expected that this method will be used repeatedly with the same scanner if
    21  // multiple queries are required.
    22  func QueryVerify(question string, scanner *bufio.Scanner, out, errOut io.Writer, verify VerifyFunc) (answer string, err error) {
    23  	defer fmt.Fprint(out, "\n")
    24  	for {
    25  		if _, err = out.Write([]byte(question)); err != nil {
    26  			return "", err
    27  		}
    28  
    29  		done := !scanner.Scan()
    30  
    31  		if done {
    32  			if err := scanner.Err(); err != nil {
    33  				return "", err
    34  			}
    35  		}
    36  		answer = scanner.Text()
    37  		if done && answer == "" {
    38  			// EOF
    39  			return "", io.EOF
    40  		}
    41  		if verify == nil {
    42  			return answer, nil
    43  		}
    44  		ok, msg, err := verify(answer)
    45  		if err != nil {
    46  			return "", err
    47  		}
    48  		// valid answer, return it!
    49  		if ok {
    50  			return answer, nil
    51  		}
    52  
    53  		// invalid answer, inform user of problem and retry.
    54  		if msg != "" {
    55  			_, err := fmt.Fprint(errOut, msg+"\n")
    56  			if err != nil {
    57  				return "", err
    58  			}
    59  		}
    60  		_, err = errOut.Write([]byte{'\n'})
    61  		if err != nil {
    62  			return "", err
    63  		}
    64  
    65  		if done {
    66  			// can't query any more, nothing we can do.
    67  			return "", io.EOF
    68  		}
    69  	}
    70  }
    71  
    72  // MatchOptions returns a function that performs a case insensitive comparison
    73  // against the given list of options.  To make a verification function that
    74  // accepts an empty default, include an empty string in the list.
    75  func MatchOptions(options []string, errmsg string) VerifyFunc {
    76  	return func(s string) (ok bool, msg string, err error) {
    77  		for _, opt := range options {
    78  			if strings.ToLower(opt) == strings.ToLower(s) {
    79  				return true, "", nil
    80  			}
    81  		}
    82  		return false, errmsg, nil
    83  	}
    84  }
    85  
    86  // FindMatch does a case-insensitive search of the given options and returns the
    87  // matching option.  Found reports whether s was found in the options.
    88  func FindMatch(s string, options []string) (match string, found bool) {
    89  	for _, opt := range options {
    90  		if strings.ToLower(opt) == strings.ToLower(s) {
    91  			return opt, true
    92  		}
    93  	}
    94  	return "", false
    95  }