module System.Console.Args.Generics (withArguments) where
import Control.Exception
import Generics.SOP
import Options.Applicative
import System.Environment
import System.Exit
import System.IO
withArguments :: (Generic a, HasDatatypeInfo a, All2 HasOptParser (Code a)) =>
(a -> IO ()) -> IO ()
withArguments action = do
case parser of
Left errorMessage -> do
hPutStrLn stderr errorMessage
exitWith $ ExitFailure 1
Right p -> do
args <- getArgs
let parserResult = execParserPure
(prefs idm)
(info (helper <*> p) fullDesc)
args
case parserResult of
Success a -> action a
Failure failure -> do
progName <- getProgName
let (message, exitCode) = renderFailure failure progName
case exitCode of
ExitFailure _ -> do
hPutStrLn stderr message
exitWith exitCode
ExitSuccess -> do
putStrLn message
CompletionInvoked _ ->
throwIO $ ErrorCall "completion not reported"
parser :: forall a . (Generic a, HasDatatypeInfo a, All2 HasOptParser (Code a)) =>
Either String (Parser a)
parser = case datatypeInfo (Proxy :: Proxy a) of
ADT _ typeName cs -> parseRecord typeName cs
Newtype _ typeName c -> parseRecord typeName (c :* Nil)
parseRecord :: (Generic a, HasDatatypeInfo a, All2 HasOptParser (Code a)) =>
DatatypeName -> NP ConstructorInfo (Code a) -> Either String (Parser a)
parseRecord typeName meta = case meta of
(Record _ fields :* Nil) ->
Right (to <$> SOP <$> Z <$> parseFields fields)
(_ :* _ :* _) -> err "sum-types"
Nil -> err "empty data types"
(Infix{} :* Nil) -> err "infix constructors"
(Constructor{} :* Nil) -> err "constructors without field labels"
where
err :: String -> Either String (Parser a)
err message =
Left ("args-generics doesn't support " ++ message ++ " (" ++ typeName ++ ").")
parseFields :: (All HasOptParser xs) => NP FieldInfo xs -> Parser (NP I xs)
parseFields Nil = pure Nil
parseFields (field :* r) =
(:*) <$> (I <$> parseField field) <*> (parseFields r)
parseField :: (HasOptParser a) => FieldInfo a -> Parser a
parseField (FieldInfo field) = getOptParser field
class HasOptParser a where
getOptParser :: String -> Parser a
instance HasOptParser String where
getOptParser name = strOption (long name)
instance HasOptParser Bool where
getOptParser name = switch (long name)
instance HasOptParser Int where
getOptParser name = option auto (long name)
instance HasOptParser a => HasOptParser (Maybe a) where
getOptParser name = optional (getOptParser name)