41 lines
1.1 KiB
Haskell
41 lines
1.1 KiB
Haskell
{-# LANGUAGE NamedFieldPuns #-}
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
module Menu (
|
|
Menu(..)
|
|
, Promptable(..)
|
|
, formatTitle
|
|
, select
|
|
) where
|
|
|
|
import Control.Monad.IO.Class (MonadIO(..))
|
|
import Text.Printf (printf)
|
|
import Utils (outputLn, prompt)
|
|
|
|
data Menu a = Menu {
|
|
title :: String
|
|
, question :: String
|
|
, invalidText :: String
|
|
}
|
|
|
|
formatTitle :: String -> [String]
|
|
formatTitle aTitle = [line, formattedTitle, line]
|
|
where
|
|
formattedTitle = "==> " ++ aTitle
|
|
line = const '-' <$> formattedTitle
|
|
|
|
formatOption :: Promptable a => a -> String
|
|
formatOption o = printf "%d. %s" (fromEnum o) (showOption o)
|
|
|
|
class (Bounded a, Enum a) => Promptable a where
|
|
menu :: Menu a
|
|
showOption :: a -> String
|
|
|
|
select :: forall a m. (MonadIO m, Promptable a) => m a
|
|
select = toEnum <$> prompt
|
|
(mapM_ outputLn (formatTitle title ++ options ++ [question]))
|
|
(outputLn invalidText)
|
|
(fromEnum (minBound :: a), fromEnum (maxBound :: a))
|
|
where
|
|
Menu {title, question, invalidText} = (menu :: Menu a)
|
|
options = formatOption <$> ([minBound .. maxBound] :: [a])
|