I've been learning Scala and I'd love to get your feedback in order to improve. As an exercise I made a simple framework for creating command-line user interfaces.
package com.ronyhe
import java.util.Scanner
import scala.collection.Set
/** A framework for creating command-line user interfaces */
package object cmd {
/**
* Present a description to the user and wait for a response
*
* This is the basic unit of interacting with the user
*
* @return the text the user typed as a response
*/
def textInput(description: String): String = {
println(description)
getUserInput
}
/**
* Pose a yes or no question to the user.
*
* This method adds a short explanation of the expected input to the description.
* Example: "Do you like Scala?" => "Do you like Scala? (y\n)"
*
* @return true if the user answered yes, false otherwise
*/
def yesOrNo(description: String): Boolean = {
import Strings.{InputsMeaningNo, InputsMeaningYes, YesOrNoSuffix}
val input = textInput(description + YesOrNoSuffix)
val isYes = InputsMeaningYes contains input
val isNo = InputsMeaningNo contains input
if (isYes)
true
else if (isNo)
false
else {
ErrorReporting.inputIsNotYesOrNo()
yesOrNo(description)
}
}
/**
* Present the user with options to select from, with a limited number of selections allowed
*
* This method adds a short explanation of the expected input to the description if needed.
* Example: "Select the relevant options" =>
* "Select the relevant options" + "\nType the numbers of the desired options separated by commas: '1, 2, 3'"
*
* @param description the instruction presented to the user
* @param options the options to display to the user
* @param numberOfSelectionsAllowed the maximum amount of options the user can choose.
* @return a Set[Int] with the indexes of the options the user selected
*/
def selection(description: String, options: Seq[String], numberOfSelectionsAllowed: Int): Set[Int] = {
val amountOfOptions = options.length
require(numberOfSelectionsAllowed > 0 && numberOfSelectionsAllowed <= amountOfOptions)
val textFunction = numberOfSelectionsAllowed match {
case 1 => Strings.singleSelectionsText _
case _ => Strings.multiSelectionText _
}
val text = textFunction(description, options)
val input = textInput(text)
def tryAgain = selection(description, options, numberOfSelectionsAllowed)
def splitToIndexes(commaSeparatedListOfInts: String) =
commaSeparatedListOfInts.split(',').map(_.trim).map(_.toInt - 1).toSet
val parsed: Set[Int] = try splitToIndexes(input)
catch {
case _: Exception =>
ErrorReporting.inputIsNotCommaSeparatedListOfIntegers()
tryAgain
}
val correctAmount = parsed.nonEmpty && parsed.size <= numberOfSelectionsAllowed
if (!correctAmount) {
ErrorReporting.selectedOptionIsOutOfBounds(amountOfOptions)
return tryAgain
}
val allSelectionsAreInBounds = parsed forall { i => i >= 0 && i < amountOfOptions }
if (!allSelectionsAreInBounds) {
ErrorReporting.selectedOptionIsOutOfBounds(amountOfOptions)
return tryAgain
}
parsed
}
/** Present the user with options to select from */
def multiSelection(description: String, options: Seq[String]): Set[Int] =
selection(description, options, options.length)
/** Present the user with options to select from. Allow only one option to be selected */
def singleSelection(description: String, options: Seq[String]): Int = selection(description, options, 1).head
private val scanner = new Scanner(System.in)
private def print(x: Any) = System.out.print(x)
private def println(x: Any) = System.out.println(x)
private def getUserInput = {
print(Strings.Prompt)
scanner.nextLine()
}
private object ErrorReporting {
import Strings.ErrorMessages
def invalidAmountOfOptions(maxAllowed: Int) = {
val message = ErrorMessages.invalidAmountOfOptionsSelected(maxAllowed)
println(message)
}
def inputIsNotAnInteger() = println(ErrorMessages.NotAnInteger)
def selectedOptionIsOutOfBounds(amountOfOptions: Int) = {
val message = ErrorMessages.numberNotInRange(amountOfOptions)
println(message)
}
def inputIsNotYesOrNo() = println(ErrorMessages.InputIsNotYesOrNo)
def inputIsNotCommaSeparatedListOfIntegers() = println(ErrorMessages.MultiSelectionCouldNotBeParsed)
}
}