grpc-spec-1.0.0: src/Network/GRPC/Spec/Timeout.hs
module Network.GRPC.Spec.Timeout (
-- * Timeouts
Timeout(..)
, TimeoutValue(TimeoutValue, getTimeoutValue)
, TimeoutUnit(..)
, isValidTimeoutValue
-- * Translation
, timeoutToMicro
) where
import GHC.Generics (Generic)
import GHC.Show
{-------------------------------------------------------------------------------
Timeouts
-------------------------------------------------------------------------------}
-- | Timeout
data Timeout = Timeout TimeoutUnit TimeoutValue
deriving stock (Show, Eq, Generic)
-- | Positive integer with ASCII representation of at most 8 digits
newtype TimeoutValue = UnsafeTimeoutValue {
getTimeoutValue :: Word
}
deriving newtype (Eq)
deriving stock (Generic)
-- | 'Show' instance relies on the v'TimeoutValue' pattern synonym
instance Show TimeoutValue where
showsPrec p (UnsafeTimeoutValue val) = showParen (p >= appPrec1) $
showString "TimeoutValue "
. showsPrec appPrec1 val
pattern TimeoutValue :: Word -> TimeoutValue
pattern TimeoutValue t <- UnsafeTimeoutValue t
where
TimeoutValue t
| isValidTimeoutValue t = UnsafeTimeoutValue t
| otherwise = error $ "invalid TimeoutValue: " ++ show t
{-# COMPLETE TimeoutValue #-}
-- | Valid timeout values
--
-- Timeout values cannot exceed 8 digits. If you need a longer timeout, consider
-- using a different 'TimeoutUnit' instead.
isValidTimeoutValue :: Word -> Bool
isValidTimeoutValue t = length (show t) <= 8
-- | Timeout unit
data TimeoutUnit =
Hour -- ^ Hours
| Minute -- ^ Minutes
| Second -- ^ Seconds
| Millisecond -- ^ Milliseconds
| Microsecond -- ^ Microseconds
| Nanosecond -- ^ Nanoseconds
--
-- Although some servers may be able to interpret this in a
-- meaningful way, /we/ cannot, and round this up to the nearest
-- microsecond.
deriving stock (Show, Eq, Generic)
{-------------------------------------------------------------------------------
Translation
-------------------------------------------------------------------------------}
-- | Translate t'Timeout' to microseconds
--
-- For 'Nanosecond' timeout we round up.
--
-- Note: the choice of 'Integer' for the result is important: timeouts can be
-- quite long, and might easily exceed the range of a 32-bit int: @2^31@
-- microseconds is roughly 35 minutes (on 64-bit architectures this is much less
-- important; @2^63@ microseconds is 292,277.2 /years/). We could use @Int64@ or
-- @Word64@, but 'Integer' works nicely with the @unbounded-delays@ package.
timeoutToMicro :: Timeout -> Integer
timeoutToMicro = \case
Timeout Hour (TimeoutValue n) -> mult n $ 1 * 1_000 * 1_000 * 60 * 24
Timeout Minute (TimeoutValue n) -> mult n $ 1 * 1_000 * 1_000 * 60
Timeout Second (TimeoutValue n) -> mult n $ 1 * 1_000 * 1_000
Timeout Millisecond (TimeoutValue n) -> mult n $ 1 * 1_000
Timeout Microsecond (TimeoutValue n) -> mult n $ 1
Timeout Nanosecond (TimeoutValue n) -> nano n
where
mult :: Word -> Integer -> Integer
mult n m = fromIntegral n * m
nano :: Word -> Integer
nano n = fromIntegral $
mu + if n' == 0 then 0 else 1
where
(mu, n') = divMod n 1_000