packages feed

gitrev-typed-0.1: src/Development/GitRev/Internal/Utils.hs

{-# LANGUAGE QuantifiedConstraints #-}

-- | Utils module.
--
-- @since 0.1
module Development.GitRev.Internal.Utils
  ( -- * Either projections
    projectStringUnknown,
    Common.projectConst,
    Common.projectFalse,
    Common.projectError,
    Common.projectErrorMap,
    Common.projectLeft,

    -- * Composing errors
    GitRevError (..),

    -- ** Functions
    runGitInEnvDirQ,

    -- ** Mapping utilities
    embedGitError,
    embedEnvError,
    embedTextError,
    Common.joinFirst,
  )
where

import Control.Exception (Exception (displayException))
import Data.Bifunctor (Bifunctor (first))
import Data.Text (Text)
import Data.Text qualified as T
import Development.GitRev.Internal.Environment (EnvError)
import Development.GitRev.Internal.Environment qualified as Env
import Development.GitRev.Internal.Git (GitError)
import Development.GitRev.Internal.Utils.Common qualified as Common
import Language.Haskell.TH (Q)
import Language.Haskell.TH.Syntax (Lift)

-- $setup
-- >>> import Development.GitRev.Typed (qToCode)
-- >>> import Development.GitRev.Internal.Git (GitError (..), gitDirtyQ, gitHashQ)
-- >>> import Development.GitRev.Internal.Environment (EnvError (..))
-- >>> import Language.Haskell.TH (Q, runIO, runQ)
-- >>> import System.Environment (setEnv)

-- | Projects 'Left' to the string @UNKNOWN@.
--
-- ==== __Examples__
--
-- >>> $$(qToCode $ projectStringUnknown (pure $ Left ()))
-- "UNKNOWN"
--
-- @since 0.1
projectStringUnknown ::
  forall f e.
  (Functor f) =>
  f (Either e String) ->
  f String
projectStringUnknown = Common.projectConst "UNKNOWN"

-- | General error type for anything that can go wrong when running
-- @gitrev-typed@ splices.
--
-- @since 0.1
data GitRevError
  = -- | Git error.
    --
    -- @since 0.1
    GitRevErrorGit GitError
  | -- | Environment variable lookup error.
    --
    -- @since 0.1
    GitRevErrorEnv EnvError
  | -- | Catch-all for anything else that can go wrong.
    --
    -- @since 0.1
    GitRevErrorText Text
  deriving stock
    ( -- | @since 0.1
      Eq,
      -- | @since 0.1
      Lift,
      -- | @since 0.1
      Show
    )

-- | @since 0.1
instance Exception GitRevError where
  displayException (GitRevErrorGit ge) = displayException ge
  displayException (GitRevErrorEnv x) = displayException x
  displayException (GitRevErrorText txt) = T.unpack txt

-- | Runs the git action under the directory @d@ pointed to by the
-- given environment variable.
--
-- ==== __Examples__
--
-- >>> setEnv "SOME_DIR" "./"
-- >>> $$(qToCode $ runGitInEnvDirQ "SOME_DIR" gitHashQ)
-- Right ...
--
-- @since 0.1
runGitInEnvDirQ ::
  forall a.
  -- | Environment variable pointing to a directory path, in which we run
  -- the git process.
  String ->
  -- | Git process to run.
  Q (Either GitError a) ->
  -- | The result.
  Q (Either GitRevError a)
runGitInEnvDirQ var =
  fmap (Common.joinFirst GitRevErrorEnv GitRevErrorGit)
    . Env.runInEnvDirQ var

-- | Embeds a 'GitError' in the larger 'GitRevError'.
--
-- ==== __Examples__
--
-- >>> :{
--   let q :: Q (Either GitError ())
--       q = pure (Left $ MkGitError "not found")
--   in runQ $ embedGitError q
-- :}
-- Left (GitRevErrorGit (MkGitError {reason = "not found"}))
--
-- @since 0.1
embedGitError ::
  forall f p a.
  ( Bifunctor p,
    Functor f
  ) =>
  -- | .
  f (p GitError a) ->
  f (p GitRevError a)
embedGitError = fmap (first GitRevErrorGit)

-- | Embeds an 'EnvError' in the larger 'GitRevError'.
--
-- ==== __Examples__
--
-- >>> :{
--   let q :: Q (Either EnvError ())
--       q = pure (Left $ MkEnvError "VAR" Nothing "VAR does not exist")
--   in runQ $ embedEnvError q
-- :}
-- Left (GitRevErrorEnv (MkEnvError {var = "VAR", value = Nothing, reason = "VAR does not exist"}))
--
-- @since 0.1
embedEnvError ::
  forall f p a.
  ( Bifunctor p,
    Functor f
  ) =>
  -- | .
  f (p EnvError a) ->
  f (p GitRevError a)
embedEnvError = fmap (first GitRevErrorEnv)

-- | Embeds a 'Text' in the larger 'GitRevError'.
--
-- ==== __Examples__
--
-- >>> :{
--   let q :: Q (Either Text ())
--       q = pure (Left "Something went wrong")
--   in runQ $ embedTextError q
-- :}
-- Left (GitRevErrorText "Something went wrong")
--
-- @since 0.1
embedTextError ::
  forall f p a.
  ( Bifunctor p,
    Functor f
  ) =>
  -- | .
  f (p Text a) ->
  f (p GitRevError a)
embedTextError = fmap (first GitRevErrorText)