{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeOperators #-} -------------------------------------------------------------------------------- module Main where -------------------------------------------------------------------------------- import RIO hiding (Handler) import RIO.Text import RIO.Time import Servant import Data.Time.Clock.POSIX import Prelude (read) import Text.ParserCombinators.ReadP import qualified Network.Wai.Handler.Warp as Warp -------------------------------------------------------------------------------- type Api = "run" :> QueryParam' '[Required] "offset" Text :> Get '[JSON] UTCTime :<|> "hello" :> QueryParam "name" Text :> Get '[JSON] Text server :: Server Api server = compute :<|> hello where compute :: Text -> Handler UTCTime compute x = do case parseInput x of Nothing -> throwError err401 Just req -> do res <- liftIO $ shiftTime req pure res hello :: Maybe Text -> Handler Text hello mName = case mName of Nothing -> pure "Hello, world!" Just name -> pure $ RIO.Text.concat ["Hello, ", name] data ShiftTimeRequest = ShiftTimeRequest { shiftSeconds :: Int , shiftMinutes :: Int , shiftHours :: Int , shiftDays :: Int , shiftWeeks :: Int , shiftMonths :: Int , shiftQuarters :: Int , shiftYears :: Int } deriving (Eq, Show) instance Semigroup ShiftTimeRequest where (ShiftTimeRequest as am ah ad aw amonths aq ay) <> (ShiftTimeRequest bs bm bh bd bw bmonths bq by) = ShiftTimeRequest { shiftSeconds = as + bs , shiftMinutes = am + bm , shiftHours = ah + bh , shiftDays = ad + bd , shiftWeeks = aw + bw , shiftMonths = amonths + bmonths , shiftQuarters = aq + bq , shiftYears = ay + by } instance Monoid ShiftTimeRequest where mempty = defaultShiftTimeRequest defaultShiftTimeRequest :: ShiftTimeRequest defaultShiftTimeRequest = ShiftTimeRequest { shiftSeconds = 0 , shiftMinutes = 0 , shiftHours = 0 , shiftDays = 0 , shiftWeeks = 0 , shiftMonths = 0 , shiftQuarters = 0 , shiftYears = 0 } -- This basically broken because it doesn't account for: -- Exhales... time stuff -- - Leap seconds, leap days, leap years... -- - Months like February having 28 days and others having 31 -- - other things that I'm probably not considering toSeconds :: ShiftTimeRequest -> NominalDiffTime toSeconds ShiftTimeRequest{..} = do let minutes = 60 hours = minutes * 60 days = hours * 24 weeks = days * 7 months = weeks * 4 quarters = months * 3 years = days * 365 fromIntegral $ shiftSeconds + shiftMinutes * minutes + shiftHours * hours + shiftDays * days + shiftWeeks * weeks + shiftMonths * months + shiftQuarters * quarters + shiftYears * years shiftTime :: ShiftTimeRequest -> IO UTCTime shiftTime req = do t <- getPOSIXTime let t' = t + toSeconds req pure $ posixSecondsToUTCTime t' data Unit = Second | Minute | Hour | Day | Week | Month | Quarter | Year deriving (Eq, Show) digit :: ReadP Char digit = satisfy (\c -> c >= '0' && c <= '9') unit :: ReadP Unit unit = do c <- get case c of 's' -> pure Second 'm' -> pure Minute 'h' -> pure Hour 'd' -> pure Day 'w' -> pure Week 'M' -> pure Month 'q' -> pure Quarter 'y' -> pure Year _ -> fail $ "We don't support this unit: " ++ show c request :: ReadP ShiftTimeRequest request = do negative <- option Nothing $ fmap Just (satisfy (== '-')) n <- read <$> many1 digit u <- unit let amt = if isJust negative then -1 * n else n case u of Second -> pure $ defaultShiftTimeRequest { shiftSeconds = amt } Minute -> pure $ defaultShiftTimeRequest { shiftMinutes = amt } Hour -> pure $ defaultShiftTimeRequest { shiftHours = amt } Day -> pure $ defaultShiftTimeRequest { shiftDays = amt } Week -> pure $ defaultShiftTimeRequest { shiftWeeks = amt } Month -> pure $ defaultShiftTimeRequest { shiftMonths = amt } Quarter -> pure $ defaultShiftTimeRequest { shiftQuarters = amt } Year -> pure $ defaultShiftTimeRequest { shiftYears = amt } parseInput :: Text -> Maybe ShiftTimeRequest parseInput x = case readP_to_S (manyTill request eof) (unpack x) of [(xs, "")] -> Just $ mconcat xs _ -> Nothing main :: IO () main = Warp.run 8000 $ serve (Proxy @Api) server