summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--all.gup3
-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
-rw-r--r--postdelay.cabal20
-rw-r--r--postdelay.nix11
-rw-r--r--[-rwxr-xr-x]postdelay.nix.gup0
-rw-r--r--result.gup4
-rw-r--r--src/Main.hs4
-rw-r--r--src/Simple.hs24
11 files changed, 148 insertions, 9 deletions
diff --git a/all.gup b/all.gup
new file mode 100644
index 0000000..c83d29c
--- /dev/null
+++ b/all.gup
@@ -0,0 +1,3 @@
1#!/usr/bin/env zsh
2
3gup -u result \ No newline at end of file
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
diff --git a/postdelay.cabal b/postdelay.cabal
index 9f13336..957e6d5 100644
--- a/postdelay.cabal
+++ b/postdelay.cabal
@@ -16,10 +16,26 @@ build-type: Simple
16-- extra-source-files: 16-- extra-source-files:
17cabal-version: >=1.10 17cabal-version: >=1.10
18 18
19executable postdelay 19library
20 main-is: Main.hs 20 exposed-modules: Postdelay.Scan
21 , Postdelay.Types
22 other-modules: Postdelay.Utils
23 , Postdelay.TimeSpec
24 build-depends: base >=4.9 && <5
25 , time >=1.6 && <2
26 , parsec >=3.1 && <4
27 , case-insensitive >=1.2 && <2
28 , hsemail >=1.7 && <2
29 , mtl >=2.2 && <3
30 hs-source-dirs: lib
31 default-language: Haskell2010
32
33executable postdelay-simple
34 main-is: Simple.hs
21 -- other-modules: 35 -- other-modules:
22 -- other-extensions: 36 -- other-extensions:
23 build-depends: base >=4.9 && <5 37 build-depends: base >=4.9 && <5
38 , postdelay
39 , transformers >=0.5 && <1
24 hs-source-dirs: src 40 hs-source-dirs: src
25 default-language: Haskell2010 41 default-language: Haskell2010
diff --git a/postdelay.nix b/postdelay.nix
index 1ef3413..66dae61 100644
--- a/postdelay.nix
+++ b/postdelay.nix
@@ -1,11 +1,16 @@
1{ mkDerivation, base, stdenv }: 1{ mkDerivation, base, case-insensitive, hsemail, mtl, parsec
2, stdenv, time, transformers
3}:
2mkDerivation { 4mkDerivation {
3 pname = "postdelay"; 5 pname = "postdelay";
4 version = "0.0.0"; 6 version = "0.0.0";
5 src = ./.; 7 src = ./.;
6 isLibrary = false; 8 isLibrary = true;
7 isExecutable = true; 9 isExecutable = true;
8 executableHaskellDepends = [ base ]; 10 libraryHaskellDepends = [
11 base case-insensitive hsemail mtl parsec time
12 ];
13 executableHaskellDepends = [ base transformers ];
9 homepage = "https://git.yggdrasil.li/gkleen/pub/postdelay"; 14 homepage = "https://git.yggdrasil.li/gkleen/pub/postdelay";
10 description = "A postfix content filter for delaying delivery of mail"; 15 description = "A postfix content filter for delaying delivery of mail";
11 license = stdenv.lib.licenses.mit; 16 license = stdenv.lib.licenses.mit;
diff --git a/postdelay.nix.gup b/postdelay.nix.gup
index 976221f..976221f 100755..100644
--- a/postdelay.nix.gup
+++ b/postdelay.nix.gup
diff --git a/result.gup b/result.gup
new file mode 100644
index 0000000..5c03e49
--- /dev/null
+++ b/result.gup
@@ -0,0 +1,4 @@
1#!/usr/bin/env zsh
2
3gup -u postdelay.nix default.nix
4nix-build -o ${1} ./default.nix \ No newline at end of file
diff --git a/src/Main.hs b/src/Main.hs
deleted file mode 100644
index 65ae4a0..0000000
--- a/src/Main.hs
+++ /dev/null
@@ -1,4 +0,0 @@
1module Main where
2
3main :: IO ()
4main = putStrLn "Hello, Haskell!"
diff --git a/src/Simple.hs b/src/Simple.hs
new file mode 100644
index 0000000..f461766
--- /dev/null
+++ b/src/Simple.hs
@@ -0,0 +1,24 @@
1module Main (main) where
2
3import Postdelay.Scan
4
5import Control.Monad.IO.Class
6import Control.Monad.Trans.Except
7
8
9main :: MonadIO m => m ()
10main = do
11 mailStr <- liftIO getContents
12 delay <- runExceptT $ scan mailStr
13 case delay of
14 Left err -> do
15 liftIO . putStrLn $ show err
16 sendNow mailStr
17 Right Nothing -> sendNow mailStr
18 Right (Just d) -> sendLater mailStr d
19
20sendNow :: MonadIO m => String -> m ()
21sendNow = undefined
22
23sendLater :: MonadIO m => String -> Delay -> m ()
24sendLater = undefined