diff options
-rw-r--r-- | default.nix | 6 | ||||
-rw-r--r-- | lib/Postdelay/Scan.hs | 37 | ||||
-rw-r--r-- | lib/Postdelay/TimeSpec.hs | 326 | ||||
-rw-r--r-- | lib/Postdelay/Types.hs | 2 | ||||
-rw-r--r-- | postdelay.cabal | 4 | ||||
-rw-r--r-- | postdelay.nix | 7 | ||||
-rw-r--r-- | postdelay.nix.gup | 2 | ||||
-rw-r--r-- | result.gup | 6 | ||||
-rw-r--r-- | shell.nix | 25 |
9 files changed, 385 insertions, 30 deletions
diff --git a/default.nix b/default.nix deleted file mode 100644 index 0ba6150..0000000 --- a/default.nix +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | argumentPackages@{ ... }: | ||
2 | |||
3 | let | ||
4 | defaultPackages = with (import <nixpkgs> {}); haskellPackages; | ||
5 | pkgs = defaultPackages // argumentPackages; | ||
6 | in pkgs.callPackage ./postdelay.nix {} | ||
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 | ||
3 | module Postdelay.Scan | 3 | module Postdelay.Scan |
4 | ( scan | 4 | ( scan |
@@ -12,13 +12,17 @@ import Postdelay.TimeSpec | |||
12 | 12 | ||
13 | import Control.Monad | 13 | import Control.Monad |
14 | import Control.Monad.IO.Class | 14 | import Control.Monad.IO.Class |
15 | import Control.Monad.Error.Class | 15 | import Control.Monad.Except |
16 | import Control.Monad.Reader | ||
17 | import Control.Monad.List | ||
18 | import Control.Exception.Base | ||
16 | 19 | ||
17 | import Text.Parsec.Char | 20 | import Text.Parsec.Char |
18 | import Text.Parsec.Prim | 21 | import Text.Parsec.Prim |
19 | import Text.Parsec.Combinator | 22 | import Text.Parsec.Combinator |
20 | import Text.Parsec.Error (ParseError(..)) | 23 | import Text.Parsec.Error (ParseError(..)) |
21 | import Text.ParserCombinators.Parsec.Rfc2822 | 24 | import Text.ParserCombinators.Parsec.Rfc2822 |
25 | import Codec.MIME.Decode (decodeWords) | ||
22 | 26 | ||
23 | import Data.CaseInsensitive (CI) | 27 | import Data.CaseInsensitive (CI) |
24 | import qualified Data.CaseInsensitive as CI | 28 | import qualified Data.CaseInsensitive as CI |
@@ -26,18 +30,39 @@ import qualified Data.CaseInsensitive as CI | |||
26 | import Data.Either | 30 | import Data.Either |
27 | import Data.Foldable | 31 | import Data.Foldable |
28 | import Data.Semigroup | 32 | import Data.Semigroup |
33 | import Data.Time | ||
34 | import Data.List | ||
35 | import Data.Ord | ||
36 | import Data.Ratio | ||
37 | |||
38 | import Data.Time.Zones | ||
39 | import System.Time (CalendarTime(..)) | ||
40 | |||
41 | import Debug.Trace | ||
29 | 42 | ||
30 | 43 | ||
31 | scan :: (MonadIO m, MonadError ParseError m) => String -> m (Maybe Delay) | 44 | scan :: (MonadIO m, MonadError ParseError m) => String -> m (Maybe Delay) |
32 | scan = fmap getOption . extractDelay <=< either throwError return . parse message "" | 45 | scan = fmap getOption . extractDelay <=< either throwError return . parse message "" |
33 | 46 | ||
34 | extractDelay :: forall m. (MonadIO m, MonadError ParseError m) => Message -> m (Option Delay) | 47 | extractDelay :: forall m. (MonadIO m, MonadError ParseError m) => Message -> m (Option Delay) |
35 | extractDelay (Message headers _) = foldMap pure <$> mapM parseDelay delayHeaders | 48 | extractDelay (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 |
diff --git a/lib/Postdelay/TimeSpec.hs b/lib/Postdelay/TimeSpec.hs index b080dcc..af8d801 100644 --- a/lib/Postdelay/TimeSpec.hs +++ b/lib/Postdelay/TimeSpec.hs | |||
@@ -1,14 +1,332 @@ | |||
1 | {-# LANGUAGE FlexibleContexts, ScopedTypeVariables, ViewPatterns, ConstraintKinds, TupleSections, RecordWildCards #-} | ||
2 | |||
1 | module Postdelay.TimeSpec | 3 | module Postdelay.TimeSpec |
2 | ( pTimeSpec | 4 | ( pTimeSpec |
5 | , TimeCtx(..) | ||
3 | ) where | 6 | ) where |
4 | 7 | ||
8 | import Control.Monad | ||
5 | import Control.Monad.IO.Class | 9 | import Control.Monad.IO.Class |
6 | import Data.Time | 10 | import Control.Monad.Reader.Class |
11 | import Control.Monad.Error.Class | ||
7 | 12 | ||
8 | import Text.Parsec.Char | 13 | import Text.Parsec.Char hiding (digit) |
14 | import qualified Text.Parsec.Char as Parsec (digit) | ||
9 | import Text.Parsec.Prim | 15 | import Text.Parsec.Prim |
10 | import Text.Parsec.Combinator | 16 | import Text.Parsec.Combinator |
11 | import Text.Parsec.Error (ParseError(..)) | 17 | import Text.Parsec.Error (ParseError(..)) |
18 | import Text.Read (readMaybe) | ||
19 | |||
20 | import Data.CaseInsensitive (CI) | ||
21 | import qualified Data.CaseInsensitive as CI | ||
22 | |||
23 | import Data.Time | ||
24 | import Data.Time.Calendar.WeekDate | ||
25 | import Data.Time.Zones | ||
26 | import Data.Function | ||
27 | import Data.Maybe | ||
28 | import Data.Foldable | ||
29 | import Data.Ord | ||
30 | import Data.List | ||
31 | import Data.Tuple | ||
32 | import Data.Bool | ||
33 | |||
34 | import Control.Exception (IOException) | ||
35 | |||
36 | import Debug.Trace | ||
37 | |||
38 | |||
39 | type MonadTP m = (MonadIO m, MonadReader TimeCtx m) | ||
40 | |||
41 | data TimeCtx = TimeCtx | ||
42 | { baseTime :: UTCTime | ||
43 | , tz :: Either TimeZone TZ | ||
44 | } | ||
45 | |||
46 | |||
47 | spaced :: Stream s m Char => ParsecT s u m a -> ParsecT s u m a | ||
48 | spaced p = spaces *> p <* spaces | ||
49 | |||
50 | string' :: Stream s m Char => String -> ParsecT s u m String | ||
51 | string' = mapM $ satisfy . ((==) `on` CI.mk) | ||
52 | |||
53 | choice' :: (Stream s m t, Foldable f) => f (ParsecT s u m a) -> ParsecT s u m a | ||
54 | choice' (toList -> f) | ||
55 | | [p] <- f = p | ||
56 | | (p:ps) <- f = try p <|> choice' ps | ||
57 | | otherwise = mzero | ||
58 | |||
59 | strChoice' :: Stream s m Char => [String] -> ParsecT s u m String | ||
60 | strChoice' = choice' . map string' . sortBy (comparing $ Down . length) | ||
61 | |||
62 | natural :: (Stream s m Char, Num a) => ParsecT s u m a | ||
63 | natural = foldl' (\init last -> init * 10 + last) 0 <$> many1 digit | ||
64 | |||
65 | decimal :: (Stream s m Char, Num a, Fractional a) => ParsecT s u m a | ||
66 | decimal = do | ||
67 | w <- foldl' (\init last -> init * 10 + last) 0 <$> many1 digit | ||
68 | f <- option 0 $ do | ||
69 | char '.' | ||
70 | foldr' (\head tail -> head + tail / 10) 0 <$> many1 digit | ||
71 | return $ w + f / 10 | ||
72 | |||
73 | digit :: (Stream s m Char, Num a) => ParsecT s u m a | ||
74 | digit = fromIntegral . (\c -> fromEnum c - fromEnum '0') <$> Parsec.digit | ||
75 | |||
76 | ensure :: MonadPlus m => (a -> Bool) -> a -> m a | ||
77 | ensure p x = bool (const mzero) return (p x) $ x | ||
78 | |||
79 | |||
80 | pTimeSpec :: MonadTP m => ParsecT String () m UTCTime | ||
81 | pTimeSpec = choice' [ flip addUTCTime <$> spaced pSpecBase <*> spaced pSpecOffset <?> "Absolute time + offset" | ||
82 | , flip addUTCTime <$> asks baseTime <*> spaced pSpecOffset <?> "Time offset" | ||
83 | , spaced pSpecBase <?> "Absolute time" | ||
84 | ] | ||
85 | <* eof <?> "Time specification" | ||
86 | |||
87 | pSpecBase :: forall m. MonadTP m => ParsecT String () m UTCTime | ||
88 | pSpecBase = choice' | ||
89 | [ utcTime <$> spaced pDate <*> spaced pTime | ||
90 | , flip utcTime <$> spaced pTime <*> spaced pDate | ||
91 | , do | ||
92 | proto@(UTCTime{..}) <- utcTime <$> (utctDay <$> asks baseTime) <*> spaced pTime | ||
93 | now <- asks baseTime | ||
94 | return $ if proto < now | ||
95 | then UTCTime (succ utctDay) utctDayTime | ||
96 | else proto | ||
97 | , utcTime <$> spaced pDate <*> ((dayFractionToTimeOfDay 0, ) <$> asks tz) | ||
98 | , spaced (string' "now") *> asks baseTime | ||
99 | ] <?> "Base specification" | ||
100 | where | ||
101 | utcTime :: Day -> (TimeOfDay, Either TimeZone TZ) -> UTCTime | ||
102 | utcTime d (t, Right tz) = localTimeToUTCTZ tz (LocalTime d t) | ||
103 | utcTime d (t, Left tz) = localTimeToUTC tz (LocalTime d t) | ||
104 | |||
105 | pSpecOffset :: MonadTP m => ParsecT String () m NominalDiffTime | ||
106 | pSpecOffset = (+) <$> pSpecOffset' <*> option 0 (try (many $ space <|> char ',' <|> char ';') >> pSpecOffset) | ||
107 | where | ||
108 | pSpecOffset' = option id (spaced pSign) <*> ((*) <$> spaced pNumber <*> spaced pSpecOffsetConst) <?> "Time offset" | ||
109 | pNumber = fromInteger <$> natural <?> "Offset multiplier" | ||
110 | |||
111 | pSign :: MonadTP m => ParsecT String () m (NominalDiffTime -> NominalDiffTime) | ||
112 | pSign = choice [ id <$ char '+' | ||
113 | , negate <$ char '-' | ||
114 | ] <?> "Offset sign" | ||
115 | |||
116 | pSpecOffsetConst :: MonadTP m => ParsecT String () m NominalDiffTime | ||
117 | pSpecOffsetConst = choice' [ 1e-12 <$ strChoice' [ "ps" | ||
118 | , "picosecond" | ||
119 | , "picoseconds" | ||
120 | ] | ||
121 | , 1e-9 <$ strChoice' [ "ns" | ||
122 | , "nanosecond" | ||
123 | , "nanoseconds" | ||
124 | ] | ||
125 | , 1e-6 <$ strChoice' [ "us", "µs" | ||
126 | , "microsecond" | ||
127 | , "microseconds" | ||
128 | ] | ||
129 | , 1e-3 <$ strChoice' [ "ms" | ||
130 | , "millisecond" | ||
131 | , "milliseconds" | ||
132 | ] | ||
133 | , 1e-2 <$ strChoice' [ "ds" | ||
134 | , "decisecond" | ||
135 | , "deciseconds" | ||
136 | ] | ||
137 | , 1e-1 <$ strChoice' [ "cs" | ||
138 | , "centisecond" | ||
139 | , "centiseconds" | ||
140 | ] | ||
141 | , 1 <$ strChoice' [ "s" | ||
142 | , "second" | ||
143 | , "seconds" | ||
144 | ] | ||
145 | , 60 <$ strChoice' [ "min" | ||
146 | , "minute" | ||
147 | , "minutes" | ||
148 | ] | ||
149 | , 3600 <$ strChoice' [ "h" | ||
150 | , "hour" | ||
151 | , "hours" | ||
152 | ] | ||
153 | , 24 * 3600 <$ strChoice' [ "d" | ||
154 | , "day" | ||
155 | , "days" | ||
156 | ] | ||
157 | , 7 * 24 * 3600 <$ strChoice' [ "week" | ||
158 | , "weeks" | ||
159 | ] | ||
160 | , 30 * 24 * 3600 <$ strChoice' [ "month" | ||
161 | , "months" | ||
162 | ] | ||
163 | , 365 * 24 * 3600 <$ strChoice' [ "year" | ||
164 | , "years" | ||
165 | ] | ||
166 | ] <?> "Offset unit" | ||
167 | |||
168 | pDate :: MonadTP m => ParsecT String () m Day | ||
169 | pDate = choice' [ do | ||
170 | (m, d) <- choice' [ do | ||
171 | m <- spaced pMonthName | ||
172 | d <- spaced natural | ||
173 | optional . spaced $ char ',' | ||
174 | return (m, d) | ||
175 | , fmap swap . (,) <$> spaced natural <*> spaced pMonthName | ||
176 | ] | ||
177 | y <- spaced natural | ||
178 | fromGregorian' y m d | ||
179 | , do | ||
180 | now <- asks baseTime | ||
181 | (m, d) <- choice' [ (,) <$> spaced pMonthName <*> spaced natural | ||
182 | , fmap swap . (,) <$> spaced natural <*> spaced pMonthName | ||
183 | ] | ||
184 | let | ||
185 | (y, _, _) = toGregorian $ utctDay now | ||
186 | proto <- fromGregorian' y m d | ||
187 | if proto < utctDay now | ||
188 | then fromGregorian' (y + 1) m d | ||
189 | else return proto | ||
190 | , do | ||
191 | now <- asks baseTime | ||
192 | optional $ spaces *> pNext <* many1 space | ||
193 | d <- spaced pWeekdayName | ||
194 | let | ||
195 | (y, w, _) = toWeekDate $ utctDay now | ||
196 | proto <- fromWeekDate' y w d | ||
197 | if proto < utctDay now | ||
198 | then maybe (fromWeekDate' (y + 1) 1 d) return $ fromWeekDateValid y (w + 1) d | ||
199 | else return proto | ||
200 | , do | ||
201 | spaced $ string' "today" | ||
202 | utctDay <$> asks baseTime | ||
203 | , do | ||
204 | spaced $ string' "tomorrow" | ||
205 | succ . utctDay <$> asks baseTime | ||
206 | , do | ||
207 | y <- natural | ||
208 | char '-' | ||
209 | m <- natural | ||
210 | char '-' | ||
211 | d <- natural | ||
212 | fromGregorian' y m d | ||
213 | , do | ||
214 | d <- natural | ||
215 | char '.' | ||
216 | m <- natural | ||
217 | char '.' | ||
218 | y <- natural | ||
219 | fromGregorian' y m d | ||
220 | , do | ||
221 | m <- natural | ||
222 | char '/' | ||
223 | d <- natural | ||
224 | char '/' | ||
225 | y <- natural | ||
226 | fromGregorian' y m d | ||
227 | , do | ||
228 | ds <- many1 digit | ||
229 | when (length ds < 5) $ fail "Insufficient digits to interpret as concatenated date" | ||
230 | let | ||
231 | d2 : d1 : m2 : m1 : ys = reverse ds | ||
232 | d = 10 * d1 + d2 | ||
233 | m = 10 * m1 + m2 | ||
234 | y = foldl' (\init last -> init * 10 + last) 0 . map fromIntegral $ reverse ys | ||
235 | fromGregorian' y m d | ||
236 | , do | ||
237 | pNext | ||
238 | many1 space | ||
239 | fmap utctDay . addUTCTime <$> pSpecOffsetConst <*> asks baseTime | ||
240 | ] <?> "Day specification" | ||
241 | where | ||
242 | fromGregorian' y m d = maybe (fail "Invalid gregorian date") return $ fromGregorianValid y m d | ||
243 | fromWeekDate' y w d = maybe (fail "Invalid iso8601 date") return $ fromWeekDateValid y w d | ||
244 | pNext = string' "next" | ||
245 | |||
246 | pMonthName :: MonadTP m => ParsecT String () m Int | ||
247 | pMonthName = choice' (zipWith (<$) [1..] [ strChoice' [ "January", "Jan" ] | ||
248 | , strChoice' [ "Febuary", "Feb" ] | ||
249 | , strChoice' [ "March", "Mar" ] | ||
250 | , strChoice' [ "April", "Apr" ] | ||
251 | , strChoice' [ "May" ] | ||
252 | , strChoice' [ "June", "Jun" ] | ||
253 | , strChoice' [ "July", "Jul" ] | ||
254 | , strChoice' [ "August", "Aug" ] | ||
255 | , strChoice' [ "September", "Sep" ] | ||
256 | , strChoice' [ "October", "Oct" ] | ||
257 | , strChoice' [ "November", "Nov" ] | ||
258 | , strChoice' [ "December", "Dec" ] | ||
259 | ]) <?> "Month name" | ||
260 | |||
261 | pWeekdayName :: MonadTP m => ParsecT String () m Int | ||
262 | pWeekdayName = choice' (zipWith (<$) [1..] [ strChoice' [ "Monday", "Mon" ] | ||
263 | , strChoice' [ "Tuesday", "Tue" ] | ||
264 | , strChoice' [ "Wednesday", "Wed" ] | ||
265 | , strChoice' [ "Thursday", "Thu" ] | ||
266 | , strChoice' [ "Friday", "Fri" ] | ||
267 | , strChoice' [ "Saturday", "Sat" ] | ||
268 | , strChoice' [ "Sunday", "Sun" ] | ||
269 | ]) | ||
270 | |||
271 | pTime :: MonadTP m => ParsecT String () m (TimeOfDay, Either TimeZone TZ) | ||
272 | pTime = choice' [ (,) <$> spaced pTimeBase <*> spaced pTimeZone | ||
273 | , (,) <$> spaced pTimeBase <*> asks tz | ||
274 | ] <?> "Time of day and timezone specification" | ||
275 | |||
276 | data AMPM = AM | PM | ||
277 | deriving (Eq, Ord, Enum) | ||
278 | |||
279 | pTimeBase :: MonadTP m => ParsecT String () m TimeOfDay | ||
280 | pTimeBase = choice' [ do | ||
281 | h <- pHour12 | ||
282 | m <- option 0 $ char ':' >> pMinute | ||
283 | s <- option 0 $ char ':' >> pSecond | ||
284 | amPM <- spaced pAMPM | ||
285 | let h' = h + fromEnum amPM * 12 | ||
286 | return $ TimeOfDay h' m s | ||
287 | , do | ||
288 | h <- pHour | ||
289 | m <- option 0 $ char ':' >> pMinute | ||
290 | s <- option 0 $ char ':' >> pSecond | ||
291 | return $ TimeOfDay h m s | ||
292 | , do | ||
293 | h <- ensure (<= 24) =<< (\d u -> 10 * d + u) <$> digit <*> digit | ||
294 | m <- option 0 $ ensure (< 60) =<< (\d u -> 10 * d + u) <$> digit <*> digit | ||
295 | s <- option 0 $ pSecond | ||
296 | return $ TimeOfDay h m s | ||
297 | , TimeOfDay 0 0 0 <$ string' "midnight" | ||
298 | , TimeOfDay 12 0 0 <$ string' "noon" | ||
299 | , TimeOfDay 16 0 0 <$ string' "teatime" | ||
300 | ] <?> "Time of day specification" | ||
301 | where | ||
302 | pAMPM = choice [ AM <$ string' "AM" | ||
303 | , PM <$ string' "PM" | ||
304 | ] | ||
305 | pHour12 = (`rem` 12) <$> (ensure (<= 12) =<< natural) | ||
306 | |||
307 | pHour = (`rem` 24) <$> (ensure (<= 24) =<< natural) | ||
308 | pMinute = ensure (< 60) =<< natural | ||
309 | pSecond = decimal | ||
12 | 310 | ||
13 | pTimeSpec :: MonadIO m => ParsecT String () m UTCTime | 311 | pTimeZone :: MonadTP m => ParsecT String () m (Either TimeZone TZ) |
14 | pTimeSpec = undefined | 312 | pTimeZone = choice' [ do |
313 | sgn <- choice [ id <$ char '+' | ||
314 | , negate <$ char '-' | ||
315 | ] | ||
316 | hs <- (\d u -> 10 * d + u) <$> digit <*> digit | ||
317 | ms <- option 0 $ (\d u -> 10 * d + u) <$> digit <*> digit | ||
318 | return . Left . minutesToTimeZone $ hs * 60 + ms | ||
319 | , do | ||
320 | n <- many1 $ letter <|> char '/' | ||
321 | tz <- liftIO $ do | ||
322 | let | ||
323 | fbHandler :: IO a -> (IOException -> IO a) | ||
324 | fbHandler fb _ = fb | ||
325 | foldl (\fb a -> a `catchError` fbHandler fb) (return Nothing) | ||
326 | [ Just <$> loadSystemTZ n | ||
327 | , Just <$> loadTZFromDB n | ||
328 | ] | ||
329 | case tz of | ||
330 | Nothing -> fail $ "Could not resolve timezone: " ++ n | ||
331 | (Just tz) -> return $ Right tz | ||
332 | ] | ||
diff --git a/lib/Postdelay/Types.hs b/lib/Postdelay/Types.hs index 3f66fb8..d5f8325 100644 --- a/lib/Postdelay/Types.hs +++ b/lib/Postdelay/Types.hs | |||
@@ -7,7 +7,7 @@ import Data.Semigroup | |||
7 | import Data.Time.Clock (UTCTime) | 7 | import Data.Time.Clock (UTCTime) |
8 | 8 | ||
9 | newtype Delay = Until { releaseTime :: UTCTime } | 9 | newtype Delay = Until { releaseTime :: UTCTime } |
10 | deriving (Eq, Ord) | 10 | deriving (Eq, Ord, Show) |
11 | 11 | ||
12 | instance Semigroup Delay where | 12 | instance Semigroup Delay where |
13 | (Until a) <> (Until b) = Until $ max a b | 13 | (Until a) <> (Until b) = Until $ max a b |
diff --git a/postdelay.cabal b/postdelay.cabal index 957e6d5..6b02951 100644 --- a/postdelay.cabal +++ b/postdelay.cabal | |||
@@ -26,7 +26,11 @@ library | |||
26 | , parsec >=3.1 && <4 | 26 | , parsec >=3.1 && <4 |
27 | , case-insensitive >=1.2 && <2 | 27 | , case-insensitive >=1.2 && <2 |
28 | , hsemail >=1.7 && <2 | 28 | , hsemail >=1.7 && <2 |
29 | , mime >=0.4 && <1 | ||
29 | , mtl >=2.2 && <3 | 30 | , mtl >=2.2 && <3 |
31 | , tz >=0.1 && <1 | ||
32 | , old-time >=1.1 && <2 | ||
33 | , list-t >=1 && <2 | ||
30 | hs-source-dirs: lib | 34 | hs-source-dirs: lib |
31 | default-language: Haskell2010 | 35 | default-language: Haskell2010 |
32 | 36 | ||
diff --git a/postdelay.nix b/postdelay.nix index 66dae61..701af04 100644 --- a/postdelay.nix +++ b/postdelay.nix | |||
@@ -1,5 +1,5 @@ | |||
1 | { mkDerivation, base, case-insensitive, hsemail, mtl, parsec | 1 | { mkDerivation, base, case-insensitive, hsemail, list-t, mime, mtl |
2 | , stdenv, time, transformers | 2 | , old-time, parsec, stdenv, time, transformers, tz |
3 | }: | 3 | }: |
4 | mkDerivation { | 4 | mkDerivation { |
5 | pname = "postdelay"; | 5 | pname = "postdelay"; |
@@ -8,7 +8,8 @@ mkDerivation { | |||
8 | isLibrary = true; | 8 | isLibrary = true; |
9 | isExecutable = true; | 9 | isExecutable = true; |
10 | libraryHaskellDepends = [ | 10 | libraryHaskellDepends = [ |
11 | base case-insensitive hsemail mtl parsec time | 11 | base case-insensitive hsemail list-t mime mtl old-time parsec time |
12 | tz | ||
12 | ]; | 13 | ]; |
13 | executableHaskellDepends = [ base transformers ]; | 14 | executableHaskellDepends = [ base transformers ]; |
14 | homepage = "https://git.yggdrasil.li/gkleen/pub/postdelay"; | 15 | homepage = "https://git.yggdrasil.li/gkleen/pub/postdelay"; |
diff --git a/postdelay.nix.gup b/postdelay.nix.gup index 976221f..bc92dcd 100644 --- a/postdelay.nix.gup +++ b/postdelay.nix.gup | |||
@@ -2,4 +2,4 @@ | |||
2 | 2 | ||
3 | gup -u ${2:r}.cabal | 3 | gup -u ${2:r}.cabal |
4 | cd ${2:h} | 4 | cd ${2:h} |
5 | cabal2nix ./. >! ${1} \ No newline at end of file | 5 | cabal2nix ./. >! ${1} |
@@ -1,4 +1,8 @@ | |||
1 | #!/usr/bin/env zsh | 1 | #!/usr/bin/env zsh |
2 | 2 | ||
3 | gup --always | ||
3 | gup -u postdelay.nix default.nix | 4 | gup -u postdelay.nix default.nix |
4 | nix-build -o ${1} ./default.nix \ No newline at end of file | 5 | |
6 | nix-build -o ${1} ./default.nix | ||
7 | |||
8 | find . \( \( -name '.gup' -or -name '.git' \) -prune \) -or \( -type f -exec shasum '{}' ';' \) | gup --contents \ No newline at end of file | ||
@@ -1,9 +1,18 @@ | |||
1 | { pkgs ? (import <nixpkgs> {}) | 1 | { nixpkgs ? import <nixpkgs> {}, compiler ? "default" }: |
2 | }: | ||
3 | 2 | ||
4 | pkgs.lib.overrideDerivation (pkgs.haskellPackages.callPackage ./default.nix {}) (oldArgs: { | 3 | let |
5 | buildInputs = with pkgs; [ cabal2nix gup ]; | 4 | inherit (nixpkgs) pkgs; |
6 | shellHook = '' | 5 | |
7 | export PROMPT_INFO=${oldArgs.name} | 6 | haskellPackages = if compiler == "default" |
8 | ''; | 7 | then pkgs.haskellPackages |
9 | }) | 8 | else pkgs.haskell.packages.${compiler}; |
9 | |||
10 | drv = haskellPackages.callPackage ./postdelay.nix {}; | ||
11 | in | ||
12 | pkgs.stdenv.lib.overrideDerivation drv.env (oldAttrs: { | ||
13 | buildInputs = oldAttrs.buildInputs ++ (with pkgs; [ cabal2nix gup ]); | ||
14 | shellHook = '' | ||
15 | ${oldAttrs.shellHook} | ||
16 | export PROMPT_INFO="${oldAttrs.name}" | ||
17 | ''; | ||
18 | }) | ||