{-# LANGUAGE DeriveAnyClass #-}
-- This module pins down the details of week days in the API. It's boilerplate-y
-- but that makes it robust.
-- | A day of week type. Integer representations are not used in the API, but
-- are provided for convenience and will not change.
module Hercules.API.DayOfWeek
( DayOfWeek (..),
-- * Conversions for @time@ package
toTime,
fromTime,
-- * Optional, stable, opinionated integer conversions
toNum,
fromNumMaybe,
fromNum,
)
where
import Control.DeepSeq (NFData)
import Data.Aeson (FromJSON, ToJSON)
import Data.Maybe (fromJust)
import Data.Swagger (ToSchema)
import qualified Data.Time
import GHC.Generics (Generic)
import Prelude (Eq, Integral, Maybe (..), Num (..), Show, mod)
-- | Day of week representation used in the API.
data DayOfWeek = Mon | Tue | Wed | Thu | Fri | Sat | Sun
deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
-- | Conversion to @time@ package representation.
toTime :: DayOfWeek -> Data.Time.DayOfWeek
toTime Mon = Data.Time.Monday
toTime Tue = Data.Time.Tuesday
toTime Wed = Data.Time.Wednesday
toTime Thu = Data.Time.Thursday
toTime Fri = Data.Time.Friday
toTime Sat = Data.Time.Saturday
toTime Sun = Data.Time.Sunday
-- | Conversion from @time@ package representation.
fromTime :: Data.Time.DayOfWeek -> DayOfWeek
fromTime Data.Time.Monday = Mon
fromTime Data.Time.Tuesday = Tue
fromTime Data.Time.Wednesday = Wed
fromTime Data.Time.Thursday = Thu
fromTime Data.Time.Friday = Fri
fromTime Data.Time.Saturday = Sat
fromTime Data.Time.Sunday = Sun
-- | Conversion to integers where 'Mon' -> 1, 'Sun' -> 7
toNum :: Num n => DayOfWeek -> n
toNum Mon = 1
toNum Tue = 2
toNum Wed = 3
toNum Thu = 4
toNum Fri = 5
toNum Sat = 6
toNum Sun = 7
-- | Conversion from integers where @1 -> Just 'Mon'@, @7 -> Just 'Sun'@, @outside of 1..7 -> Nothing@
fromNumMaybe :: (Num n, Eq n) => n -> Maybe DayOfWeek
fromNumMaybe 1 = Just Mon
fromNumMaybe 2 = Just Tue
fromNumMaybe 3 = Just Wed
fromNumMaybe 4 = Just Thu
fromNumMaybe 5 = Just Fri
fromNumMaybe 6 = Just Sat
fromNumMaybe 7 = Just Sun
fromNumMaybe _ = Nothing
-- | Conversion from integers where @{...,-7,0,7,...} -> Just 'Sun'@, @{...,-6,1,8,...} -> Just 'Mon'@, etc.
--
-- Requires a sensible implementation of the 'Integral' 'mod' method that returns non-negative numbers.
-- 'Integer', 'Int', 'Int8', etc are ok. So is the 'Word' family of types, though beware of 'minBound' overflows.
fromNum :: (Integral n, Eq n) => n -> DayOfWeek
fromNum n = fromJust (fromNumMaybe (1 + (n - 1) `mod` 7))