module UI.Butcher.Monadic.Flag
( Flag(..)
, flagHelp
, flagHelpStr
, flagDefault
, addSimpleBoolFlag
, addSimpleCountFlag
, addSimpleFlagA
, addFlagReadParam
, addFlagReadParams
, addFlagStringParam
, addFlagStringParams
)
where
#include "prelude.inc"
import Control.Monad.Free
import qualified Control.Monad.Trans.MultiRWS.Strict as MultiRWSS
import qualified Control.Monad.Trans.MultiState.Strict as MultiStateS
import qualified Text.PrettyPrint as PP
import Data.HList.ContainsType
import UI.Butcher.Monadic.Internal.Types
import UI.Butcher.Monadic.Internal.Core
import Data.List.Extra ( firstJust )
newtype InpParseString a = InpParseString (StateS.StateT String Maybe a)
deriving (Functor, Applicative, Monad, State.Class.MonadState String, Alternative, MonadPlus)
runInpParseString :: String -> InpParseString a -> Maybe (a, String)
runInpParseString s (InpParseString m) = StateS.runStateT m s
pExpect :: String -> InpParseString ()
pExpect s = InpParseString $ do
inp <- StateS.get
case List.stripPrefix s inp of
Nothing -> mzero
Just rest -> StateS.put rest
pExpectEof :: InpParseString ()
pExpectEof =
InpParseString $ StateS.get >>= \inp -> if null inp then pure () else mzero
pOption :: InpParseString () -> InpParseString ()
pOption m = m <|> return ()
data Flag p = Flag
{ _flag_help :: Maybe PP.Doc
, _flag_default :: Maybe p
}
instance Monoid (Flag p) where
mempty = Flag Nothing Nothing
Flag a1 b1 `mappend` Flag a2 b2 = Flag (a1 <|> a2) (b1 <|> b2)
flagHelp :: PP.Doc -> Flag p
flagHelp h = mempty { _flag_help = Just h }
flagHelpStr :: String -> Flag p
flagHelpStr s = mempty { _flag_help = Just $ PP.text s }
flagDefault :: p -> Flag p
flagDefault d = mempty { _flag_default = Just d }
addSimpleBoolFlag
:: Applicative f
=> String
-> [String]
-> Flag Void
-> CmdParser f out Bool
addSimpleBoolFlag shorts longs flag =
addSimpleBoolFlagAll shorts longs flag (pure ())
addSimpleFlagA
:: String
-> [String]
-> Flag Void
-> f ()
-> CmdParser f out ()
addSimpleFlagA shorts longs flag act
= void $ addSimpleBoolFlagAll shorts longs flag act
addSimpleBoolFlagAll
:: String
-> [String]
-> Flag Void
-> f ()
-> CmdParser f out Bool
addSimpleBoolFlagAll shorts longs flag a
= fmap (not . null)
$ addCmdPartManyA ManyUpperBound1 desc parseF (\() -> a)
where
allStrs = fmap (\c -> "-"++[c]) shorts
++ fmap (\s -> "--"++s) longs
desc :: PartDesc
desc = (maybe id PartWithHelp $ _flag_help flag)
$ PartAlts $ PartLiteral <$> allStrs
parseF :: String -> Maybe ((), String)
parseF str = ( firstJust (\s -> [ ((), drop (length s) str) | s==str ])
allStrs)
<|> ( firstJust (\s -> [ ((), drop (length s + 1) str)
| (s ++ " ") `isPrefixOf` str ])
allStrs)
addSimpleCountFlag :: Applicative f
=> String
-> [String]
-> Flag Void
-> CmdParser f out Int
addSimpleCountFlag shorts longs flag
= fmap length
$ addCmdPartMany ManyUpperBoundN desc parseF
where
allStrs = fmap (\c -> "-"++[c]) shorts
++ fmap (\s -> "--"++s) longs
desc :: PartDesc
desc = (maybe id PartWithHelp $ _flag_help flag)
$ PartAlts $ PartLiteral <$> allStrs
parseF :: String -> Maybe ((), String)
parseF str = ( firstJust (\s -> [ ((), drop (length s) str) | s==str ])
allStrs)
<|> ( firstJust (\s -> [ ((), drop (length s + 1) str)
| (s ++ " ") `isPrefixOf` str ])
allStrs)
addFlagReadParam
:: forall f p out
. (Applicative f, Typeable p, Text.Read.Read p, Show p)
=> String
-> [String]
-> String
-> Flag p
-> CmdParser f out p
addFlagReadParam shorts longs name flag = addCmdPartInpA desc
parseF
(\_ -> pure ())
where
allStrs =
[ Left $ "-" ++ [c] | c <- shorts ] ++ [ Right $ "--" ++ l | l <- longs ]
desc =
(maybe id PartWithHelp $ _flag_help flag)
$ maybe id (PartDefault . show) (_flag_default flag)
$ PartSeq [desc1, desc2]
desc1 :: PartDesc
desc1 = PartAlts $ PartLiteral . either id id <$> allStrs
desc2 = PartVariable name
parseF :: Input -> Maybe (p, Input)
parseF inp = case inp of
InputString str ->
maybe (_flag_default flag <&> \x -> (x, inp)) (Just . second InputString)
$ parseResult
where
parseResult = runInpParseString str $ do
Data.Foldable.msum $ allStrs <&> \case
Left s -> pExpect s *> pOption (pExpect " " <|> pExpect "=")
Right s -> pExpect s *> (pExpect " " <|> pExpect "=")
InpParseString $ do
i <- StateS.get
case Text.Read.reads i of
((x, ' ':r):_) -> StateS.put (dropWhile Char.isSpace r) $> x
((x, "" ):_) -> StateS.put "" $> x
_ -> mzero
InputArgs (arg1:argR) -> case runInpParseString arg1 parser of
Just ((), "") -> case argR of
[] -> Nothing
(arg2:rest) -> readMaybe arg2 <&> \x -> (x, InputArgs rest)
Just ((), remainingStr) ->
readMaybe remainingStr <&> \x -> (x, InputArgs argR)
Nothing -> _flag_default flag <&> \d -> (d, inp)
where
parser :: InpParseString ()
parser = do
Data.Foldable.msum $ allStrs <&> \case
Left s -> pExpect s *> pOption (pExpect "=")
Right s -> pExpect s *> (pExpect "=" <|> pExpectEof)
InputArgs _ -> _flag_default flag <&> \d -> (d, inp)
addFlagReadParams
:: forall f p out
. (Applicative f, Typeable p, Text.Read.Read p, Show p)
=> String
-> [String]
-> String
-> Flag p
-> CmdParser f out [p]
addFlagReadParams shorts longs name flag
= addFlagReadParamsAll shorts longs name flag (\_ -> pure ())
addFlagReadParamsAll
:: forall f p out . (Typeable p, Text.Read.Read p, Show p) => String
-> [String]
-> String
-> Flag p
-> (p -> f ())
-> CmdParser f out [p]
addFlagReadParamsAll shorts longs name flag act = addCmdPartManyInpA
ManyUpperBoundN
desc
parseF
act
where
allStrs =
[ Left $ "-" ++ [c] | c <- shorts ] ++ [ Right $ "--" ++ l | l <- longs ]
desc = (maybe id PartWithHelp $ _flag_help flag) $ PartSeq [desc1, desc2]
desc1 :: PartDesc
desc1 = PartAlts $ PartLiteral . either id id <$> allStrs
desc2 =
(maybe id (PartDefault . show) $ _flag_default flag) $ PartVariable name
parseF :: Input -> Maybe (p, Input)
parseF inp = case inp of
InputString str -> fmap (second InputString) $ parseResult
where
parseResult = runInpParseString str $ do
Data.Foldable.msum $ allStrs <&> \case
Left s -> pExpect s *> pOption (pExpect " " <|> pExpect "=")
Right s -> pExpect s *> (pExpect " " <|> pExpect "=")
InpParseString $ do
i <- StateS.get
case Text.Read.reads i of
((x, ' ':r):_) -> StateS.put (dropWhile Char.isSpace r) $> x
((x, "" ):_) -> StateS.put "" $> x
_ -> lift $ _flag_default flag
InputArgs (arg1:argR) -> case runInpParseString arg1 parser of
Just ((), "") -> case argR of
[] -> mdef
(arg2:rest) -> (readMaybe arg2 <&> \x -> (x, InputArgs rest)) <|> mdef
where mdef = _flag_default flag <&> \p -> (p, InputArgs argR)
Just ((), remainingStr) ->
readMaybe remainingStr <&> \x -> (x, InputArgs argR)
Nothing -> Nothing
where
parser :: InpParseString ()
parser = do
Data.Foldable.msum $ allStrs <&> \case
Left s -> pExpect s *> pOption (pExpect "=")
Right s -> pExpect s *> (pExpect "=" <|> pExpectEof)
InputArgs _ -> Nothing
addFlagStringParam
:: forall f out . (Applicative f) => String
-> [String]
-> String
-> Flag String
-> CmdParser f out String
addFlagStringParam shorts longs name flag = addCmdPartInpA desc
parseF
(\_ -> pure ())
where
allStrs =
[ Left $ "-" ++ [c] | c <- shorts ] ++ [ Right $ "--" ++ l | l <- longs ]
desc = (maybe id PartWithHelp $ _flag_help flag) $ PartSeq [desc1, desc2]
desc1 :: PartDesc
desc1 = PartAlts $ PartLiteral . either id id <$> allStrs
desc2 = PartVariable name
parseF :: Input -> Maybe (String, Input)
parseF inp = case inp of
InputString str ->
maybe (_flag_default flag <&> \x -> (x, inp)) (Just . second InputString)
$ parseResult
where
parseResult = runInpParseString str $ do
Data.Foldable.msum $ allStrs <&> \case
Left s -> pExpect s *> pOption (pExpect " " <|> pExpect "=")
Right s -> pExpect s *> (pExpect " " <|> pExpect "=")
InpParseString $ do
i <- StateS.get
let (x, rest) = break Char.isSpace $ dropWhile Char.isSpace i
StateS.put rest
pure x
InputArgs (arg1:argR) -> case runInpParseString arg1 parser of
Just ((), "" ) -> case argR of
[] -> Nothing
(x:rest) -> Just (x, InputArgs rest)
Just ((), remainingStr) -> Just (remainingStr, InputArgs argR)
Nothing -> _flag_default flag <&> \d -> (d, inp)
where
parser :: InpParseString ()
parser = do
Data.Foldable.msum $ allStrs <&> \case
Left s -> pExpect s *> pOption (pExpect "=")
Right s -> pExpect s *> (pExpect "=" <|> pExpectEof)
InputArgs _ -> _flag_default flag <&> \d -> (d, inp)
addFlagStringParams
:: forall f out
. (Applicative f)
=> String
-> [String]
-> String
-> Flag Void
-> CmdParser f out [String]
addFlagStringParams shorts longs name flag
= addFlagStringParamsAll shorts longs name flag (\_ -> pure ())
addFlagStringParamsAll
:: forall f out . String
-> [String]
-> String
-> Flag Void
-> (String -> f ())
-> CmdParser f out [String]
addFlagStringParamsAll shorts longs name flag act = addCmdPartManyInpA
ManyUpperBoundN
desc
parseF
act
where
allStrs =
[ Left $ "-" ++ [c] | c <- shorts ] ++ [ Right $ "--" ++ l | l <- longs ]
desc = (maybe id PartWithHelp $ _flag_help flag) $ PartSeq [desc1, desc2]
desc1 :: PartDesc
desc1 = PartAlts $ PartLiteral . either id id <$> allStrs
desc2 =
(maybe id (PartDefault . show) $ _flag_default flag) $ PartVariable name
parseF :: Input -> Maybe (String, Input)
parseF inp = case inp of
InputString str -> fmap (second InputString) $ parseResult
where
parseResult = runInpParseString str $ do
Data.Foldable.msum $ allStrs <&> \case
Left s -> pExpect s *> pOption (pExpect " " <|> pExpect "=")
Right s -> pExpect s *> (pExpect " " <|> pExpect "=")
InpParseString $ do
i <- StateS.get
let (x, rest) = break Char.isSpace $ dropWhile Char.isSpace i
StateS.put rest
pure x
InputArgs (arg1:argR) -> case runInpParseString arg1 parser of
Just ((), "" ) -> case argR of
[] -> Nothing
(x:rest) -> Just (x, InputArgs rest)
Just ((), remainingStr) -> Just (remainingStr, InputArgs argR)
Nothing -> Nothing
where
parser :: InpParseString ()
parser = do
Data.Foldable.msum $ allStrs <&> \case
Left s -> pExpect s *> pOption (pExpect "=")
Right s -> pExpect s *> (pExpect "=" <|> pExpectEof)
InputArgs _ -> Nothing