summaryrefslogtreecommitdiff
path: root/lib/Postdelay/Scan.hs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Postdelay/Scan.hs')
-rw-r--r--lib/Postdelay/Scan.hs37
1 files changed, 31 insertions, 6 deletions
diff --git a/lib/Postdelay/Scan.hs b/lib/Postdelay/Scan.hs
index e6de0cf..fba9f35 100644
--- a/lib/Postdelay/Scan.hs
+++ b/lib/Postdelay/Scan.hs
@@ -1,4 +1,4 @@
1{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, FlexibleContexts #-} 1{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, FlexibleContexts, RecordWildCards, ViewPatterns #-}
2 2
3module Postdelay.Scan 3module Postdelay.Scan
4 ( scan 4 ( scan
@@ -12,13 +12,17 @@ import Postdelay.TimeSpec
12 12
13import Control.Monad 13import Control.Monad
14import Control.Monad.IO.Class 14import Control.Monad.IO.Class
15import Control.Monad.Error.Class 15import Control.Monad.Except
16import Control.Monad.Reader
17import Control.Monad.List
18import Control.Exception.Base
16 19
17import Text.Parsec.Char 20import Text.Parsec.Char
18import Text.Parsec.Prim 21import Text.Parsec.Prim
19import Text.Parsec.Combinator 22import Text.Parsec.Combinator
20import Text.Parsec.Error (ParseError(..)) 23import Text.Parsec.Error (ParseError(..))
21import Text.ParserCombinators.Parsec.Rfc2822 24import Text.ParserCombinators.Parsec.Rfc2822
25import Codec.MIME.Decode (decodeWords)
22 26
23import Data.CaseInsensitive (CI) 27import Data.CaseInsensitive (CI)
24import qualified Data.CaseInsensitive as CI 28import qualified Data.CaseInsensitive as CI
@@ -26,18 +30,39 @@ import qualified Data.CaseInsensitive as CI
26import Data.Either 30import Data.Either
27import Data.Foldable 31import Data.Foldable
28import Data.Semigroup 32import Data.Semigroup
33import Data.Time
34import Data.List
35import Data.Ord
36import Data.Ratio
37
38import Data.Time.Zones
39import System.Time (CalendarTime(..))
40
41import Debug.Trace
29 42
30 43
31scan :: (MonadIO m, MonadError ParseError m) => String -> m (Maybe Delay) 44scan :: (MonadIO m, MonadError ParseError m) => String -> m (Maybe Delay)
32scan = fmap getOption . extractDelay <=< either throwError return . parse message "" 45scan = fmap getOption . extractDelay <=< either throwError return . parse message ""
33 46
34extractDelay :: forall m. (MonadIO m, MonadError ParseError m) => Message -> m (Option Delay) 47extractDelay :: forall m. (MonadIO m, MonadError ParseError m) => Message -> m (Option Delay)
35extractDelay (Message headers _) = foldMap pure <$> mapM parseDelay delayHeaders 48extractDelay (Message headers _)
49 = let latestCtx = maximumBy (comparing baseTime) dateHeaders
50 in foldMap pure <$> mapM (flip runReaderT latestCtx . parseDelay) delayHeaders
36 where 51 where
37 delayHeaders :: [Field] 52 delayHeaders :: [Field]
38 delayHeaders = do 53 delayHeaders = do
39 h@(OptionalField field content) <- headers 54 (OptionalField field content) <- headers
40 guard $ CI.mk field == "X-Delay" 55 guard $ CI.mk field == "X-Delay"
41 return h 56 return . OptionalField field $ decodeWords content
42 parseDelay :: Field -> m Delay 57 dateHeaders :: [TimeCtx]
58 dateHeaders = do
59 (Date CalendarTime{..}) <- headers
60 let tz = minutesToTimeZone . round $ ctTZ % 60
61 return $ TimeCtx
62 { baseTime = localTimeToUTC tz $ LocalTime
63 (fromGregorian (fromIntegral ctYear) (fromEnum ctMonth + 1) ctDay)
64 (TimeOfDay ctHour ctMin $ fromIntegral ctSec + fromIntegral ctPicosec * 1e-12)
65 , tz = Left tz
66 }
67 parseDelay :: Field -> ReaderT TimeCtx m Delay
43 parseDelay (OptionalField field content) = either throwError return =<< runParserT (Until <$> pTimeSpec) () field content 68 parseDelay (OptionalField field content) = either throwError return =<< runParserT (Until <$> pTimeSpec) () field content