-
-
Save r0l1/3dcbb0c8f6cfe9c66ab8008f55f8f28b to your computer and use it in GitHub Desktop.
| /* MIT License | |
| * | |
| * Copyright (c) 2017 Roland Singer [[email protected]] | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| * of this software and associated documentation files (the "Software"), to deal | |
| * in the Software without restriction, including without limitation the rights | |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| * copies of the Software, and to permit persons to whom the Software is | |
| * furnished to do so, subject to the following conditions: | |
| * | |
| * The above copyright notice and this permission notice shall be included in all | |
| * copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| * SOFTWARE. | |
| */ | |
| package main | |
| import ( | |
| "bufio" | |
| "fmt" | |
| "log" | |
| "os" | |
| "strings" | |
| ) | |
| // askForConfirmation asks the user for confirmation. A user must type in "yes" or "no" and | |
| // then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES", and "Yes" all count as | |
| // confirmations. If the input is not recognized, it will ask again. The function does not return | |
| // until it gets a valid response from the user. | |
| func askForConfirmation(s string) bool { | |
| reader := bufio.NewReader(os.Stdin) | |
| for { | |
| fmt.Printf("%s [y/n]: ", s) | |
| response, err := reader.ReadString('\n') | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| response = strings.ToLower(strings.TrimSpace(response)) | |
| if response == "y" || response == "yes" { | |
| return true | |
| } else if response == "n" || response == "no" { | |
| return false | |
| } | |
| } | |
| } | |
| func main() { | |
| c := askForConfirmation("Do you really want to reset your system?") | |
| if c { | |
| fmt.Println("Sorry dude, I need root rights...") | |
| return | |
| } | |
| } |
@das-vinculum Yes, it should. The user will be asked again.
@das-vinculum:
If the input is not recognized, it will ask again. The function does not return until it gets a valid response from the user.
It should probably be made to fail after several tries.
My approach is a little more strict (anything other than 'y' is false), but could be tweaked to only accept 'y' or 'n'.
// confirm displays a prompt `s` to the user and returns a bool indicating yes / no
// If the lowercased, trimmed input begins with anything other than 'y', it returns false
// It accepts an int `tries` representing the number of attempts before returning false
func confirm(s string, tries int) bool {
r := bufio.NewReader(os.Stdin)
for ; tries > 0; tries-- {
fmt.Printf("%s [y/n]: ", s)
res, err := r.ReadString('\n')
if err != nil {
log.Fatal(err)
}
// Empty input (i.e. "\n")
if len(res) < 2 {
continue
}
return strings.ToLower(strings.TrimSpace(res))[0] == 'y'
}
return false
}Just for learning sake, How would you write a unit test for something like this?
you'd have to mock out the call out ReadString for testing
@davideasaf You can slightly change the function signature:
func askForConfirmation(s string, in io.Reader) bool {
reader := bufio.NewReader(in)
...
}That will allow you to pass in a mock reader in unit tests and os.Stdin in your production code.
My approach is a little more strict (anything other than 'y' is false), but could be tweaked to only accept 'y' or 'n'.
// confirm displays a prompt `s` to the user and returns a bool indicating yes / no // If the lowercased, trimmed input begins with anything other than 'y', it returns false // It accepts an int `tries` representing the number of attempts before returning false func confirm(s string, tries int) bool { r := bufio.NewReader(os.Stdin) for ; tries > 0; tries-- { fmt.Printf("%s [y/n]: ", s) res, err := r.ReadString('\n') if err != nil { log.Fatal(err) } // Empty input (i.e. "\n") if len(res) < 2 { continue } return strings.ToLower(strings.TrimSpace(res))[0] == 'y' } return false }
@ryanbaer Works great, but I'm getting an error when just pressing the enter key. Shouldn't it be if len(res) < 3 { continue } as "\n" already has a length of 2?
if you don't care about retries:
func askForConfirmation(s string) bool {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("%s [y/N]: ", s)
response, err := reader.ReadString('\n')
if err != nil {
panic(err)
}
response = strings.ToLower(strings.TrimSpace(response))
if response == "y" || response == "yes" {
return true
}
return false
}if you don't care about retries:
func askForConfirmation(s string) bool {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("%s [y/N]: ", s)
response, err := reader.ReadString('\n')
if err != nil {
panic(err)
}
response = strings.ToLower(strings.TrimSpace(response))
if response == "y" || response == "yes" {
return true
}
return false
}
That doesn't work if the user enters garbage, like
oh yeah!