summaryrefslogtreecommitdiff
path: root/lib/Postdelay
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Postdelay')
-rw-r--r--lib/Postdelay/Scan.hs43
-rw-r--r--lib/Postdelay/TimeSpec.hs14
-rw-r--r--lib/Postdelay/Types.hs13
-rw-r--r--lib/Postdelay/Utils.hs21
4 files changed, 91 insertions, 0 deletions
diff --git a/lib/Postdelay/Scan.hs b/lib/Postdelay/Scan.hs
new file mode 100644
index 0000000..e6de0cf
--- /dev/null
+++ b/lib/Postdelay/Scan.hs
@@ -0,0 +1,43 @@
1{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, FlexibleContexts #-}
2
3module Postdelay.Scan
4 ( scan
5 , Delay(..)
6 , ParseError(..)
7 ) where
8
9import Postdelay.Types
10import Postdelay.Utils
11import Postdelay.TimeSpec
12
13import Control.Monad
14import Control.Monad.IO.Class
15import Control.Monad.Error.Class
16
17import Text.Parsec.Char
18import Text.Parsec.Prim
19import Text.Parsec.Combinator
20import Text.Parsec.Error (ParseError(..))
21import Text.ParserCombinators.Parsec.Rfc2822
22
23import Data.CaseInsensitive (CI)
24import qualified Data.CaseInsensitive as CI
25
26import Data.Either
27import Data.Foldable
28import Data.Semigroup
29
30
31scan :: (MonadIO m, MonadError ParseError m) => String -> m (Maybe Delay)
32scan = fmap getOption . extractDelay <=< either throwError return . parse message ""
33
34extractDelay :: forall m. (MonadIO m, MonadError ParseError m) => Message -> m (Option Delay)
35extractDelay (Message headers _) = foldMap pure <$> mapM parseDelay delayHeaders
36 where
37 delayHeaders :: [Field]
38 delayHeaders = do
39 h@(OptionalField field content) <- headers
40 guard $ CI.mk field == "X-Delay"
41 return h
42 parseDelay :: Field -> m Delay
43 parseDelay (OptionalField field content) = either throwError return =<< runParserT (Until <$> pTimeSpec) () field content
diff --git a/lib/Postdelay/TimeSpec.hs b/lib/Postdelay/TimeSpec.hs
new file mode 100644
index 0000000..b080dcc
--- /dev/null
+++ b/lib/Postdelay/TimeSpec.hs
@@ -0,0 +1,14 @@
1module Postdelay.TimeSpec
2 ( pTimeSpec
3 ) where
4
5import Control.Monad.IO.Class
6import Data.Time
7
8import Text.Parsec.Char
9import Text.Parsec.Prim
10import Text.Parsec.Combinator
11import Text.Parsec.Error (ParseError(..))
12
13pTimeSpec :: MonadIO m => ParsecT String () m UTCTime
14pTimeSpec = undefined
diff --git a/lib/Postdelay/Types.hs b/lib/Postdelay/Types.hs
new file mode 100644
index 0000000..3f66fb8
--- /dev/null
+++ b/lib/Postdelay/Types.hs
@@ -0,0 +1,13 @@
1module Postdelay.Types
2 ( Delay(..)
3 ) where
4
5import Data.Semigroup
6
7import Data.Time.Clock (UTCTime)
8
9newtype Delay = Until { releaseTime :: UTCTime }
10 deriving (Eq, Ord)
11
12instance Semigroup Delay where
13 (Until a) <> (Until b) = Until $ max a b
diff --git a/lib/Postdelay/Utils.hs b/lib/Postdelay/Utils.hs
new file mode 100644
index 0000000..d716b4d
--- /dev/null
+++ b/lib/Postdelay/Utils.hs
@@ -0,0 +1,21 @@
1{-# LANGUAGE FlexibleContexts #-}
2
3module Postdelay.Utils
4 ( hoistParsecT
5 ) where
6
7import Control.Monad.Error.Class
8
9import Data.Functor
10import Data.Either
11
12import Data.Functor.Identity
13import Text.Parsec.Prim
14import Text.Parsec.Error
15
16hoistParsecT :: (Monad m, Stream s Identity t, Stream s m t) => ParsecT s u Identity a -> ParsecT s u m a
17hoistParsecT p = do
18 st <- getParserState
19 let res = runParser p' undefined "" undefined
20 p' = setParserState st >> ((,) <$> getState <*> p)
21 either (fail . show) (\(st', res) -> putState st' $> res) $ res