-
-
Save r0l1/3dcbb0c8f6cfe9c66ab8008f55f8f28b to your computer and use it in GitHub Desktop.
/* MIT License | |
* | |
* Copyright (c) 2017 Roland Singer [roland.singer@desertbit.com] | |
* | |
* 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!