{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable,
ExistentialQuantification, GADTs,
FlexibleInstances, UndecidableInstances,
TypeOperators #-}
module Test.Tasty.Options
(
IsOption(..)
, OptionSet
, setOption
, changeOption
, lookupOption
, singleOption
, OptionDescription(..)
, flagCLParser
, mkFlagCLParser
, mkOptionCLParser
, safeRead
, safeReadBool
) where
import qualified Data.Map as Map
import Data.Map (Map)
import Data.Maybe
import Data.Char (toLower)
import Data.Tagged
import Data.Proxy
import Data.Typeable
import Data.Monoid
import Data.Foldable
import Prelude hiding (mod)
import Options.Applicative
#if !MIN_VERSION_base(4,11,0)
import Data.Semigroup (Semigroup)
import qualified Data.Semigroup (Semigroup((<>)))
#endif
class Typeable v => IsOption v where
defaultValue :: v
parseValue :: String -> Maybe v
optionName :: Tagged v String
optionHelp :: Tagged v String
showDefaultValue :: v -> Maybe String
showDefaultValue v
_ = Maybe String
forall a. Maybe a
Nothing
optionCLParser :: Parser v
optionCLParser = Mod OptionFields v -> Parser v
forall v. IsOption v => Mod OptionFields v -> Parser v
mkOptionCLParser Mod OptionFields v
forall a. Monoid a => a
mempty
data OptionValue = forall v . IsOption v => OptionValue v
newtype OptionSet = OptionSet (Map TypeRep OptionValue)
instance Monoid OptionSet where
mempty :: OptionSet
mempty = Map TypeRep OptionValue -> OptionSet
OptionSet Map TypeRep OptionValue
forall a. Monoid a => a
mempty
OptionSet Map TypeRep OptionValue
a mappend :: OptionSet -> OptionSet -> OptionSet
`mappend` OptionSet Map TypeRep OptionValue
b =
Map TypeRep OptionValue -> OptionSet
OptionSet (Map TypeRep OptionValue -> OptionSet)
-> Map TypeRep OptionValue -> OptionSet
forall a b. (a -> b) -> a -> b
$ (OptionValue -> OptionValue -> OptionValue)
-> Map TypeRep OptionValue
-> Map TypeRep OptionValue
-> Map TypeRep OptionValue
forall k a. Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a
Map.unionWith ((OptionValue -> OptionValue -> OptionValue)
-> OptionValue -> OptionValue -> OptionValue
forall a b c. (a -> b -> c) -> b -> a -> c
flip OptionValue -> OptionValue -> OptionValue
forall a b. a -> b -> a
const) Map TypeRep OptionValue
a Map TypeRep OptionValue
b
instance Semigroup OptionSet where
<> :: OptionSet -> OptionSet -> OptionSet
(<>) = OptionSet -> OptionSet -> OptionSet
forall a. Monoid a => a -> a -> a
mappend
setOption :: IsOption v => v -> OptionSet -> OptionSet
setOption :: v -> OptionSet -> OptionSet
setOption v
v (OptionSet Map TypeRep OptionValue
s) =
Map TypeRep OptionValue -> OptionSet
OptionSet (Map TypeRep OptionValue -> OptionSet)
-> Map TypeRep OptionValue -> OptionSet
forall a b. (a -> b) -> a -> b
$ TypeRep
-> OptionValue
-> Map TypeRep OptionValue
-> Map TypeRep OptionValue
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert (v -> TypeRep
forall a. Typeable a => a -> TypeRep
typeOf v
v) (v -> OptionValue
forall v. IsOption v => v -> OptionValue
OptionValue v
v) Map TypeRep OptionValue
s
lookupOption :: forall v . IsOption v => OptionSet -> v
lookupOption :: OptionSet -> v
lookupOption (OptionSet Map TypeRep OptionValue
s) =
case TypeRep -> Map TypeRep OptionValue -> Maybe OptionValue
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (v -> TypeRep
forall a. Typeable a => a -> TypeRep
typeOf (v
forall a. HasCallStack => a
undefined :: v)) Map TypeRep OptionValue
s of
Just (OptionValue v
x) | Just v
v <- v -> Maybe v
forall a b. (Typeable a, Typeable b) => a -> Maybe b
cast v
x -> v
v
Just {} -> String -> v
forall a. HasCallStack => String -> a
error String
"OptionSet: broken invariant (shouldn't happen)"
Maybe OptionValue
Nothing -> v
forall v. IsOption v => v
defaultValue
changeOption :: forall v . IsOption v => (v -> v) -> OptionSet -> OptionSet
changeOption :: (v -> v) -> OptionSet -> OptionSet
changeOption v -> v
f OptionSet
s = v -> OptionSet -> OptionSet
forall v. IsOption v => v -> OptionSet -> OptionSet
setOption (v -> v
f (v -> v) -> v -> v
forall a b. (a -> b) -> a -> b
$ OptionSet -> v
forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
s) OptionSet
s
singleOption :: IsOption v => v -> OptionSet
singleOption :: v -> OptionSet
singleOption v
v = v -> OptionSet -> OptionSet
forall v. IsOption v => v -> OptionSet -> OptionSet
setOption v
v OptionSet
forall a. Monoid a => a
mempty
data OptionDescription where
Option :: IsOption v => Proxy v -> OptionDescription
flagCLParser
:: forall v . IsOption v
=> Maybe Char
-> v
-> Parser v
flagCLParser :: Maybe Char -> v -> Parser v
flagCLParser Maybe Char
mbShort = Mod FlagFields v -> v -> Parser v
forall v. IsOption v => Mod FlagFields v -> v -> Parser v
mkFlagCLParser ((Char -> Mod FlagFields v) -> Maybe Char -> Mod FlagFields v
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap Char -> Mod FlagFields v
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Maybe Char
mbShort)
mkFlagCLParser
:: forall v . IsOption v
=> Mod FlagFields v
-> v
-> Parser v
mkFlagCLParser :: Mod FlagFields v -> v -> Parser v
mkFlagCLParser Mod FlagFields v
mod v
v = v -> Mod FlagFields v -> Parser v
forall a. a -> Mod FlagFields a -> Parser a
flag' v
v
( String -> Mod FlagFields v
forall (f :: * -> *) a. HasName f => String -> Mod f a
long (Tagged v String -> String
forall k (s :: k) b. Tagged s b -> b
untag (Tagged v String
forall v. IsOption v => Tagged v String
optionName :: Tagged v String))
Mod FlagFields v -> Mod FlagFields v -> Mod FlagFields v
forall a. Semigroup a => a -> a -> a
<> String -> Mod FlagFields v
forall (f :: * -> *) a. String -> Mod f a
help (Tagged v String -> String
forall k (s :: k) b. Tagged s b -> b
untag (Tagged v String
forall v. IsOption v => Tagged v String
optionHelp :: Tagged v String))
Mod FlagFields v -> Mod FlagFields v -> Mod FlagFields v
forall a. Semigroup a => a -> a -> a
<> Mod FlagFields v
mod
)
mkOptionCLParser :: forall v . IsOption v => Mod OptionFields v -> Parser v
mkOptionCLParser :: Mod OptionFields v -> Parser v
mkOptionCLParser Mod OptionFields v
mod =
ReadM v -> Mod OptionFields v -> Parser v
forall a. ReadM a -> Mod OptionFields a -> Parser a
option ReadM v
parse
( String -> Mod OptionFields v
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
name
Mod OptionFields v -> Mod OptionFields v -> Mod OptionFields v
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields v
forall (f :: * -> *) a. String -> Mod f a
help (Tagged v String -> String
forall k (s :: k) b. Tagged s b -> b
untag (Tagged v String
forall v. IsOption v => Tagged v String
optionHelp :: Tagged v String))
Mod OptionFields v -> Mod OptionFields v -> Mod OptionFields v
forall a. Semigroup a => a -> a -> a
<> Mod OptionFields v
mod
)
where
name :: String
name = Tagged v String -> String
forall k (s :: k) b. Tagged s b -> b
untag (Tagged v String
forall v. IsOption v => Tagged v String
optionName :: Tagged v String)
parse :: ReadM v
parse = ReadM String
forall s. IsString s => ReadM s
str ReadM String -> (String -> ReadM v) -> ReadM v
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
ReadM v -> (v -> ReadM v) -> Maybe v -> ReadM v
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> ReadM v
forall a. String -> ReadM a
readerError (String -> ReadM v) -> String -> ReadM v
forall a b. (a -> b) -> a -> b
$ String
"Could not parse " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name) v -> ReadM v
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe v -> ReadM v) -> (String -> Maybe v) -> String -> ReadM v
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> Maybe v
forall v. IsOption v => String -> Maybe v
parseValue
safeRead :: Read a => String -> Maybe a
safeRead :: String -> Maybe a
safeRead String
s
| [(a
x, String
"")] <- ReadS a
forall a. Read a => ReadS a
reads String
s = a -> Maybe a
forall a. a -> Maybe a
Just a
x
| Bool
otherwise = Maybe a
forall a. Maybe a
Nothing
safeReadBool :: String -> Maybe Bool
safeReadBool :: String -> Maybe Bool
safeReadBool String
s =
case ((Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower String
s) of
String
"true" -> Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
String
"false" -> Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
False
String
_ -> Maybe Bool
forall a. Maybe a
Nothing