{-# LANGUAGE FlexibleInstances #-}

-- | This module is responsible for handling CLI options
module Language.Fortran.Extras.RunOptions
  ( FortranSrcRunOptions(..)
  , getFortranSrcRunOptions
  , unwrapFortranSrcOptions
  , RunOptions(..)
  , getRunOptions
  )
where

import qualified Data.ByteString.Char8         as B
import           Data.Char                      ( toLower )
import           Data.List                      ( isInfixOf )
import           Data.Semigroup                 ( (<>) )
import           Language.Fortran.Version       ( FortranVersion(..)
                                                , selectFortranVersion
                                                )
import           Options.Applicative

import           Language.Fortran.Util.Files
                                                ( flexReadFile )

-- | Holds fortran-src specific CLI options.
-- This includes the version of the parser, included files and the path
-- of the source
data FortranSrcRunOptions = FortranSrcRunOptions
    { FortranSrcRunOptions -> FortranVersion
version  :: !FortranVersion
    , FortranSrcRunOptions -> [String]
includes :: ![String]
    , FortranSrcRunOptions -> String
path     :: !String
    } deriving Int -> FortranSrcRunOptions -> ShowS
[FortranSrcRunOptions] -> ShowS
FortranSrcRunOptions -> String
(Int -> FortranSrcRunOptions -> ShowS)
-> (FortranSrcRunOptions -> String)
-> ([FortranSrcRunOptions] -> ShowS)
-> Show FortranSrcRunOptions
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [FortranSrcRunOptions] -> ShowS
$cshowList :: [FortranSrcRunOptions] -> ShowS
show :: FortranSrcRunOptions -> String
$cshow :: FortranSrcRunOptions -> String
showsPrec :: Int -> FortranSrcRunOptions -> ShowS
$cshowsPrec :: Int -> FortranSrcRunOptions -> ShowS
Show

-- | Provided version, includes and path strings, this functon maps them to
-- 'FortranSrcRunOptions'
--
-- Note that this will throw an exception on an unrecognized version string.
toFortranSrcOptions :: String -> [String] -> String -> FortranSrcRunOptions
toFortranSrcOptions :: String -> [String] -> String -> FortranSrcRunOptions
toFortranSrcOptions String
verStr =
    let (Just FortranVersion
ver) = String -> Maybe FortranVersion
selectFortranVersion String
verStr
     in FortranVersion -> [String] -> String -> FortranSrcRunOptions
FortranSrcRunOptions FortranVersion
ver

-- | Definition of parser for 'FortranSrcRunOptions'
fortranSrcRunOptionsParser :: Parser FortranSrcRunOptions
fortranSrcRunOptionsParser :: Parser FortranSrcRunOptions
fortranSrcRunOptionsParser =
  String -> [String] -> String -> FortranSrcRunOptions
toFortranSrcOptions
    (String -> [String] -> String -> FortranSrcRunOptions)
-> Parser String
-> Parser ([String] -> String -> FortranSrcRunOptions)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Mod OptionFields String -> Parser String
forall s. IsString s => Mod OptionFields s -> Parser s
strOption
          (  Char -> Mod OptionFields String
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'v'
          Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields String
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"fortranVersion"
          Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields String
forall (f :: * -> *) a. HasMetavar f => String -> Mod f a
metavar String
"VERSION"
          Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields String
forall (f :: * -> *) a. String -> Mod f a
help
               String
"Fortran version to use, format: Fortran[66/77/77l/77e/90/95/03/08]"
          )
    Parser ([String] -> String -> FortranSrcRunOptions)
-> Parser [String] -> Parser (String -> FortranSrcRunOptions)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser String -> Parser [String]
forall (f :: * -> *) a. Alternative f => f a -> f [a]
many
          (Mod OptionFields String -> Parser String
forall s. IsString s => Mod OptionFields s -> Parser s
strOption
            (Char -> Mod OptionFields String
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'I' Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields String
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"include" Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields String
forall (f :: * -> *) a. HasMetavar f => String -> Mod f a
metavar String
"DIRECTORY" Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields String
forall (f :: * -> *) a. String -> Mod f a
help
              String
"Directory to include files from"
            )
          )
    Parser (String -> FortranSrcRunOptions)
-> Parser String -> Parser FortranSrcRunOptions
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ReadM String -> Mod ArgumentFields String -> Parser String
forall a. ReadM a -> Mod ArgumentFields a -> Parser a
argument ReadM String
forall s. IsString s => ReadM s
str (String -> Mod ArgumentFields String
forall (f :: * -> *) a. HasMetavar f => String -> Mod f a
metavar String
"PATH")

-- | Given description and header, decorate fortran-src options parser with
-- info details
fortranSrcRunOptionsInfo :: String -> String -> ParserInfo FortranSrcRunOptions
fortranSrcRunOptionsInfo :: String -> String -> ParserInfo FortranSrcRunOptions
fortranSrcRunOptionsInfo String
programDescription String
headerDescription = Parser FortranSrcRunOptions
-> InfoMod FortranSrcRunOptions -> ParserInfo FortranSrcRunOptions
forall a. Parser a -> InfoMod a -> ParserInfo a
info
  (Parser (FortranSrcRunOptions -> FortranSrcRunOptions)
forall a. Parser (a -> a)
helper Parser (FortranSrcRunOptions -> FortranSrcRunOptions)
-> Parser FortranSrcRunOptions -> Parser FortranSrcRunOptions
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser FortranSrcRunOptions
fortranSrcRunOptionsParser)
  (InfoMod FortranSrcRunOptions
forall a. InfoMod a
fullDesc InfoMod FortranSrcRunOptions
-> InfoMod FortranSrcRunOptions -> InfoMod FortranSrcRunOptions
forall a. Semigroup a => a -> a -> a
<> String -> InfoMod FortranSrcRunOptions
forall a. String -> InfoMod a
progDesc String
programDescription InfoMod FortranSrcRunOptions
-> InfoMod FortranSrcRunOptions -> InfoMod FortranSrcRunOptions
forall a. Semigroup a => a -> a -> a
<> String -> InfoMod FortranSrcRunOptions
forall a. String -> InfoMod a
header String
headerDescription)

-- | Given description and header, execute fortran-src options parser
-- and get the 'FortranSrcRunOptions'
getFortranSrcRunOptions :: String -> String -> IO FortranSrcRunOptions
getFortranSrcRunOptions :: String -> String -> IO FortranSrcRunOptions
getFortranSrcRunOptions String
programDescription String
headerDescription =
  ParserInfo FortranSrcRunOptions -> IO FortranSrcRunOptions
forall a. ParserInfo a -> IO a
execParser (ParserInfo FortranSrcRunOptions -> IO FortranSrcRunOptions)
-> ParserInfo FortranSrcRunOptions -> IO FortranSrcRunOptions
forall a b. (a -> b) -> a -> b
$ String -> String -> ParserInfo FortranSrcRunOptions
fortranSrcRunOptionsInfo String
programDescription String
headerDescription

-- | Obtain path, contents, include dirs and 'FortranVersion'
-- from 'FortranSrcRunOptions'
unwrapFortranSrcOptions
  :: FortranSrcRunOptions -> IO (String, B.ByteString, [String], FortranVersion)
unwrapFortranSrcOptions :: FortranSrcRunOptions
-> IO (String, ByteString, [String], FortranVersion)
unwrapFortranSrcOptions FortranSrcRunOptions
options = do
  let p :: String
p = FortranSrcRunOptions -> String
path FortranSrcRunOptions
options
  ByteString
c <- String -> IO ByteString
flexReadFile String
p
  let i :: [String]
i = FortranSrcRunOptions -> [String]
includes FortranSrcRunOptions
options
      v :: FortranVersion
v = FortranSrcRunOptions -> FortranVersion
version FortranSrcRunOptions
options
  (String, ByteString, [String], FortranVersion)
-> IO (String, ByteString, [String], FortranVersion)
forall (m :: * -> *) a. Monad m => a -> m a
return (String
p, ByteString
c, [String]
i, FortranVersion
v)

-- | Holds 'FortranSrcRunOptions' and additional tool specific CLI options
data RunOptions a = RunOptions
    { RunOptions a -> FortranSrcRunOptions
fortranSrcOpts :: FortranSrcRunOptions
    , RunOptions a -> a
toolOpts :: a
    } deriving Int -> RunOptions a -> ShowS
[RunOptions a] -> ShowS
RunOptions a -> String
(Int -> RunOptions a -> ShowS)
-> (RunOptions a -> String)
-> ([RunOptions a] -> ShowS)
-> Show (RunOptions a)
forall a. Show a => Int -> RunOptions a -> ShowS
forall a. Show a => [RunOptions a] -> ShowS
forall a. Show a => RunOptions a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RunOptions a] -> ShowS
$cshowList :: forall a. Show a => [RunOptions a] -> ShowS
show :: RunOptions a -> String
$cshow :: forall a. Show a => RunOptions a -> String
showsPrec :: Int -> RunOptions a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> RunOptions a -> ShowS
Show

-- | Given decription, header and tool options parser, decorate options
-- with info details
runOptionsInfo :: String -> String -> Parser a -> ParserInfo (RunOptions a)
runOptionsInfo :: String -> String -> Parser a -> ParserInfo (RunOptions a)
runOptionsInfo String
programDescription String
headerDescription Parser a
toolOptsParser = Parser (RunOptions a)
-> InfoMod (RunOptions a) -> ParserInfo (RunOptions a)
forall a. Parser a -> InfoMod a -> ParserInfo a
info
  (Parser (RunOptions a -> RunOptions a)
forall a. Parser (a -> a)
helper Parser (RunOptions a -> RunOptions a)
-> Parser (RunOptions a) -> Parser (RunOptions a)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (FortranSrcRunOptions -> a -> RunOptions a)
-> Parser FortranSrcRunOptions -> Parser a -> Parser (RunOptions a)
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 FortranSrcRunOptions -> a -> RunOptions a
forall a. FortranSrcRunOptions -> a -> RunOptions a
RunOptions Parser FortranSrcRunOptions
fortranSrcRunOptionsParser Parser a
toolOptsParser)
  (InfoMod (RunOptions a)
forall a. InfoMod a
fullDesc InfoMod (RunOptions a)
-> InfoMod (RunOptions a) -> InfoMod (RunOptions a)
forall a. Semigroup a => a -> a -> a
<> String -> InfoMod (RunOptions a)
forall a. String -> InfoMod a
progDesc String
programDescription InfoMod (RunOptions a)
-> InfoMod (RunOptions a) -> InfoMod (RunOptions a)
forall a. Semigroup a => a -> a -> a
<> String -> InfoMod (RunOptions a)
forall a. String -> InfoMod a
header String
headerDescription)

-- | Given description, header and tool options parser, execute options parser
-- and get the 'RunOptions'
getRunOptions :: String -> String -> Parser a -> IO (RunOptions a)
getRunOptions :: String -> String -> Parser a -> IO (RunOptions a)
getRunOptions String
programDescription String
headerDescription Parser a
toolOptsParser =
  ParserInfo (RunOptions a) -> IO (RunOptions a)
forall a. ParserInfo a -> IO a
execParser
    (ParserInfo (RunOptions a) -> IO (RunOptions a))
-> ParserInfo (RunOptions a) -> IO (RunOptions a)
forall a b. (a -> b) -> a -> b
$ String -> String -> Parser a -> ParserInfo (RunOptions a)
forall a. String -> String -> Parser a -> ParserInfo (RunOptions a)
runOptionsInfo String
programDescription String
headerDescription Parser a
toolOptsParser