-- SPDX-FileCopyrightText: 2021 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA

{-# LANGUAGE NoImplicitPrelude #-}

-- | Unsafe utilities.
--
-- This module should be imported qualified.
module Unsafe
  ( module Universum.Unsafe

  -- * Unsafe converters between @Integral@ types checking for overflow/underflow
  , Unsafe.fromIntegral
  , Unsafe.fromInteger

  -- * Unsafe converters from @Either@ for making unsafe counter-parts of safe functions
  , unsafe
  , unsafeM
  ) where

import Fmt (Buildable, pretty)
import Morley.Prelude.FromIntegral (fromIntegralNoOverflow)
import Universum
import Universum.Unsafe

-- | Unsafe converter between 'Integral' types
-- checking for overflow/underflow. Return
-- @value@ if conversion does not produce
-- overflow/underflow and raise an exception
-- with corresponding error message otherwise.
--
-- It is needed to replace 'Universum.Base.fromIntegral'
-- which misses most of the overflow/underflow checks.
--
-- Note the function is strict in its argument.
fromIntegral :: (HasCallStack, Integral a, Integral b) => a -> b
fromIntegral :: forall a b. (HasCallStack, Integral a, Integral b) => a -> b
fromIntegral = (ArithException -> b) -> (b -> b) -> Either ArithException b -> b
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Text -> b
forall a. HasCallStack => Text -> a
error (Text -> b) -> (ArithException -> Text) -> ArithException -> b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
forall a. IsString a => String -> a
fromString (String -> Text)
-> (ArithException -> String) -> ArithException -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ArithException -> String
forall e. Exception e => e -> String
displayException) b -> b
forall a. a -> a
id (Either ArithException b -> b)
-> (a -> Either ArithException b) -> a -> b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Either ArithException b
forall a b.
(Integral a, Integral b) =>
a -> Either ArithException b
fromIntegralNoOverflow

-- | Unsafe converter between 'Integer' and 'Integral'
-- types checking for overflow/underflow. Return @value@
-- if conversion does not produce overflow/underflow and
-- raise an exception with corresponding error message
-- otherwise.
--
-- Note the function is strict in its argument.
fromInteger :: (HasCallStack, Integral a) => Integer -> a
fromInteger :: forall a. (HasCallStack, Integral a) => Integer -> a
fromInteger = Integer -> a
forall a b. (HasCallStack, Integral a, Integral b) => a -> b
Unsafe.fromIntegral

-- | Unsafe converter from 'Either', which uses buildable
-- 'Left' to throw an exception with 'error'.
--
-- It is primarily needed for making unsafe counter-parts
-- of safe functions. In particular, for replacing
-- @unsafeFName x = either (error . pretty) id@
-- constructors and converters, which produce many similar
-- functions at the call site, with @unsafe . fName $ x@.
unsafe :: (HasCallStack, Buildable a) => Either a b -> b
unsafe :: forall a b. (HasCallStack, Buildable a) => Either a b -> b
unsafe = (a -> b) -> (b -> b) -> Either a b -> b
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Text -> b
forall a. HasCallStack => Text -> a
error (Text -> b) -> (a -> Text) -> a -> b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Text
forall a b. (Buildable a, FromDoc b) => a -> b
pretty) b -> b
forall a. a -> a
id
{-# INLINE unsafe #-}

-- | Similar to 'unsafe' converter, but with the use of
-- monadic 'fail' and returning the result wrapped in a monad.
unsafeM :: (MonadFail m, Buildable a) => Either a b -> m b
unsafeM :: forall (m :: * -> *) a b.
(MonadFail m, Buildable a) =>
Either a b -> m b
unsafeM = (a -> m b) -> (b -> m b) -> Either a b -> m b
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (String -> m b
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> m b) -> (a -> String) -> a -> m b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> String
forall a b. (Buildable a, FromDoc b) => a -> b
pretty) b -> m b
forall (f :: * -> *) a. Applicative f => a -> f a
pure
{-# INLINE unsafeM #-}