243 lines
6.2 KiB
Haskell
243 lines
6.2 KiB
Haskell
{-# OPTIONS_GHC -Wno-orphans #-}
|
|
{-# LANGUAGE QuasiQuotes #-}
|
|
{-# LANGUAGE TemplateHaskell #-}
|
|
{-# LANGUAGE DataKinds #-}
|
|
{-# LANGUAGE TypeOperators #-}
|
|
{-# LANGUAGE DeriveLift #-}
|
|
{-# LANGUAGE StandaloneDeriving #-}
|
|
{-# LANGUAGE FlexibleInstances #-}
|
|
{-# LANGUAGE FlexibleContexts #-}
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
{-# LANGUAGE AllowAmbiguousTypes #-}
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
|
|
module GHCup.Prelude where
|
|
|
|
import Control.Applicative
|
|
import Control.Exception.Safe
|
|
import Control.Monad
|
|
import Control.Monad.IO.Class
|
|
import Control.Monad.Trans.Class ( lift )
|
|
import Data.Bifunctor
|
|
import Data.ByteString ( ByteString )
|
|
import Data.Monoid ( (<>) )
|
|
import Data.String
|
|
import Data.Text ( Text )
|
|
import Data.Versions
|
|
import GHC.Base
|
|
import Haskus.Utils.Types.List
|
|
import Haskus.Utils.Variant.Excepts
|
|
import Language.Haskell.TH
|
|
import Language.Haskell.TH.Quote ( QuasiQuoter(..) )
|
|
import Language.Haskell.TH.Syntax ( Exp(..) , Lift)
|
|
import System.IO.Error
|
|
|
|
import qualified Data.ByteString.Lazy as L
|
|
import qualified Data.Strict.Maybe as S
|
|
import qualified Data.Text as T
|
|
import qualified Data.Text.Encoding as E
|
|
import qualified Data.Text.Lazy as TL
|
|
import qualified Data.Text.Lazy.Builder as B
|
|
import qualified Data.Text.Lazy.Builder.Int as B
|
|
import qualified Data.Text.Lazy.Encoding as TLE
|
|
import qualified Language.Haskell.TH.Syntax as TH
|
|
|
|
|
|
|
|
fS :: IsString a => String -> a
|
|
fS = fromString
|
|
|
|
fromStrictMaybe :: S.Maybe a -> Maybe a
|
|
fromStrictMaybe = S.maybe Nothing Just
|
|
|
|
fSM :: S.Maybe a -> Maybe a
|
|
fSM = fromStrictMaybe
|
|
|
|
toStrictMaybe :: Maybe a -> S.Maybe a
|
|
toStrictMaybe = maybe S.Nothing S.Just
|
|
|
|
tSM :: Maybe a -> S.Maybe a
|
|
tSM = toStrictMaybe
|
|
|
|
internalError :: String -> IO a
|
|
internalError = fail . ("Internal error: " <>)
|
|
|
|
iE :: String -> IO a
|
|
iE = internalError
|
|
|
|
|
|
showT :: Show a => a -> Text
|
|
showT = fS . show
|
|
|
|
-- | Like 'when', but where the test can be monadic.
|
|
whenM :: Monad m => m Bool -> m () -> m ()
|
|
whenM ~b ~t = ifM b t (return ())
|
|
|
|
-- | Like 'unless', but where the test can be monadic.
|
|
unlessM :: Monad m => m Bool -> m () -> m ()
|
|
unlessM ~b ~f = ifM b (return ()) f
|
|
|
|
-- | Like @if@, but where the test can be monadic.
|
|
ifM :: Monad m => m Bool -> m a -> m a -> m a
|
|
ifM ~b ~t ~f = do
|
|
b' <- b
|
|
if b' then t else f
|
|
|
|
whileM :: Monad m => m a -> (a -> m Bool) -> m a
|
|
whileM ~action ~f = do
|
|
a <- action
|
|
b' <- f a
|
|
if b' then whileM action f else pure a
|
|
|
|
whileM_ :: Monad m => m a -> (a -> m Bool) -> m ()
|
|
whileM_ ~action = void . whileM action
|
|
|
|
guardM :: (Monad m, Alternative m) => m Bool -> m ()
|
|
guardM ~f = guard =<< f
|
|
|
|
lBS2sT :: L.ByteString -> Text
|
|
lBS2sT = TL.toStrict . TLE.decodeUtf8
|
|
|
|
|
|
|
|
handleIO' :: (MonadIO m, MonadCatch m)
|
|
=> IOErrorType
|
|
-> (IOException -> m a)
|
|
-> m a
|
|
-> m a
|
|
handleIO' err handler = handleIO
|
|
(\e -> if err == ioeGetErrorType e then handler e else liftIO $ ioError e)
|
|
|
|
|
|
(??) :: forall e es a m . (Monad m, e :< es) => Maybe a -> e -> Excepts es m a
|
|
(??) m e = maybe (throwE e) pure m
|
|
|
|
|
|
(!?) :: forall e es a m
|
|
. (Monad m, e :< es)
|
|
=> m (Maybe a)
|
|
-> e
|
|
-> Excepts es m a
|
|
(!?) em e = lift em >>= (?? e)
|
|
|
|
|
|
lE :: forall e es a m . (Monad m, e :< es) => Either e a -> Excepts es m a
|
|
lE = liftE . veitherToExcepts . fromEither
|
|
|
|
lE' :: forall e' e es a m
|
|
. (Monad m, e :< es)
|
|
=> (e' -> e)
|
|
-> Either e' a
|
|
-> Excepts es m a
|
|
lE' f = liftE . veitherToExcepts . fromEither . bimap f id
|
|
|
|
lEM :: forall e es a m . (Monad m, e :< es) => m (Either e a) -> Excepts es m a
|
|
lEM em = lift em >>= lE
|
|
|
|
lEM' :: forall e' e es a m
|
|
. (Monad m, e :< es)
|
|
=> (e' -> e)
|
|
-> m (Either e' a)
|
|
-> Excepts es m a
|
|
lEM' f em = lift em >>= lE . bimap f id
|
|
|
|
fromEither :: Either a b -> VEither '[a] b
|
|
fromEither = either (VLeft . V) VRight
|
|
|
|
|
|
liftException :: ( MonadCatch m
|
|
, MonadIO m
|
|
, Monad m
|
|
, e :< es'
|
|
, LiftVariant es es'
|
|
)
|
|
=> IOErrorType
|
|
-> e
|
|
-> Excepts es m a
|
|
-> Excepts es' m a
|
|
liftException errType ex =
|
|
handleIO
|
|
(\e ->
|
|
if errType == ioeGetErrorType e then throwE ex else liftIO $ ioError e
|
|
)
|
|
. liftE
|
|
|
|
|
|
-- TODO: does this work?
|
|
hideExcept :: forall e es es' a m
|
|
. (Monad m, e :< es, LiftVariant (Remove e es) es')
|
|
=> e
|
|
-> a
|
|
-> Excepts es m a
|
|
-> Excepts es' m a
|
|
hideExcept _ a action =
|
|
catchLiftLeft ((\_ -> pure a) :: (e -> Excepts es' m a)) action
|
|
|
|
|
|
throwEither :: (Exception a, MonadThrow m) => Either a b -> m b
|
|
throwEither a = case a of
|
|
Left e -> throwM e
|
|
Right r -> pure r
|
|
|
|
|
|
|
|
deriving instance Lift Versioning
|
|
deriving instance Lift Version
|
|
deriving instance Lift SemVer
|
|
deriving instance Lift Mess
|
|
deriving instance Lift PVP
|
|
deriving instance Lift (NonEmpty Word)
|
|
deriving instance Lift VSep
|
|
deriving instance Lift VUnit
|
|
instance Lift Text
|
|
|
|
qq :: (Text -> Q Exp) -> QuasiQuoter
|
|
qq quoteExp' = QuasiQuoter
|
|
{ quoteExp = (\s -> quoteExp' . T.pack $ s)
|
|
, quotePat = \_ ->
|
|
fail "illegal QuasiQuote (allowed as expression only, used as a pattern)"
|
|
, quoteType = \_ ->
|
|
fail "illegal QuasiQuote (allowed as expression only, used as a type)"
|
|
, quoteDec = \_ -> fail
|
|
"illegal QuasiQuote (allowed as expression only, used as a declaration)"
|
|
}
|
|
|
|
vver :: QuasiQuoter
|
|
vver = qq mkV
|
|
where
|
|
mkV :: Text -> Q Exp
|
|
mkV = either (fail . show) TH.lift . version
|
|
|
|
mver :: QuasiQuoter
|
|
mver = qq mkV
|
|
where
|
|
mkV :: Text -> Q Exp
|
|
mkV = either (fail . show) TH.lift . mess
|
|
|
|
sver :: QuasiQuoter
|
|
sver = qq mkV
|
|
where
|
|
mkV :: Text -> Q Exp
|
|
mkV = either (fail . show) TH.lift . semver
|
|
|
|
vers :: QuasiQuoter
|
|
vers = qq mkV
|
|
where
|
|
mkV :: Text -> Q Exp
|
|
mkV = either (fail . show) TH.lift . versioning
|
|
|
|
pver :: QuasiQuoter
|
|
pver = qq mkV
|
|
where
|
|
mkV :: Text -> Q Exp
|
|
mkV = either (fail . show) TH.lift . pvp
|
|
|
|
|
|
verToBS :: Version -> ByteString
|
|
verToBS = E.encodeUtf8 . prettyVer
|
|
|
|
|
|
|
|
intToText :: Integral a => a -> T.Text
|
|
intToText = TL.toStrict . B.toLazyText . B.decimal
|