servant-util-0.1.0: src/Servant/Util/Internal/Util.hs
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Servant.Util.Internal.Util
( Positive (..)
, toPositive
, unsafeToPositive
, IsNotZero
, KnownPositive
, positiveVal
) where
import Universum
import Fmt (Buildable (..))
import GHC.TypeLits (ErrorMessage (..), TypeError)
import Servant (FromHttpApiData (..), ToHttpApiData (..))
newtype Positive a = PositiveUnsafe { unPositive :: a }
deriving (Show, Eq, Ord)
instance Buildable x => Buildable (Positive x) where
build = build . unPositive
toPositive :: (Show a, Ord a, Num a) => a -> Either Text (Positive a)
toPositive a
| a > 0 = Right $ PositiveUnsafe a
| otherwise = Left $ "Non-positive value: " <> show a
unsafeToPositive :: (Show a, Ord a, Num a, HasCallStack) => a -> Positive a
unsafeToPositive = either error id . toPositive
type family IsNotZero (k :: Nat) :: Constraint where
IsNotZero 0 = TypeError ('Text "Null is now allowed here")
IsNotZero k = ()
type KnownPositive k = (KnownNat k, IsNotZero k)
positiveVal :: forall k i. (KnownPositive k, Num i) => Positive i
positiveVal = PositiveUnsafe . fromIntegral $ natVal @k Proxy
instance (FromHttpApiData a, Show a, Ord a, Num a) => FromHttpApiData (Positive a) where
parseUrlPiece t = parseUrlPiece @a t >>= toPositive
instance ToHttpApiData a => ToHttpApiData (Positive a) where
toUrlPiece = toUrlPiece @a . unPositive