-- | Provides the 'MonadOptparse' typeclass.
--
-- @since 0.1
module Effects.Optparse
  ( -- * Effect
    MonadOptparse (..),

    -- * Misc
    OsPath,
    osPath,
    validOsPath,
  )
where

import Control.Monad.Trans.Class (MonadTrans (lift))
import Control.Monad.Trans.Reader (ReaderT)
import FileSystem.OsPath (OsPath)
import FileSystem.OsPath qualified as FS.OsPath
import GHC.Stack (HasCallStack)
import Options.Applicative (ParserInfo, ParserPrefs, ParserResult, ReadM)
import Options.Applicative qualified as OA

-- | Effects for
-- [optparse-applicative](https://hackage.haskell.org/package/optparse-applicative/docs/Options-Applicative.html).
--
-- The vast majority of optparse-applicative is not re-exported, as most of
-- the interface is pure. This is merely the functions needed to run a parser.
--
-- @since 0.1
class (Monad m) => MonadOptparse m where
  -- | Lifted 'OA.execParser'.
  --
  -- @since 0.1
  execParser :: (HasCallStack) => ParserInfo a -> m a

  -- | Lifted 'OA.customExecParser'.
  --
  -- @since 0.1
  customExecParser :: (HasCallStack) => ParserPrefs -> ParserInfo a -> m a

  -- | Lifted 'OA.handleParseResult'.
  --
  -- @since 0.1
  handleParseResult :: (HasCallStack) => ParserResult a -> m a

-- | @since 0.1
instance MonadOptparse IO where
  execParser :: forall a. HasCallStack => ParserInfo a -> IO a
execParser = ParserInfo a -> IO a
forall a. ParserInfo a -> IO a
OA.execParser
  {-# INLINEABLE execParser #-}
  customExecParser :: forall a. HasCallStack => ParserPrefs -> ParserInfo a -> IO a
customExecParser = ParserPrefs -> ParserInfo a -> IO a
forall a. ParserPrefs -> ParserInfo a -> IO a
OA.customExecParser
  {-# INLINEABLE customExecParser #-}
  handleParseResult :: forall a. HasCallStack => ParserResult a -> IO a
handleParseResult = ParserResult a -> IO a
forall a. ParserResult a -> IO a
OA.handleParseResult
  {-# INLINEABLE handleParseResult #-}

-- | @since 0.1
instance (MonadOptparse m) => MonadOptparse (ReaderT env m) where
  execParser :: forall a. HasCallStack => ParserInfo a -> ReaderT env m a
execParser = m a -> ReaderT env m a
forall (m :: * -> *) a. Monad m => m a -> ReaderT env m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m a -> ReaderT env m a)
-> (ParserInfo a -> m a) -> ParserInfo a -> ReaderT env m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ParserInfo a -> m a
forall a. HasCallStack => ParserInfo a -> m a
forall (m :: * -> *) a.
(MonadOptparse m, HasCallStack) =>
ParserInfo a -> m a
execParser
  {-# INLINEABLE execParser #-}
  customExecParser :: forall a.
HasCallStack =>
ParserPrefs -> ParserInfo a -> ReaderT env m a
customExecParser ParserPrefs
p = m a -> ReaderT env m a
forall (m :: * -> *) a. Monad m => m a -> ReaderT env m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m a -> ReaderT env m a)
-> (ParserInfo a -> m a) -> ParserInfo a -> ReaderT env m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ParserPrefs -> ParserInfo a -> m a
forall a. HasCallStack => ParserPrefs -> ParserInfo a -> m a
forall (m :: * -> *) a.
(MonadOptparse m, HasCallStack) =>
ParserPrefs -> ParserInfo a -> m a
customExecParser ParserPrefs
p
  {-# INLINEABLE customExecParser #-}
  handleParseResult :: forall a. HasCallStack => ParserResult a -> ReaderT env m a
handleParseResult = m a -> ReaderT env m a
forall (m :: * -> *) a. Monad m => m a -> ReaderT env m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m a -> ReaderT env m a)
-> (ParserResult a -> m a) -> ParserResult a -> ReaderT env m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ParserResult a -> m a
forall a. HasCallStack => ParserResult a -> m a
forall (m :: * -> *) a.
(MonadOptparse m, HasCallStack) =>
ParserResult a -> m a
handleParseResult
  {-# INLINEABLE handleParseResult #-}

-- | 'OsPath' 'OA.Option' reader.
--
-- @since 0.1
osPath :: ReadM OsPath
osPath :: ReadM OsPath
osPath = ReadM FilePath
forall s. IsString s => ReadM s
OA.str ReadM FilePath -> (FilePath -> ReadM OsPath) -> ReadM OsPath
forall a b. ReadM a -> (a -> ReadM b) -> ReadM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= FilePath -> ReadM OsPath
forall (m :: * -> *).
(HasCallStack, MonadFail m) =>
FilePath -> m OsPath
FS.OsPath.encodeFail

-- | 'OsPath' 'OA.Option' reader. This includes validation i.e. fails if the
-- path is considered invalid on the given platform.
--
-- @since 0.1
validOsPath :: ReadM OsPath
validOsPath :: ReadM OsPath
validOsPath = ReadM FilePath
forall s. IsString s => ReadM s
OA.str ReadM FilePath -> (FilePath -> ReadM OsPath) -> ReadM OsPath
forall a b. ReadM a -> (a -> ReadM b) -> ReadM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= FilePath -> ReadM OsPath
forall (m :: * -> *).
(HasCallStack, MonadFail m) =>
FilePath -> m OsPath
FS.OsPath.encodeValidFail