BradCypert.com
Prompt the user for a rune at the terminal in Go
June 13, 2019

Prompt the user for a rune at the terminal in Go

Posted on June 13, 2019  (Last modified on December 27, 2022 )
2 minutes  • 356 words
This project uses these versions of languages, frameworks, and libraries.
  • go go : 1.16
This tutorial may work with newer versions and possibly older versions, but has only been tested on the versions mentioned above.

I’ve been spending a lot of my spare time working on a Go project called Deckard . It’s a command line interface (or CLI) for handling database schema changes. Recently, I found myself wanting to prompt the user for a rune before running a command in that CLI.

Why would I do this? Deckard handles schema changes via the migrations pattern . This consists of having an up migration and a down migration. The up migration might be to add a new table or alter a column, while a down migration might be to drop that table or alter that column back to its previous form. This works great, but it’s worth making note that down migrations are often destructive.

The goal was simple. When the user tried to run down migrations, prompt them and ask them to make sure they’re really trying to run down migrations. I mean, I had just accidentally run down migrations while trying to test up migrations a few days! It could happen to anyone. It felt like a necessary feature.

Thankfully, a common pattern is to prompt the user for "[Y/n]". In this pattern, the uppercase letter is the default. However, for Deckard, I ended up swapping the two so that No was the default option. Anyways, Go made this very easy as I was able to simply prompt the user for a rune from the terminal before calling the function that ran the migration.

// warn the user. Downs are usually destructive.
fmt.Printf("Heads up! You're about to run DOWN migrations. These migrations are likely destructive.\n")
fmt.Printf("Would you like to continue? y/N:")
reader := bufio.NewReader(os.Stdin)
char, _, err := reader.ReadRune()

if char == 'Y' || char == 'y' {
	database.RunDown(migration, cmdSteps)
} else {
	log.Fatal("Understood! Aborting...")
}

And that’s it! Simply create a new buffered IO reader from standard in and call .ReadRune() on the reader! Then you can work with the rune like normal! Note: It’s worth mentioning that the above code will hang when prompting for a rune.

If you’d like to learn more about Go, you can find more of my articles on the language here !

Cartoon headshot of Brad Cypert
Follow me

Connect with me to follow along on my journey in my career, open source, and mentorship. Occasionally, I'll share good advice and content (quality not guaranteed).