Hello community,

here is the log from the commit of package ghc-imm for openSUSE:Factory checked 
in at 2017-08-31 20:56:32
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ghc-imm (Old)
 and      /work/SRC/openSUSE:Factory/.ghc-imm.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "ghc-imm"

Thu Aug 31 20:56:32 2017 rev:2 rq:513399 version:1.2.0.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/ghc-imm/ghc-imm.changes  2017-05-17 
10:51:04.022572947 +0200
+++ /work/SRC/openSUSE:Factory/.ghc-imm.new/ghc-imm.changes     2017-08-31 
20:56:32.714727365 +0200
@@ -1,0 +2,5 @@
+Thu Jul 27 14:07:10 UTC 2017 - psim...@suse.com
+
+- Update to version 1.2.0.0.
+
+-------------------------------------------------------------------

Old:
----
  imm-1.1.0.0.tar.gz

New:
----
  imm-1.2.0.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ ghc-imm.spec ++++++
--- /var/tmp/diff_new_pack.6Y5Bf8/_old  2017-08-31 20:56:33.570607111 +0200
+++ /var/tmp/diff_new_pack.6Y5Bf8/_new  2017-08-31 20:56:33.582605426 +0200
@@ -18,7 +18,7 @@
 
 %global pkg_name imm
 Name:           ghc-%{pkg_name}
-Version:        1.1.0.0
+Version:        1.2.0.0
 Release:        0
 Summary:        Execute arbitrary actions for each unread element of RSS/Atom 
feeds
 License:        WTFPL
@@ -71,6 +71,7 @@
 BuildRequires:  ghc-uri-bytestring-devel
 BuildRequires:  ghc-xml-conduit-devel
 BuildRequires:  ghc-xml-devel
+BuildRequires:  ghc-xml-types-devel
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 
 %description

++++++ imm-1.1.0.0.tar.gz -> imm-1.2.0.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/imm.cabal new/imm-1.2.0.0/imm.cabal
--- old/imm-1.1.0.0/imm.cabal   2016-10-24 22:15:11.000000000 +0200
+++ new/imm-1.2.0.0/imm.cabal   2017-03-19 10:26:16.000000000 +0100
@@ -1,9 +1,9 @@
 name:                imm
-version:             1.1.0.0
+version:             1.2.0.0
 synopsis:            Execute arbitrary actions for each unread element of 
RSS/Atom feeds
 description:         Cf README file
 homepage:            https://github.com/k0ral/imm
-license:             OtherLicense
+license:             PublicDomain
 license-file:        LICENSE
 author:              kamaradclimber, koral
 maintainer:          koral <ko...@mailoo.org>
@@ -33,6 +33,8 @@
     Imm.Logger
     Imm.Logger.Simple
     Imm.Prelude
+    Imm.XML
+    Imm.XML.Simple
   other-modules:
     Imm.Aeson
     Imm.Dyre
@@ -40,7 +42,7 @@
     Imm.Options
     Imm.Pretty
     Paths_imm
-  build-depends: aeson, atom-conduit >= 0.4, base == 4.*, blaze-html, 
blaze-markup, bytestring, case-insensitive, chunked-data >= 0.3.0, comonad, 
conduit, conduit-combinators, connection, containers, directory >= 1.2.3.0, 
dyre, fast-logger, filepath, free, hashable, HaskellNet, HaskellNet-SSL >= 
0.3.3.0, http-client >= 0.4.30, http-client-tls, http-types, mime-mail, 
monoid-subclasses, mono-traversable >= 1.0.0, network, opml-conduit >= 0.6, 
optparse-applicative, rainbow, rainbox, rss-conduit >= 0.3, safe-exceptions, 
tagged, text, transformers, time, timerep >= 2.0.0.0, tls, uri-bytestring, xml, 
xml-conduit, ansi-wl-pprint
+  build-depends: aeson, ansi-wl-pprint, atom-conduit >= 0.4, base == 4.*, 
blaze-html, blaze-markup, bytestring, case-insensitive, chunked-data >= 0.3.0, 
comonad, conduit, conduit-combinators, connection, containers, directory >= 
1.2.3.0, dyre, fast-logger, filepath, free, hashable, HaskellNet, 
HaskellNet-SSL >= 0.3.3.0, http-client >= 0.4.30, http-client-tls, http-types, 
mime-mail, monoid-subclasses, mono-traversable >= 1.0.0, network, opml-conduit 
>= 0.6, optparse-applicative, rainbow, rainbox, rss-conduit >= 0.3.1, 
safe-exceptions, tagged, text, transformers, time, timerep >= 2.0.0.0, tls, 
uri-bytestring, xml, xml-conduit >= 1.5, xml-types
   -- Build-tools:
   hs-source-dirs: src/lib
   ghc-options: -Wall -fno-warn-unused-do-bind
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/bin/Executable.hs 
new/imm-1.2.0.0/src/bin/Executable.hs
--- old/imm-1.1.0.0/src/bin/Executable.hs       2016-08-23 12:03:21.000000000 
+0200
+++ new/imm-1.2.0.0/src/bin/Executable.hs       2017-03-19 10:26:16.000000000 
+0100
@@ -5,19 +5,19 @@
 -- {{{ Imports
 import           Imm
 import           Imm.Database.JsonFile
-import qualified Imm.Hooks.WriteFile   as WriteFile
 import           Imm.HTTP.Simple
 import           Imm.Logger.Simple
 import           Imm.Prelude
+import           Imm.XML.Simple
 
 import           System.Exit
 -- }}}
 
--- mkDummyCoHooks :: (MonadIO m, MonadThrow m) => () -> CoHooksF m ()
--- mkDummyCoHooks _ = CoHooksF coOnNewElement where
---   coOnNewElement _ _ = do
---     io $ putStrLn "No hook defined."
---     throwM $ ExitFailure 1
+mkDummyCoHooks :: (MonadIO m, MonadThrow m) => () -> CoHooksF m ()
+mkDummyCoHooks _ = CoHooksF coOnNewElement where
+  coOnNewElement _ _ = do
+    io $ putStrLn "No hook defined."
+    throwM $ ExitFailure 1
 
 
 main :: IO ()
@@ -26,4 +26,4 @@
   manager <- defaultManager
   database <- defaultDatabase
 
-  imm (mkCoHttpClient, manager) (mkCoDatabase, database) (mkCoLogger, logger) 
(WriteFile.mkCoHooks, WriteFile.defaultSettings "/home/koral/feeds")
+  imm (mkCoHttpClient, manager) (mkCoDatabase, database) (mkCoLogger, logger) 
(mkDummyCoHooks, ()) (mkCoXmlParser, defaultPreProcess)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/Boot.hs 
new/imm-1.2.0.0/src/lib/Imm/Boot.hs
--- old/imm-1.1.0.0/src/lib/Imm/Boot.hs 2016-10-24 22:08:58.000000000 +0200
+++ new/imm-1.2.0.0/src/lib/Imm/Boot.hs 2017-03-19 10:26:16.000000000 +0100
@@ -37,6 +37,7 @@
 import Imm.Options as Options hiding(logLevel)
 import Imm.Prelude
 import Imm.Pretty
+import Imm.XML
 
 import Control.Comonad.Cofree
 import Control.Monad.Trans.Free
@@ -58,6 +59,7 @@
 -- > import           Imm.Hooks.SendMail
 -- > import           Imm.HTTP.Simple
 -- > import           Imm.Logger.Simple
+-- > import           Imm.XML.Simple
 -- >
 -- > main :: IO ()
 -- > main = do
@@ -65,7 +67,7 @@
 -- >   manager  <- defaultManager
 -- >   database <- defaultDatabase
 -- >
--- >   imm (mkCoHttpClient, manager) (mkCoDatabase, database) (mkCoLogger, 
logger) (mkCoHooks, sendmail)
+-- >   imm (mkCoHttpClient, manager) (mkCoDatabase, database) (mkCoLogger, 
logger) (mkCoHooks, sendmail) (mkCoXmlParser, defaultPreProcess)
 -- >
 -- > sendmail :: SendMailSettings
 -- > sendmail = SendMailSettings smtpServer formatMail
@@ -82,14 +84,15 @@
 -- >   (Just $ Authentication PLAIN "user" "password")
 -- >   (StartTls "smtp.host" defaultSettingsSMTPSTARTTLS)
 imm :: (a -> CoHttpClientF IO a, a)  -- ^ HTTP client interpreter (cf 
"Imm.HTTP")
-    -> (b -> CoDatabaseF' IO b, b)  -- ^ Database interpreter (cf 
"Imm.Database")
-    -> (c -> CoLoggerF IO c, c)     -- ^ Logger interpreter (cf "Imm.Logger")
-    -> (d -> CoHooksF IO d, d)      -- ^ Hooks interpreter (cf "Imm.Hooks")
+    -> (b -> CoDatabaseF' IO b, b)   -- ^ Database interpreter (cf 
"Imm.Database")
+    -> (c -> CoLoggerF IO c, c)      -- ^ Logger interpreter (cf "Imm.Logger")
+    -> (d -> CoHooksF IO d, d)       -- ^ Hooks interpreter (cf "Imm.Hooks")
+    -> (e -> CoXmlParserF IO e, e)   -- ^ XML parsing interpreter (cf 
"Imm.XML")
     -> IO ()
-imm coHttpClient coDatabase coLogger coHooks = void $ do
+imm coHttpClient coDatabase coLogger coHooks coXmlParser = void $ do
   options <- parseOptions
   Dyre.wrap (optionDyreMode options) realMain (optionCommand options, 
optionLogLevel options, optionColorizeLogs options, coiter next start)
-  where (next, start) = mkCoImm coHttpClient coDatabase coLogger coHooks
+  where (next, start) = mkCoImm coHttpClient coDatabase coLogger coHooks 
coXmlParser
 
 realMain :: (MonadIO m, PairingM (CoImmF m) ImmF m, MonadCatch m)
          => (Command, LogLevel, Bool, Cofree (CoImmF m) a) -> m ()
@@ -117,14 +120,22 @@
 
 -- * DSL/interpreter model
 
-type CoImmF m = Product (CoHttpClientF m) (Product (CoDatabaseF' m) (Product 
(CoLoggerF m) (CoHooksF m)))
-type ImmF = Sum HttpClientF (Sum DatabaseF' (Sum LoggerF HooksF))
+type CoImmF m = Product (CoHttpClientF m)
+  (Product (CoDatabaseF' m)
+   (Product (CoLoggerF m)
+    (Product (CoHooksF m) (CoXmlParserF m)
+    )))
+type ImmF = Sum HttpClientF (Sum DatabaseF' (Sum LoggerF (Sum HooksF 
XmlParserF)))
 
 mkCoImm :: (Functor m)
-        => (a -> CoHttpClientF m a, a) -> (b -> CoDatabaseF' m b, b) -> (c -> 
CoLoggerF m c, c) -> (d -> CoHooksF m d, d)
-        -> ((a ::: b ::: c ::: d) -> CoImmF m (a ::: b ::: c ::: d), a ::: b 
::: c ::: d)
-mkCoImm (coHttpClient, a) (coDatabase, b) (coLogger, c) (coHooks, d) =
-  (coHttpClient *:* coDatabase *:* coLogger *:* coHooks, a >: b >: c >: d)
+        => (a -> CoHttpClientF m a, a)
+        -> (b -> CoDatabaseF' m b, b)
+        -> (c -> CoLoggerF m c, c)
+        -> (d -> CoHooksF m d, d)
+        -> (e -> CoXmlParserF m e, e)
+        -> ((a ::: b ::: c ::: d ::: e) -> CoImmF m (a ::: b ::: c ::: d ::: 
e), a ::: b ::: c ::: d ::: e)
+mkCoImm (coHttpClient, a) (coDatabase, b) (coLogger, c) (coHooks, d) 
(coXmlParser, e) =
+  (coHttpClient *: coDatabase *: coLogger *: coHooks *: coXmlParser, a +: b +: 
c +: d +: e)
 
 
 -- * Util
@@ -144,7 +155,7 @@
   unless (null x || x == ("Y" :: Text)) $ throwM InterruptedException
 
 
-resolveTarget :: (MonadIO m, MonadThrow m, Functor f, MonadFree f m, 
DatabaseF' :<: f)
+resolveTarget :: (MonadIO m, MonadThrow m, MonadFree f m, DatabaseF' :<: f)
               => SafeGuard -> Maybe Core.FeedRef -> m [FeedID]
 resolveTarget s Nothing = do
   result <- keys <$> Database.fetchAll FeedTable
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/Core.hs 
new/imm-1.2.0.0/src/lib/Imm/Core.hs
--- old/imm-1.1.0.0/src/lib/Imm/Core.hs 2016-09-28 00:05:51.000000000 +0200
+++ new/imm-1.2.0.0/src/lib/Imm/Core.hs 2017-03-19 10:26:16.000000000 +0100
@@ -29,6 +29,7 @@
 import Imm.Logger
 import Imm.Prelude
 import Imm.Pretty
+import Imm.XML
 
 -- import Control.Concurrent.Async.Lifted (Async, async, mapConcurrently, 
waitAny)
 -- import Control.Concurrent.Async.Pool
@@ -51,12 +52,8 @@
 
 import           System.Info
 
-import Text.Atom.Conduit.Parse
-import Text.Atom.Types
 import Text.OPML.Conduit.Parse
 import Text.OPML.Types as OPML
-import Text.RSS.Conduit.Parse
-import Text.RSS.Types
 import Text.XML as XML ()
 import Text.XML.Stream.Parse as XML
 
@@ -70,7 +67,7 @@
   putStrLn $ "compiled by " ++ compilerName ++ "-" ++ showVersion 
compilerVersion
 
 -- | Print database status for given feed(s)
-showFeed :: (MonadIO m, LoggerF :<: f, MonadThrow m, Functor f, MonadFree f m, 
DatabaseF' :<: f)
+showFeed :: (MonadIO m, LoggerF :<: f, MonadThrow m, MonadFree f m, DatabaseF' 
:<: f)
          => [FeedID] -> m ()
 showFeed feedIDs = do
   feeds <- Database.fetchList FeedTable feedIDs
@@ -78,38 +75,41 @@
   if null feeds then logWarning "No subscription" else putBox $ 
entryTableToBox feeds
 
 -- | Register the given feed URI in database
-subscribe :: (LoggerF :<: f, Functor f, MonadFree f m, DatabaseF' :<: f, 
MonadCatch m)
+subscribe :: (LoggerF :<: f, MonadFree f m, DatabaseF' :<: f, MonadCatch m)
           => URI -> Maybe Text -> m ()
 subscribe uri category = Database.register (FeedID uri) $ fromMaybe "default" 
category
 
 -- | Check for unread elements without processing them
-check :: (MonadIO m, MonadCatch m, LoggerF :<: f, Functor f, MonadFree f m, 
DatabaseF' :<: f, HttpClientF :<: f)
+check :: (MonadIO m, MonadCatch m, LoggerF :<: f, MonadFree f m, DatabaseF' 
:<: f, HttpClientF :<: f, XmlParserF :<: f)
       => [FeedID] -> m ()
 check feedIDs = do
-  results <- forM (zip ([1..] :: [Int]) feedIDs) $ \(i, feedID) -> do
+  results <- for (zip ([1..] :: [Int]) feedIDs) $ \(i, feedID) -> do
     logInfo $ brackets (fill width (bold $ cyan $ pretty i) <+> "/" <+> pretty 
total) <+> "Checking" <+> magenta (pretty feedID) <> "..."
     try $ checkOne feedID
 
+  flushLogs
+
   putBox $ statusTableToBox $ mapFromList $ zip feedIDs results
+
+  let (failures, successes) = partitionEithers $ zipWith (\a -> bimap (a,) 
(a,)) feedIDs results
+  unless (null failures) $ logError $ bold (pretty $ length failures) <+> 
"feeds in error"
+  forM_ failures $ \(feedID, e) ->
+    logError $ indent 2 (pretty feedID <++> indent 2 (pretty $ 
displayException e))
+
   where width = length (show total :: String)
         total = length feedIDs
 
-checkOne :: (MonadIO m, MonadCatch m, LoggerF :<: f, Functor f, MonadFree f m, 
DatabaseF' :<: f, HttpClientF :<: f)
+checkOne :: (MonadIO m, MonadCatch m, LoggerF :<: f, MonadFree f m, DatabaseF' 
:<: f, HttpClientF :<: f, XmlParserF :<: f)
          => FeedID -> m Int
-checkOne feedID@(FeedID uri) = do
-  body <- HTTP.get uri
-  feed <- runConduit $ parseLBS def body =$= force "Invalid feed" ((fmap Left 
<$> atomFeed) `orE` (fmap Right <$> rssDocument))
-
+checkOne feedID = do
+  feed <- getFeed feedID
   case feed of
-    Left _ -> logDebug $ "Parsed Atom feed: " <> pretty feedID
-    Right _ -> logDebug $ "Parsed RSS feed: " <> pretty feedID
+    Atom _ -> logDebug $ "Parsed Atom feed: " <> pretty feedID
+    Rss _ -> logDebug $ "Parsed RSS feed: " <> pretty feedID
 
-  let dates = either
-              (map entryUpdated . feedEntries)
-              (mapMaybe itemPubDate . channelItems)
-              feed
+  let dates = mapMaybe getDate $ getElements feed
 
-  logDebug $ vsep $ either (map prettyEntry . feedEntries) (map prettyItem . 
channelItems) feed
+  logDebug $ vsep $ map prettyElement $ getElements feed
   status <- Database.getStatus feedID
 
   return $ length $ filter (unread status) dates
@@ -117,10 +117,10 @@
         unread _ _                = True
 
 
-run :: (MonadIO m, MonadCatch m, HooksF :<: f, LoggerF :<: f, Functor f, 
MonadFree f m, DatabaseF' :<: f, HttpClientF :<: f)
+run :: (MonadIO m, MonadCatch m, HooksF :<: f, LoggerF :<: f, MonadFree f m, 
DatabaseF' :<: f, HttpClientF :<: f, XmlParserF :<: f)
     => [FeedID] -> m ()
 run feedIDs = do
-  results <- forM (zip ([1..] :: [Int]) feedIDs) $ \(i, feedID) -> do
+  results <- for (zip ([1..] :: [Int]) feedIDs) $ \(i, feedID) -> do
     logInfo $ brackets (fill width (bold $ cyan $ pretty i) <+> "/" <+> pretty 
total) <+> "Processing" <+> magenta (pretty feedID) <> "..."
     result <- tryAny $ runOne feedID
     return $ bimap (feedID,) (feedID,) result
@@ -136,14 +136,13 @@
   where width = length (show total :: String)
         total = length feedIDs
 
-runOne :: (MonadIO m, MonadCatch m, HooksF :<: f, LoggerF :<: f, Functor f, 
MonadFree f m, DatabaseF' :<: f, HttpClientF :<: f)
+runOne :: (MonadIO m, MonadCatch m, HooksF :<: f, LoggerF :<: f, MonadFree f 
m, DatabaseF' :<: f, HttpClientF :<: f, XmlParserF :<: f)
     => FeedID -> m ()
-runOne feedID@(FeedID uri) = do
-  body <- HTTP.get uri
-  feed <- runConduit $ parseLBS def body =$= force "Invalid feed" ((fmap Atom 
<$> atomFeed) `orE` (fmap Rss <$> rssDocument))
+runOne feedID = do
+  feed <- getFeed feedID
   unreadElements <- filterM (fmap not . isRead feedID) $ getElements feed
 
-  unless (null unreadElements) $ logInfo $ indent 2 $ green (pretty $ length 
unreadElements) <+> "unread element(s)"
+  unless (null unreadElements) $ logInfo $ indent 2 $ green (pretty $ length 
unreadElements) <+> "new element(s)"
 
   forM_ unreadElements $ \element -> do
     onNewElement feed element
@@ -152,7 +151,7 @@
   Database.markAsRead feedID
 
 
-isRead :: (Functor f, MonadCatch m, DatabaseF' :<: f, MonadFree f m) => FeedID 
-> FeedElement -> m Bool
+isRead :: (MonadCatch m, DatabaseF' :<: f, MonadFree f m) => FeedID -> 
FeedElement -> m Bool
 isRead feedID element = do
   DatabaseEntry _ _ readHashes lastCheck <- Database.fetch FeedTable feedID
   let matchHash = not $ null $ (setFromList (getHashes element) :: Set Int) 
`intersection` readHashes
@@ -163,18 +162,23 @@
   return $ matchHash || matchDate
 
 -- | 'subscribe' to all feeds described by the OPML document provided in input 
(stdin)
-importOPML :: (MonadIO m, LoggerF :<: f, Functor f, MonadFree f m, DatabaseF' 
:<: f, MonadCatch m) => m ()
+importOPML :: (MonadIO m, LoggerF :<: f, MonadFree f m, DatabaseF' :<: f, 
MonadCatch m) => m ()
 importOPML = do
   opml <- runConduit $ Conduit.stdin =$= XML.parseBytes def =$= force "Invalid 
OPML" parseOpml
   forM_ (opmlOutlines opml) $ importOPML' mempty
 
-importOPML' :: (MonadIO m, LoggerF :<: f, Functor f, MonadFree f m, DatabaseF' 
:<: f, MonadCatch m)
+importOPML' :: (MonadIO m, LoggerF :<: f, MonadFree f m, DatabaseF' :<: f, 
MonadCatch m)
             => Maybe Text -> Tree OpmlOutline -> m ()
 importOPML' _ (Node (OpmlOutlineGeneric b _) sub) = mapM_ (importOPML' (Just . 
toNullable $ OPML.text b)) sub
 importOPML' c (Node (OpmlOutlineSubscription _ s) _) = subscribe (xmlUri s) c
 importOPML' _ _ = return ()
 
 
+getFeed :: (MonadIO m, MonadCatch m, MonadFree f m, HttpClientF :<: f, LoggerF 
:<: f, XmlParserF :<: f)
+        => FeedID -> m Feed
+getFeed (FeedID uri) = HTTP.get uri >>= parseXml uri
+
+
 -- * Boxes
 
 putBox :: (Orientation a, MonadIO m) => Box a -> m ()
@@ -202,6 +206,6 @@
 statusTableToBox t = tableByColumns $ Rainbox.intersperse sep $ fromList 
[col1, col2, col3] where
   result = sortBy (comparing fst) $ Map.toList t
   col1 = fromList $ cell "# UNREAD" : map (cell . either (const "?") show . 
snd) result
-  col2 = fromList $ cell "STATUS" : map (cell . either (fromString . 
displayException) (const "OK") . snd) result
+  col2 = fromList $ cell "STATUS" : map (cell . either (const "ERROR") (const 
"OK") . snd) result
   col3 = fromList $ cell "FEED" : map (cell . show . pretty . fst) result
   sep = fromList [separator mempty 2]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/Database/FeedTable.hs 
new/imm-1.2.0.0/src/lib/Imm/Database/FeedTable.hs
--- old/imm-1.1.0.0/src/lib/Imm/Database/FeedTable.hs   2016-10-01 
00:18:43.000000000 +0200
+++ new/imm-1.2.0.0/src/lib/Imm/Database/FeedTable.hs   2017-03-19 
10:26:16.000000000 +0100
@@ -95,19 +95,19 @@
 
 -- * Primitives
 
-register :: (MonadThrow m, LoggerF :<: f, DatabaseF' :<: f, Functor f, 
MonadFree f m)
+register :: (MonadThrow m, LoggerF :<: f, DatabaseF' :<: f, MonadFree f m)
           => FeedID -> Text -> m ()
 register feedID category = do
   logInfo $ "Registering feed " <> magenta (pretty feedID) <> "..."
   insert FeedTable feedID $ newDatabaseEntry feedID category
 
-getStatus :: (DatabaseF' :<: f, Functor f, MonadFree f m, MonadCatch m)
+getStatus :: (DatabaseF' :<: f, MonadFree f m, MonadCatch m)
           => FeedID -> m FeedStatus
 getStatus feedID = handleAny (\_ -> return Unknown) $ do
   result <- fmap Just (fetch FeedTable feedID) `catchAny` (\_ -> return 
Nothing)
   return $ maybe New LastUpdate $ entryLastCheck =<< result
 
-addReadHash :: (DatabaseF' :<: f, Functor f, MonadFree f m, MonadThrow m, 
LoggerF :<: f)
+addReadHash :: (DatabaseF' :<: f, MonadFree f m, MonadThrow m, LoggerF :<: f)
                => FeedID -> Int -> m ()
 addReadHash feedID hash = do
   logDebug $ "Adding read hash: " <> pretty hash <> "..."
@@ -115,7 +115,7 @@
   where f a = a { entryReadHashes = insertSet hash $ entryReadHashes a }
 
 -- | Set the last check time to now
-markAsRead :: (MonadIO m, DatabaseF' :<: f, Functor f, MonadFree f m, 
MonadThrow m, LoggerF :<: f)
+markAsRead :: (MonadIO m, DatabaseF' :<: f, MonadFree f m, MonadThrow m, 
LoggerF :<: f)
            => FeedID -> m ()
 markAsRead feedID = do
   logDebug $ "Marking feed as read: " <> pretty feedID <> "..."
@@ -124,7 +124,7 @@
   where f time a = a { entryLastCheck = Just time }
 
 -- | Unset feed's last update and remove all read hashes
-markAsUnread ::  (DatabaseF' :<: f, Functor f, MonadFree f m, MonadThrow m, 
LoggerF :<: f)
+markAsUnread ::  (DatabaseF' :<: f, MonadFree f m, MonadThrow m, LoggerF :<: f)
              => FeedID -> m ()
 markAsUnread feedID = do
   logInfo $ "Marking feed as unread: " <> show (pretty feedID) <> "..."
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/Database/JsonFile.hs 
new/imm-1.2.0.0/src/lib/Imm/Database/JsonFile.hs
--- old/imm-1.1.0.0/src/lib/Imm/Database/JsonFile.hs    2016-08-22 
15:15:42.000000000 +0200
+++ new/imm-1.2.0.0/src/lib/Imm/Database/JsonFile.hs    2017-03-19 
10:26:16.000000000 +0100
@@ -39,9 +39,9 @@
 mkJsonFileDatabase :: (Table t) => FilePath -> JsonFileDatabase t
 mkJsonFileDatabase file = JsonFileDatabase file mempty Empty
 
--- | Default database is stored in @$XDG_DATA_HOME\/imm\/feeds.json@
+-- | Default database is stored in @$XDG_CONFIG_HOME\/imm\/feeds.json@
 defaultDatabase :: Table t => IO (JsonFileDatabase t)
-defaultDatabase = mkJsonFileDatabase <$> getXdgDirectory XdgData 
"imm/feeds.json"
+defaultDatabase = mkJsonFileDatabase <$> getXdgDirectory XdgConfig 
"imm/feeds.json"
 
 
 data JsonException = UnableDecode
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/Database.hs 
new/imm-1.2.0.0/src/lib/Imm/Database.hs
--- old/imm-1.1.0.0/src/lib/Imm/Database.hs     2016-09-27 23:01:07.000000000 
+0200
+++ new/imm-1.2.0.0/src/lib/Imm/Database.hs     2017-03-19 10:26:16.000000000 
+0100
@@ -111,62 +111,62 @@
 
 -- * Primitives
 
-describeDatabase :: (Functor f, MonadFree f m, DatabaseF t :<: f)
+describeDatabase :: (MonadFree f m, DatabaseF t :<: f)
                  => t -> m Doc
 describeDatabase t = liftF . inj $ Describe t id
 
-fetch :: (Functor f, MonadFree f m, DatabaseF t :<: f, Table t, MonadThrow m)
+fetch :: (MonadFree f m, DatabaseF t :<: f, Table t, MonadThrow m)
       => t -> Key t -> m (Entry t)
 fetch t k = do
   results <- liftF . inj $ FetchList t [k] id
   result <- lookup k <$> liftE results
   maybe (throwM $ NotFound t [k]) return result
 
-fetchList :: (Functor f, MonadFree f m, DatabaseF t :<: f, MonadThrow m)
+fetchList :: (MonadFree f m, DatabaseF t :<: f, MonadThrow m)
           => t -> [Key t] -> m (Map (Key t) (Entry t))
 fetchList t k = do
   result <- liftF . inj $ FetchList t k id
   liftE result
 
-fetchAll :: (MonadThrow m, Functor f, MonadFree f m, DatabaseF t :<: f) => t 
-> m (Map (Key t) (Entry t))
+fetchAll :: (MonadThrow m, MonadFree f m, DatabaseF t :<: f) => t -> m (Map 
(Key t) (Entry t))
 fetchAll t = do
   result <- liftF . inj $ FetchAll t id
   liftE result
 
-update :: (Functor f, MonadFree f m, DatabaseF t :<: f, MonadThrow m)
+update :: (MonadFree f m, DatabaseF t :<: f, MonadThrow m)
        => t -> Key t -> (Entry t -> Entry t) -> m ()
 update t k f = do
   result <- liftF . inj $ Update t k f id
   liftE result
 
-insert :: (MonadThrow m, Functor f, MonadFree f m, LoggerF :<: f, DatabaseF t 
:<: f)
+insert :: (MonadThrow m, MonadFree f m, LoggerF :<: f, DatabaseF t :<: f)
        => t -> Key t -> Entry t -> m ()
 insert t k v = insertList t [(k, v)]
 
-insertList :: (MonadThrow m, Functor f, MonadFree f m, LoggerF :<: f, 
DatabaseF t :<: f)
+insertList :: (MonadThrow m, MonadFree f m, LoggerF :<: f, DatabaseF t :<: f)
            => t -> [(Key t, Entry t)] -> m ()
 insertList t i = do
   logInfo $ "Inserting " <> yellow (pretty $ length i) <> " entries..."
   result <- liftF . inj $ InsertList t i id
   liftE result
 
-delete :: (MonadThrow m, Functor f, MonadFree f m, LoggerF :<: f, DatabaseF t 
:<: f) => t -> Key t -> m ()
+delete :: (MonadThrow m, MonadFree f m, LoggerF :<: f, DatabaseF t :<: f) => t 
-> Key t -> m ()
 delete t k = deleteList t [k]
 
-deleteList :: (MonadThrow m, Functor f, MonadFree f m, LoggerF :<: f, 
DatabaseF t :<: f)
+deleteList :: (MonadThrow m, MonadFree f m, LoggerF :<: f, DatabaseF t :<: f)
            => t -> [Key t] -> m ()
 deleteList t k = do
   logInfo $ "Deleting " <> yellow (pretty $ length k) <> " entries..."
   result <- liftF . inj $ DeleteList t k id
   liftE result
 
-purge :: (MonadThrow m, Functor f, MonadFree f m, DatabaseF t :<: f, LoggerF 
:<: f) => t -> m ()
+purge :: (MonadThrow m, MonadFree f m, DatabaseF t :<: f, LoggerF :<: f) => t 
-> m ()
 purge t = do
   logInfo "Purging database..."
   result <- liftF . inj $ Purge t id
   liftE result
 
-commit :: (MonadThrow m, Functor f, MonadFree f m, DatabaseF t :<: f, LoggerF 
:<: f) => t -> m ()
+commit :: (MonadThrow m, MonadFree f m, DatabaseF t :<: f, LoggerF :<: f) => t 
-> m ()
 commit t = do
   logDebug "Committing database transaction..."
   result <- liftF . inj $ Commit t id
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/Feed.hs 
new/imm-1.2.0.0/src/lib/Imm/Feed.hs
--- old/imm-1.1.0.0/src/lib/Imm/Feed.hs 2016-10-09 14:19:07.000000000 +0200
+++ new/imm-1.2.0.0/src/lib/Imm/Feed.hs 2017-03-19 10:26:16.000000000 +0100
@@ -7,7 +7,6 @@
 import           Imm.Pretty
 
 import           Data.Hashable
-import           Data.NonNull
 import           Data.Time
 
 import           Text.Atom.Types
@@ -57,3 +56,10 @@
   <> [hash $ itemTitle item]
   <> [hash $ itemDescription item]
 getHashes (AtomElement entry) = [hash $ entryId entry, (hash :: String -> Int) 
$ show $ prettyAtomText $ entryTitle entry]
+
+
+-- * Misc
+
+prettyElement :: FeedElement -> Doc
+prettyElement (RssElement item)   = prettyItem item
+prettyElement (AtomElement entry) = prettyEntry entry
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/HTTP.hs 
new/imm-1.2.0.0/src/lib/Imm/HTTP.hs
--- old/imm-1.1.0.0/src/lib/Imm/HTTP.hs 2016-09-27 23:51:58.000000000 +0200
+++ new/imm-1.2.0.0/src/lib/Imm/HTTP.hs 2017-03-19 10:26:16.000000000 +0100
@@ -40,7 +40,7 @@
 -- * Primitives
 
 -- | Perform an HTTP GET request
-get :: (MonadFree f m, Functor f, HttpClientF :<: f, LoggerF :<: f, MonadThrow 
m)
+get :: (MonadFree f m, HttpClientF :<: f, LoggerF :<: f, MonadThrow m)
     => URI -> m LByteString
 get uri = do
   logDebug $ "Fetching " <> prettyURI uri
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/Hooks/WriteFile.hs 
new/imm-1.2.0.0/src/lib/Imm/Hooks/WriteFile.hs
--- old/imm-1.1.0.0/src/lib/Imm/Hooks/WriteFile.hs      2016-10-16 
22:24:40.000000000 +0200
+++ new/imm-1.2.0.0/src/lib/Imm/Hooks/WriteFile.hs      2017-03-19 
10:26:16.000000000 +0100
@@ -1,3 +1,4 @@
+{-# LANGUAGE FlexibleContexts  #-}
 {-# LANGUAGE NamedFieldPuns    #-}
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# LANGUAGE OverloadedStrings #-}
@@ -11,7 +12,9 @@
 import           Imm.Prelude
 import           Imm.Pretty
 
-import           Data.Monoid.Textual           hiding (map)
+import           Control.Arrow
+
+import           Data.Monoid.Textual           hiding (elem, map)
 import qualified Data.Text.Lazy                as Text
 import           Data.Time
 
@@ -19,9 +22,10 @@
 import           System.FilePath
 
 import           Text.Atom.Types
-import qualified Text.Blaze                    as Blaze
 import           Text.Blaze.Html.Renderer.Text
-import           Text.Blaze.Html5              as H hiding (map)
+import           Text.Blaze.Html5              (Html, docTypeHtml,
+                                                preEscapedToHtml, (!))
+import qualified Text.Blaze.Html5              as H hiding (map)
 import           Text.Blaze.Html5.Attributes   as H (charset, href)
 import           Text.RSS.Types
 
@@ -57,9 +61,12 @@
 defaultFilePath :: FilePath -> Feed -> FeedElement -> FilePath
 defaultFilePath root feed element = makeValid $ root </> feedTitle </> 
fileName <.> "html" where
   date = maybe "" (formatTime defaultTimeLocale "%F-") $ getDate element
-  fileName = date <> convertText (sanitizePath $ getTitle element)
-  feedTitle = convertText $ sanitizePath $ getFeedTitle feed
-  sanitizePath = intercalate "-" . split isPathSeparator
+  fileName = date <> sanitize (convertText $ getTitle element)
+  feedTitle = sanitize $ convertText $ getFeedTitle feed
+  sanitize = replaceIf isPathSeparator '-' >>> replaceAny ".?!#" '_'
+  replaceAny :: [Char] -> Char -> String -> String
+  replaceAny list = replaceIf (`elem` list)
+  replaceIf f b = map (\c -> if f c then b else c)
 
 -- | Generate an HTML page, with a title, a header and an article that 
contains the feed element
 defaultFileContent :: Feed -> FeedElement -> ByteString
@@ -67,9 +74,9 @@
   H.head $ do
     H.meta ! H.charset "utf-8"
     H.title $ convertText $ getFeedTitle feed <> " | " <> getTitle element
-  body $ do
+  H.body $ do
     H.h1 $ convertText $ getFeedTitle feed
-    article $ do
+    H.article $ do
       defaultHeader feed element
       defaultBody feed element
 
@@ -78,29 +85,29 @@
 
 -- | Generate an HTML @<header>@ for a given feed element
 defaultHeader :: Feed -> FeedElement -> Html
-defaultHeader _ element@(RssElement item) = header $ do
+defaultHeader _ element@(RssElement item) = H.header $ do
   H.h2 $ maybe id (\uri -> H.a ! H.href uri) link $ convertText $ getTitle 
element
-  unless (null author) $ address $ "Published by " >> convertText author
-  forM_ (itemPubDate item) $ \date -> p $ " on " >> time (convertDoc $ 
prettyTime date)
+  unless (null author) $ H.address $ "Published by " >> convertText author
+  forM_ (itemPubDate item) $ \date -> H.p $ " on " >> H.time (convertDoc $ 
prettyTime date)
   where link = withRssURI (convertDoc . prettyURI) <$> itemLink item
         author = itemAuthor item
-defaultHeader _ element@(AtomElement entry) = header $ do
+defaultHeader _ element@(AtomElement entry) = H.header $ do
   H.h2 $ convertText $ getTitle element
-  address $ do
+  H.address $ do
     "Published by "
     forM_ (entryAuthors entry) $ \author -> do
       convertDoc $ prettyPerson author
       ", "
-  p $ "on " >> time (convertDoc $ prettyTime $ entryUpdated entry)
+  H.p $ "on " >> H.time (convertDoc $ prettyTime $ entryUpdated entry)
 
 -- | Generate the HTML content for a given feed element
 defaultBody :: Feed -> FeedElement -> Html
-defaultBody _ (RssElement item) = p $ preEscapedToHtml $ itemDescription item
+defaultBody _ (RssElement item) = H.p $ preEscapedToHtml $ itemDescription item
 defaultBody _ (AtomElement entry) = do
-  unless (null links) $ p $ do
+  unless (null links) $ H.p $ do
     "Related links:"
     H.ul $ forM_ links $ \uri -> H.li (H.a ! H.href (convertAtomURI uri) $ 
convertAtomURI uri)
-  p $ preEscapedToHtml $ fromMaybe "<empty>" $ content <|> summary
+  H.p $ preEscapedToHtml $ fromMaybe "<empty>" $ content <|> summary
   where links   = map linkHref $ entryLinks entry
         content = show . prettyAtomContent <$> entryContent entry :: Maybe Text
         summary = show . prettyAtomText <$> entrySummary entry :: Maybe Text
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/Hooks.hs 
new/imm-1.2.0.0/src/lib/Imm/Hooks.hs
--- old/imm-1.1.0.0/src/lib/Imm/Hooks.hs        2016-08-21 10:48:44.000000000 
+0200
+++ new/imm-1.2.0.0/src/lib/Imm/Hooks.hs        2017-03-19 10:26:16.000000000 
+0100
@@ -37,7 +37,7 @@
 
 -- * Primitives
 
-onNewElement :: (Functor f, MonadFree f m, LoggerF :<: f, HooksF :<: f) => 
Feed -> FeedElement -> m ()
+onNewElement :: (MonadFree f m, LoggerF :<: f, HooksF :<: f) => Feed -> 
FeedElement -> m ()
 onNewElement feed element = do
   logDebug $ "Unread element:" <+> textual (getTitle element)
   liftF . inj $ OnNewElement feed element ()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/Logger.hs 
new/imm-1.2.0.0/src/lib/Imm/Logger.hs
--- old/imm-1.1.0.0/src/lib/Imm/Logger.hs       2016-10-23 17:50:25.000000000 
+0200
+++ new/imm-1.2.0.0/src/lib/Imm/Logger.hs       2017-03-19 10:26:16.000000000 
+0100
@@ -64,24 +64,24 @@
 
 -- * Primitives
 
-log :: (Functor f, MonadFree f m, LoggerF :<: f) => LogLevel -> Doc -> m ()
+log :: (MonadFree f m, LoggerF :<: f) => LogLevel -> Doc -> m ()
 log level message = liftF . inj $ Log level message ()
 
-getLogLevel :: (Functor f, MonadFree f m, LoggerF :<: f) => m LogLevel
+getLogLevel :: (MonadFree f m, LoggerF :<: f) => m LogLevel
 getLogLevel = liftF . inj $ GetLevel id
 
-setLogLevel :: (Functor f, MonadFree f m, LoggerF :<: f) => LogLevel -> m ()
+setLogLevel :: (MonadFree f m, LoggerF :<: f) => LogLevel -> m ()
 setLogLevel level = liftF . inj $ SetLevel level ()
 
-setColorizeLogs :: (Functor f, MonadFree f m, LoggerF :<: f) => Bool -> m ()
+setColorizeLogs :: (MonadFree f m, LoggerF :<: f) => Bool -> m ()
 setColorizeLogs colorize = liftF . inj $ SetColorize colorize ()
 
-flushLogs :: (Functor f, MonadFree f m, LoggerF :<: f) => m ()
+flushLogs :: (MonadFree f m, LoggerF :<: f) => m ()
 flushLogs = liftF . inj $ Flush ()
 
 -- * Helpers
 
-logDebug, logInfo, logWarning, logError :: (Functor f, MonadFree f m, LoggerF 
:<: f) => Doc -> m ()
+logDebug, logInfo, logWarning, logError :: (MonadFree f m, LoggerF :<: f) => 
Doc -> m ()
 logDebug = log Debug
 logInfo = log Info
 logWarning = log Warning
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/Options.hs 
new/imm-1.2.0.0/src/lib/Imm/Options.hs
--- old/imm-1.1.0.0/src/lib/Imm/Options.hs      2016-10-12 20:21:56.000000000 
+0200
+++ new/imm-1.2.0.0/src/lib/Imm/Options.hs      2017-03-19 10:26:16.000000000 
+0100
@@ -68,7 +68,7 @@
 --         ++ catMaybes [("CONFIG=" ++) <$> opts^.configurationLabel_]
 
 parseOptions :: (MonadIO m) => m CliOptions
-parseOptions = io $ customExecParser (defaultPrefs {- noBacktrack -} ) (info 
parser $ progDesc "Fetch elements from RSS/Atom feeds and execute arbitrary 
actions for each of them.")
+parseOptions = io $ customExecParser (prefs noBacktrack) (info parser $ 
progDesc "Convert items from RSS/Atom feeds to mails.")
   where parser = helper <*> optional dyreMasterBinary *> optional dyreDebug *> 
cliOptions
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/Prelude.hs 
new/imm-1.2.0.0/src/lib/Imm/Prelude.hs
--- old/imm-1.1.0.0/src/lib/Imm/Prelude.hs      2016-10-23 19:10:49.000000000 
+0200
+++ new/imm-1.2.0.0/src/lib/Imm/Prelude.hs      2017-03-19 10:26:16.000000000 
+0100
@@ -45,7 +45,7 @@
 import           Data.Tagged
 import qualified Data.Text                       as T (Text ())
 import qualified Data.Text.Lazy                  as LT (Text ())
-import           Data.Traversable                as X (forM)
+import           Data.Traversable                as X (for, forM)
 import           Data.Typeable                   as X
 
 import qualified GHC.Show                        as Show
@@ -81,13 +81,13 @@
 infixr 0 :::
 
 -- | Right-associative tuple data-constructor
-(>:) :: a -> b -> (a,b)
-(>:) a b = (a, b)
-infixr 0 >:
-
-(*:*) :: (Functor f, Functor g) => (a -> f a) -> (b -> g b) -> (a, b) -> 
Product f g (a, b)
-(*:*) f g (a,b) = Pair ((,b) <$> f a) ((a,) <$> g b)
-infixr 0 *:*
+(+:) :: a -> b -> (a,b)
+(+:) a b = (a, b)
+infixr 0 +:
+
+(*:) :: (Functor f, Functor g) => (a -> f a) -> (b -> g b) -> (a, b) -> 
Product f g (a, b)
+(*:) f g (a,b) = Pair ((,b) <$> f a) ((a,) <$> g b)
+infixr 0 *:
 
 
 data HLeft
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/Pretty.hs 
new/imm-1.2.0.0/src/lib/Imm/Pretty.hs
--- old/imm-1.1.0.0/src/lib/Imm/Pretty.hs       2016-08-23 01:04:31.000000000 
+0200
+++ new/imm-1.2.0.0/src/lib/Imm/Pretty.hs       2017-03-19 10:26:16.000000000 
+0100
@@ -16,7 +16,8 @@
 import           Text.Atom.Types              as Atom
 -- import           Text.OPML.Types              as OPML hiding (text)
 -- import qualified Text.OPML.Types              as OPML
-import           Text.PrettyPrint.ANSI.Leijen as X hiding ((<$>), (</>), (<>))
+import           Text.PrettyPrint.ANSI.Leijen as X hiding (sep, width, (<$>),
+                                                    (</>), (<>))
 import           Text.RSS.Types               as RSS
 
 import           URI.ByteString
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/XML/Simple.hs 
new/imm-1.2.0.0/src/lib/Imm/XML/Simple.hs
--- old/imm-1.1.0.0/src/lib/Imm/XML/Simple.hs   1970-01-01 01:00:00.000000000 
+0100
+++ new/imm-1.2.0.0/src/lib/Imm/XML/Simple.hs   2017-03-19 10:26:16.000000000 
+0100
@@ -0,0 +1,37 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
+-- | Simple interpreter to parse XML into 'Feed', based on 'Conduit'.
+module Imm.XML.Simple where
+
+-- {{{ Imports
+import           Imm.Feed
+import           Imm.Prelude
+import           Imm.XML
+
+import           Control.Monad
+import           Control.Monad.Fix
+
+import           Data.Conduit
+import           Data.XML.Types
+
+import           Text.Atom.Conduit.Parse
+import           Text.RSS.Conduit.Parse
+import           Text.RSS1.Conduit.Parse
+import           Text.XML.Stream.Parse
+
+import           URI.ByteString
+-- }}}
+
+-- | A 'Conduit' to alter the raw XML before feeding it to the parser, 
depending on the feed 'URI'
+type PreProcess m = URI -> Conduit Event m Event
+
+-- | Interpreter for 'XmlParserF'
+mkCoXmlParser :: (MonadIO m, MonadCatch m) => PreProcess m -> CoXmlParserF m 
(PreProcess m)
+mkCoXmlParser preProcess = CoXmlParserF coParse where
+  coParse uri bytestring = handleAny (\e -> return (Left e, preProcess)) $ do
+    result <- runConduit $ parseLBS def bytestring =$= preProcess uri =$= 
force "Invalid feed" ((fmap Atom <$> atomFeed) `orE` (fmap Rss <$> rssDocument) 
`orE` (fmap Rss <$> rss1Document))
+    return (Right result, preProcess)
+
+-- | Default pre-process always forwards all 'Event's
+defaultPreProcess :: Monad m => PreProcess m
+defaultPreProcess _ = fix $ \loop -> await >>= maybe (return ()) (yield >=> 
const loop)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/imm-1.1.0.0/src/lib/Imm/XML.hs 
new/imm-1.2.0.0/src/lib/Imm/XML.hs
--- old/imm-1.1.0.0/src/lib/Imm/XML.hs  1970-01-01 01:00:00.000000000 +0100
+++ new/imm-1.2.0.0/src/lib/Imm/XML.hs  2017-03-19 10:26:16.000000000 +0100
@@ -0,0 +1,45 @@
+{-# LANGUAGE DeriveFunctor         #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE NoImplicitPrelude     #-}
+{-# LANGUAGE TypeOperators         #-}
+-- | DSL/interpreter model for parsing XML into a 'Feed'
+module Imm.XML where
+
+-- {{{ Imports
+import           Imm.Error
+import           Imm.Feed
+import           Imm.Prelude
+
+import           Control.Monad.Trans.Free
+
+import           URI.ByteString
+-- }}}
+
+-- * Types
+
+-- | XML parsing DSL
+data XmlParserF next
+  = ParseXml URI LByteString (Either SomeException Feed -> next)
+  deriving(Functor)
+
+-- | XML parsing interpreter
+newtype CoXmlParserF m a = CoXmlParserF
+  { parseXmlH :: URI -> LByteString -> m (Either SomeException Feed, a)
+  } deriving(Functor)
+
+instance Monad m => PairingM (CoXmlParserF m) XmlParserF m where
+  -- pairM :: (a -> b -> m r) -> f a -> g b -> m r
+  pairM f (CoXmlParserF p) (ParseXml uri bytestring next) = do
+    (result, a) <- p uri bytestring
+    f a $ next result
+
+-- * Primitives
+
+-- | Parse XML into a 'Feed'
+parseXml :: (MonadFree f m, XmlParserF :<: f, MonadThrow m)
+         => URI -> LByteString -> m Feed
+parseXml uri bytestring = do
+  result <- liftF . inj $ ParseXml uri bytestring id
+  liftE result


Reply via email to