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