{- | Module : Main Description : The haskell-updater executable License : GPL-2 or later Fancy output facility. -} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} module Output ( -- * Verbosity Verbosity(..) -- * MonadSay , MonadSay(..) , say , vsay , pkgListPrintLn , printList -- * SayIO , SayIO(..) ) where import Control.Monad.Reader import Control.Monad.State.Strict import qualified Data.Set as Set import System.IO (hPutStrLn, stderr) import Distribution.Gentoo.Packages -- | output mode (chattiness) data Verbosity = Quiet -- ^ Minimal information is printed (@--quiet@) | Normal -- ^ Some information is printed (default) | Verbose -- ^ Extra information is printed (@--verbose@) deriving (Eq, Ord, Show, Read) -- | Monads that have an environment that stores the specified verbosity level, -- and can output messages class Monad m => MonadSay m where outputLn :: String -> m () -- ^ Output a line askVerbosity :: m Verbosity -- | Print a message that will show up if verbosity is set to 'Normal' or -- 'Verbose'. say :: MonadSay m => String -> m () say msg = askVerbosity >>= \case Quiet -> return () Normal -> outputLn msg Verbose -> outputLn msg -- | Print a message that will only show up if verbosity is set to 'Verbose' vsay :: MonadSay m => String -> m () vsay msg = askVerbosity >>= \case Quiet -> return () Normal -> return () Verbose -> outputLn msg -- | Print a bullet list of values with one value per line. printList :: MonadSay m => (a -> String) -> [a] -> m () printList f = mapM_ (say . (++) " * " . f) -- | Print a list of packages, with a description of what they are. pkgListPrintLn :: MonadSay m => String -> Set.Set Package -> m () pkgListPrintLn desc pkgs | null pkgs = do say $ unwords ["No", desc, "packages found!"] say "" | otherwise = askVerbosity >>= \case Quiet -> pure () Normal -> do outputLn $ unwords ["Found", show (Set.size pkgs), desc, "packages."] outputLn "" Verbose -> do outputLn $ unwords ["Found the following", desc, "packages:"] printList printPkg (Set.toList pkgs) outputLn "" -- | A simple wrapper for adding a basic 'MonadSay' instance to 'IO'. -- This always uses 'Normal' verbosity. -- -- Note that we avoid directly adding a 'MonadSay' instance for 'IO', since -- this can cause the type-checker to miss certain mistakes (such as using -- @'liftIO' . 'vsay'@, which would choose the 'IO' instance, thus always -- using 'Normal' verbosity and ignoring the 'Verbosity' given via the -- command line). newtype SayIO a = SayIO { sayIO :: IO a } deriving (Functor, Applicative, Monad, MonadIO) instance MonadSay SayIO where outputLn = SayIO . hPutStrLn stderr askVerbosity = pure Normal instance MonadSay m => MonadSay (StateT s m) where outputLn = lift . outputLn askVerbosity = lift askVerbosity instance MonadSay m => MonadSay (ReaderT r m) where outputLn = lift . outputLn askVerbosity = lift askVerbosity