diff options
-rw-r--r-- | all.gup | 3 | ||||
-rw-r--r-- | lib/Postdelay/Scan.hs | 43 | ||||
-rw-r--r-- | lib/Postdelay/TimeSpec.hs | 14 | ||||
-rw-r--r-- | lib/Postdelay/Types.hs | 13 | ||||
-rw-r--r-- | lib/Postdelay/Utils.hs | 21 | ||||
-rw-r--r-- | postdelay.cabal | 20 | ||||
-rw-r--r-- | postdelay.nix | 11 | ||||
-rw-r--r--[-rwxr-xr-x] | postdelay.nix.gup | 0 | ||||
-rw-r--r-- | result.gup | 4 | ||||
-rw-r--r-- | src/Main.hs | 4 | ||||
-rw-r--r-- | src/Simple.hs | 24 |
11 files changed, 148 insertions, 9 deletions
@@ -0,0 +1,3 @@ | |||
1 | #!/usr/bin/env zsh | ||
2 | |||
3 | gup -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 | |||
3 | module Postdelay.Scan | ||
4 | ( scan | ||
5 | , Delay(..) | ||
6 | , ParseError(..) | ||
7 | ) where | ||
8 | |||
9 | import Postdelay.Types | ||
10 | import Postdelay.Utils | ||
11 | import Postdelay.TimeSpec | ||
12 | |||
13 | import Control.Monad | ||
14 | import Control.Monad.IO.Class | ||
15 | import Control.Monad.Error.Class | ||
16 | |||
17 | import Text.Parsec.Char | ||
18 | import Text.Parsec.Prim | ||
19 | import Text.Parsec.Combinator | ||
20 | import Text.Parsec.Error (ParseError(..)) | ||
21 | import Text.ParserCombinators.Parsec.Rfc2822 | ||
22 | |||
23 | import Data.CaseInsensitive (CI) | ||
24 | import qualified Data.CaseInsensitive as CI | ||
25 | |||
26 | import Data.Either | ||
27 | import Data.Foldable | ||
28 | import Data.Semigroup | ||
29 | |||
30 | |||
31 | scan :: (MonadIO m, MonadError ParseError m) => String -> m (Maybe Delay) | ||
32 | scan = fmap getOption . extractDelay <=< either throwError return . parse message "" | ||
33 | |||
34 | extractDelay :: forall m. (MonadIO m, MonadError ParseError m) => Message -> m (Option Delay) | ||
35 | extractDelay (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 @@ | |||
1 | module Postdelay.TimeSpec | ||
2 | ( pTimeSpec | ||
3 | ) where | ||
4 | |||
5 | import Control.Monad.IO.Class | ||
6 | import Data.Time | ||
7 | |||
8 | import Text.Parsec.Char | ||
9 | import Text.Parsec.Prim | ||
10 | import Text.Parsec.Combinator | ||
11 | import Text.Parsec.Error (ParseError(..)) | ||
12 | |||
13 | pTimeSpec :: MonadIO m => ParsecT String () m UTCTime | ||
14 | pTimeSpec = 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 @@ | |||
1 | module Postdelay.Types | ||
2 | ( Delay(..) | ||
3 | ) where | ||
4 | |||
5 | import Data.Semigroup | ||
6 | |||
7 | import Data.Time.Clock (UTCTime) | ||
8 | |||
9 | newtype Delay = Until { releaseTime :: UTCTime } | ||
10 | deriving (Eq, Ord) | ||
11 | |||
12 | instance 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 | |||
3 | module Postdelay.Utils | ||
4 | ( hoistParsecT | ||
5 | ) where | ||
6 | |||
7 | import Control.Monad.Error.Class | ||
8 | |||
9 | import Data.Functor | ||
10 | import Data.Either | ||
11 | |||
12 | import Data.Functor.Identity | ||
13 | import Text.Parsec.Prim | ||
14 | import Text.Parsec.Error | ||
15 | |||
16 | hoistParsecT :: (Monad m, Stream s Identity t, Stream s m t) => ParsecT s u Identity a -> ParsecT s u m a | ||
17 | hoistParsecT 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: |
17 | cabal-version: >=1.10 | 17 | cabal-version: >=1.10 |
18 | 18 | ||
19 | executable postdelay | 19 | library |
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 | |||
33 | executable 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 | }: | ||
2 | mkDerivation { | 4 | mkDerivation { |
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 | |||
3 | gup -u postdelay.nix default.nix | ||
4 | nix-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 @@ | |||
1 | module Main where | ||
2 | |||
3 | main :: IO () | ||
4 | main = 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 @@ | |||
1 | module Main (main) where | ||
2 | |||
3 | import Postdelay.Scan | ||
4 | |||
5 | import Control.Monad.IO.Class | ||
6 | import Control.Monad.Trans.Except | ||
7 | |||
8 | |||
9 | main :: MonadIO m => m () | ||
10 | main = 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 | |||
20 | sendNow :: MonadIO m => String -> m () | ||
21 | sendNow = undefined | ||
22 | |||
23 | sendLater :: MonadIO m => String -> Delay -> m () | ||
24 | sendLater = undefined | ||