packages feed

atom-conduit 0.3.1.2 → 0.4.0.0

raw patch · 5 files changed

+217/−223 lines, 5 filesdep +blaze-builderdep +conduit-combinatorsdep +safe-exceptionsdep −conduit-parsedep −exceptionsdep −foldldep ~xml-conduitPVP ok

version bump matches the API change (PVP)

Dependencies added: blaze-builder, conduit-combinators, safe-exceptions

Dependencies removed: conduit-parse, exceptions, foldl, xml-conduit-parse

Dependency ranges changed: xml-conduit

API changes (from Hackage documentation)

- Text.Atom.Conduit.Parse: atomCategory :: (MonadCatch m) => ConduitParser Event m AtomCategory
+ Text.Atom.Conduit.Parse: atomCategory :: MonadThrow m => ConduitM Event o m (Maybe AtomCategory)
- Text.Atom.Conduit.Parse: atomContent :: (MonadCatch m) => ConduitParser Event m AtomContent
+ Text.Atom.Conduit.Parse: atomContent :: MonadThrow m => ConduitM Event o m (Maybe AtomContent)
- Text.Atom.Conduit.Parse: atomEntry :: (MonadCatch m) => ConduitParser Event m AtomEntry
+ Text.Atom.Conduit.Parse: atomEntry :: MonadThrow m => ConduitM Event o m (Maybe AtomEntry)
- Text.Atom.Conduit.Parse: atomFeed :: (MonadCatch m) => ConduitParser Event m AtomFeed
+ Text.Atom.Conduit.Parse: atomFeed :: MonadThrow m => ConduitM Event o m (Maybe AtomFeed)
- Text.Atom.Conduit.Parse: atomGenerator :: (MonadCatch m) => ConduitParser Event m AtomGenerator
+ Text.Atom.Conduit.Parse: atomGenerator :: MonadThrow m => ConduitM Event o m (Maybe AtomGenerator)
- Text.Atom.Conduit.Parse: atomLink :: (MonadCatch m) => ConduitParser Event m AtomLink
+ Text.Atom.Conduit.Parse: atomLink :: MonadThrow m => ConduitM Event o m (Maybe AtomLink)
- Text.Atom.Conduit.Parse: atomPerson :: (MonadCatch m) => Text -> ConduitParser Event m AtomPerson
+ Text.Atom.Conduit.Parse: atomPerson :: MonadThrow m => Text -> ConduitM Event o m (Maybe AtomPerson)
- Text.Atom.Conduit.Parse: atomSource :: (MonadCatch m) => ConduitParser Event m AtomSource
+ Text.Atom.Conduit.Parse: atomSource :: MonadThrow m => ConduitM Event o m (Maybe AtomSource)
- Text.Atom.Conduit.Parse: atomText :: (MonadCatch m) => Text -> ConduitParser Event m AtomText
+ Text.Atom.Conduit.Parse: atomText :: MonadThrow m => Text -> ConduitM Event o m (Maybe AtomText)
- Text.Atom.Lens: entryIdL :: Functor f => (NonNull Text -> f (NonNull Text)) -> AtomEntry -> f AtomEntry
+ Text.Atom.Lens: entryIdL :: Functor f => (Text -> f Text) -> AtomEntry -> f AtomEntry
- Text.Atom.Lens: feedIdL :: Functor f => (NonNull Text -> f (NonNull Text)) -> AtomFeed -> f AtomFeed
+ Text.Atom.Lens: feedIdL :: Functor f => (Text -> f Text) -> AtomFeed -> f AtomFeed
- Text.Atom.Types: AtomEntry :: [AtomPerson] -> [AtomCategory] -> Maybe AtomContent -> [AtomPerson] -> NonNull Text -> [AtomLink] -> Maybe UTCTime -> Maybe AtomText -> Maybe AtomSource -> Maybe AtomText -> AtomText -> UTCTime -> AtomEntry
+ Text.Atom.Types: AtomEntry :: [AtomPerson] -> [AtomCategory] -> Maybe AtomContent -> [AtomPerson] -> Text -> [AtomLink] -> Maybe UTCTime -> Maybe AtomText -> Maybe AtomSource -> Maybe AtomText -> AtomText -> UTCTime -> AtomEntry
- Text.Atom.Types: AtomFeed :: [AtomPerson] -> [AtomCategory] -> [AtomPerson] -> [AtomEntry] -> Maybe AtomGenerator -> Maybe AtomURI -> NonNull Text -> [AtomLink] -> Maybe AtomURI -> Maybe AtomText -> Maybe AtomText -> AtomText -> UTCTime -> AtomFeed
+ Text.Atom.Types: AtomFeed :: [AtomPerson] -> [AtomCategory] -> [AtomPerson] -> [AtomEntry] -> Maybe AtomGenerator -> Maybe AtomURI -> Text -> [AtomLink] -> Maybe AtomURI -> Maybe AtomText -> Maybe AtomText -> AtomText -> UTCTime -> AtomFeed
- Text.Atom.Types: [entryId] :: AtomEntry -> NonNull Text
+ Text.Atom.Types: [entryId] :: AtomEntry -> Text
- Text.Atom.Types: [feedId] :: AtomFeed -> NonNull Text
+ Text.Atom.Types: [feedId] :: AtomFeed -> Text

Files

Text/Atom/Conduit/Parse.hs view
@@ -24,20 +24,21 @@   ) where  -- {{{ Imports-import           Text.Atom.Types+import           Blaze.ByteString.Builder (toByteString) -import           Control.Applicative-import           Control.Foldl           hiding (mconcat, set)-import           Control.Monad           hiding (foldM)-import           Control.Monad.Catch+import           Conduit                  (foldC, headC, headDefC, sinkList) -import           Data.Conduit.Parser-import           Data.Conduit.Parser.XML+import           Control.Applicative      hiding (many)+import           Control.Exception.Safe   as Exception+import           Control.Monad            hiding (foldM)+import           Control.Monad.Fix++import           Data.Conduit import           Data.Maybe import           Data.Monoid import           Data.MonoTraversable-import           Data.NonNull            (NonNull, fromNullable, toNullable)-import           Data.Text               as Text (Text)+import           Data.NonNull             (NonNull, fromNullable, toNullable)+import           Data.Text                as Text (Text, unpack) import           Data.Text.Encoding import           Data.Time.Clock import           Data.Time.LocalTime@@ -46,66 +47,85 @@  import           Lens.Simple -import           Prelude                 hiding (last, lookup)+import           Prelude                  hiding (last, lookup) -import           Text.Parser.Combinators+import           Text.Atom.Types+import           Text.XML.Stream.Parse+import qualified Text.XML.Stream.Render   as Render  import           URI.ByteString -- }}}  -- {{{ Util-data AtomException = InvalidURI URIParseError+data AtomException = InvalidDate Text+                   | InvalidURI URIParseError+                   | MissingElement Text                    | NullElement  deriving instance Eq AtomException deriving instance Show AtomException  instance Exception AtomException where-  displayException (InvalidURI e) = "Invalid URI reference: " ++ show e-  displayException NullElement = "Null element"+  displayException (InvalidDate t)    = "Invalid date: " ++ unpack t+  displayException (InvalidURI e)     = "Invalid URI reference: " ++ show e+  displayException (MissingElement t) = "Missing element: " ++ unpack t+  displayException NullElement        = "Null element" -asURIReference :: (MonadThrow m) => Text -> m AtomURI+asURIReference :: MonadThrow m => Text -> m AtomURI asURIReference t = case (parseURI' t, parseRelativeRef' t) of-  (Right u, _) -> return $ AtomURI u-  (_, Right u) -> return $ AtomURI u+  (Right u, _)     -> return $ AtomURI u+  (_, Right u)     -> return $ AtomURI u   (Left _, Left e) -> throwM $ InvalidURI e   where parseURI' = parseURI laxURIParserOptions . encodeUtf8         parseRelativeRef' = parseRelativeRef laxURIParserOptions . encodeUtf8  asNonNull :: (MonoFoldable a, MonadThrow m) => a -> m (NonNull a)-asNonNull = maybe (throwM NullElement) return . fromNullable+asNonNull = liftMaybe NullElement . fromNullable +liftMaybe :: (MonadThrow m, Exception e) => e -> Maybe a -> m a+liftMaybe e = maybe (throw e) return+ -- | Like 'tagName' but ignores the namespace.-tagName' :: (MonadCatch m) => Text -> AttrParser a -> (a -> ConduitParser Event m b) -> ConduitParser Event m b-tagName' t = tagPredicate (\n -> nameLocalName n == t)+tagName' :: MonadThrow m => Text -> AttrParser a -> (a -> ConduitM Event o m b) -> ConduitM Event o m (Maybe b)+tagName' t = tagPredicate (\n -> nameLocalName n == t && nameNamespace n == Just "http://www.w3.org/2005/Atom")  -- | Tag which content is a date-time that follows RFC 3339 format.-tagDate :: (MonadCatch m) => Text -> ConduitParser Event m UTCTime-tagDate name = tagIgnoreAttrs' name $ content (fmap zonedTimeToUTC . parseTimeRFC3339)+tagDate :: MonadThrow m => Text -> ConduitM Event o m (Maybe UTCTime)+tagDate name = tagIgnoreAttrs' name $ do+  text <- content+  zonedTimeToUTC <$> liftMaybe (InvalidDate text) (parseTimeRFC3339 text)  -- | Like 'tagName'' but ignores all attributes.-tagIgnoreAttrs' :: (MonadCatch m) => Text -> ConduitParser Event m a -> ConduitParser Event m a+tagIgnoreAttrs' :: MonadThrow m => Text -> ConduitM Event o m a -> ConduitM Event o m (Maybe a) tagIgnoreAttrs' name handler = tagName' name ignoreAttrs $ const handler -unknownTag :: (MonadCatch m) => ConduitParser Event m ()-unknownTag = anyTag $ \_ _ -> void $ many (void unknownTag <|> void textContent)+xhtmlContent :: MonadThrow m => ConduitM Event o m Text+xhtmlContent = fmap (decodeUtf8 . toByteString) $ takeAllTreesContent =$= Render.renderBuilder def =$= foldC -atomId :: (MonadCatch m) => ConduitParser Event m (NonNull Text)-atomId = tagIgnoreAttrs' "id" $ content asNonNull -atomIcon, atomLogo :: (MonadCatch m) => ConduitParser Event m AtomURI-atomIcon = tagIgnoreAttrs' "icon" $ content asURIReference-atomLogo = tagIgnoreAttrs' "logo" $ content asURIReference+projectC :: Monad m => Fold a a' b b' -> Conduit a m b+projectC prism = fix $ \recurse -> do+  item <- await+  case (item, item ^? (_Just . prism)) of+    (_, Just a) -> yield a >> recurse+    (Just _, _) -> recurse+    _           -> return () -lastRequired :: (Monad m, Parsing m) => String -> FoldM m a a-lastRequired e = FoldM (\_ a -> return $ Right a) (return $ Left e) (either unexpected return)+headRequiredC :: MonadThrow m => Text -> Consumer a m a+headRequiredC e = liftMaybe (MissingElement e) =<< headC++atomId :: MonadThrow m => ConduitM Event o m (Maybe Text)+atomId = tagIgnoreAttrs' "id" content++atomIcon, atomLogo :: MonadThrow m => ConduitM Event o m (Maybe AtomURI)+atomIcon = tagIgnoreAttrs' "icon" $ content >>= asURIReference+atomLogo = tagIgnoreAttrs' "logo" $ content >>= asURIReference -- }}}   data PersonPiece = PersonName (NonNull Text)                  | PersonEmail Text                  | PersonUri AtomURI-                 | PersonUnknown  makeTraversals ''PersonPiece @@ -117,43 +137,40 @@ -- >   <email>JohnDoe@example.com</email> -- >   <uri>http://example.com/~johndoe</uri> -- > </author>-atomPerson :: (MonadCatch m) => Text -> ConduitParser Event m AtomPerson-atomPerson name = named ("Atom person construct <" <> name <> ">") $ tagIgnoreAttrs' name $ do-  p <- many piece-  flip foldM p $ AtomPerson-    <$> handlesM _PersonName (lastRequired "Missing or invalid <name> element.")-    <*> generalize (handles _PersonEmail $ lastDef "")-    <*> generalize (handles _PersonUri last)-  where piece :: (MonadCatch m) => ConduitParser Event m PersonPiece-        piece = choice [ PersonName <$> tagIgnoreAttrs' "name" (content asNonNull)-                       , PersonEmail <$> tagIgnoreAttrs' "email" textContent-                       , PersonUri <$> tagIgnoreAttrs' "uri" (content asURIReference)-                       , PersonUnknown <$ unknownTag-                       ]+atomPerson :: MonadThrow m => Text -> ConduitM Event o m (Maybe AtomPerson)+atomPerson name = tagIgnoreAttrs' name $ (manyYield' (choose piece) =$= parser) <* many ignoreAllTreesContent where+  parser = getZipConduit $ AtomPerson+    <$> ZipConduit (projectC _PersonName =$= headRequiredC "Missing or invalid <name> element")+    <*> ZipConduit (projectC _PersonEmail =$= headDefC "")+    <*> ZipConduit (projectC _PersonUri =$= headC)+  piece = [ fmap PersonName <$> tagIgnoreAttrs' "name" (content >>= asNonNull)+          , fmap PersonEmail <$> tagIgnoreAttrs' "email" content+          , fmap PersonUri <$> tagIgnoreAttrs' "uri" (content >>= asURIReference)+          ]   -- | Parse an @atom:category@ element. -- Example: -- -- > <category term="sports"/>-atomCategory :: (MonadCatch m) => ConduitParser Event m AtomCategory+atomCategory :: MonadThrow m => ConduitM Event o m (Maybe AtomCategory) atomCategory = tagName' "category" categoryAttrs $ \(t, s, l) -> do   term <- asNonNull t   return $ AtomCategory term s l-  where categoryAttrs = (,,) <$> textAttr "term"-                             <*> (textAttr "scheme" <|> pure mempty)-                             <*> (textAttr "label" <|> pure mempty)+  where categoryAttrs = (,,) <$> requireAttr "term"+                             <*> (requireAttr "scheme" <|> pure mempty)+                             <*> (requireAttr "label" <|> pure mempty)                              <* ignoreAttrs  -- | Parse an @atom:content@ element.-atomContent :: (MonadCatch m) => ConduitParser Event m AtomContent-atomContent = tagName' "content" contentAttrs handler-  where contentAttrs = (,) <$> optional (textAttr "type") <*> optional (attr "src" asURIReference) <* ignoreAttrs-        handler (Just "xhtml", _) = AtomContentInlineXHTML <$> tagIgnoreAttrs' "div" textContent-        handler (ctype, Just uri) = return $ AtomContentOutOfLine (fromMaybe mempty ctype) uri-        handler (Just "html", _) = AtomContentInlineText TypeHTML <$> textContent-        handler (Nothing, _) = AtomContentInlineText TypeText <$> textContent-        handler (Just ctype, _) = AtomContentInlineOther ctype <$> textContent+atomContent :: MonadThrow m => ConduitM Event o m (Maybe AtomContent)+atomContent = tagName' "content" contentAttrs handler where+  contentAttrs = (,) <$> optional (requireAttr "type") <*> optional (requireAttr "src" >>= asURIReference) <* ignoreAttrs+  handler (Just "xhtml", _) = AtomContentInlineXHTML <$> force "<div>" (tagIgnoreAttrs "{http://www.w3.org/1999/xhtml}div" xhtmlContent)+  handler (ctype, Just uri) = return $ AtomContentOutOfLine (fromMaybe mempty ctype) uri+  handler (Just "html", _) = AtomContentInlineText TypeHTML <$> content+  handler (Nothing, _) = AtomContentInlineText TypeText <$> content+  handler (Just ctype, _) = AtomContentInlineOther ctype <$> content  -- | Parse an @atom:link@ element. -- Examples:@@ -161,15 +178,15 @@ -- > <link rel="self" href="/feed" /> -- -- > <link rel="alternate" href="/blog/1234"/>-atomLink :: (MonadCatch m) => ConduitParser Event m AtomLink+atomLink :: MonadThrow m => ConduitM Event o m (Maybe AtomLink) atomLink = tagName' "link" linkAttrs $ \(href, rel, ltype, lang, title, length') ->   return $ AtomLink href rel ltype lang title length'-  where linkAttrs = (,,,,,) <$> attr "href" asURIReference-                            <*> (textAttr "rel" <|> pure mempty)-                            <*> (textAttr "type" <|> pure mempty)-                            <*> (textAttr "hreflang" <|> pure mempty)-                            <*> (textAttr "title" <|> pure mempty)-                            <*> (textAttr "length" <|> pure mempty)+  where linkAttrs = (,,,,,) <$> (requireAttr "href" >>= asURIReference)+                            <*> (requireAttr "rel" <|> pure mempty)+                            <*> (requireAttr "type" <|> pure mempty)+                            <*> (requireAttr "hreflang" <|> pure mempty)+                            <*> (requireAttr "title" <|> pure mempty)+                            <*> (requireAttr "length" <|> pure mempty)                             <* ignoreAttrs  -- | Parse an Atom text construct.@@ -186,18 +203,11 @@ -- >     AT&amp;T bought <b>by SBC</b>! -- >   </div> -- > </title>-atomText :: (MonadCatch m) => Text -> ConduitParser Event m AtomText-atomText name = named ("Atom text construct <" <> name <> ">") $ tagName' name (optional (textAttr "type") <* ignoreAttrs) handler-  where handler (Just "xhtml") = AtomXHTMLText <$> tagIgnoreAttrs' "div" xhtmlContent-        handler (Just "html") = AtomPlainText TypeHTML <$> textContent-        handler _ = AtomPlainText TypeText <$> textContent-        xhtmlContent :: MonadCatch m => ConduitParser Event m Text-        xhtmlContent = mconcat <$> many (textContent <|> anyTag (\name attrs -> renderTag name attrs <$> xhtmlContent))-        renderTag name attrs content = "<" <> nameLocalName name <> renderAttrs attrs <> ">" <> content <> "</" <> nameLocalName name <> ">"-        renderAttrs [] = ""-        renderAttrs ((name, content):t) = " " <> nameLocalName name <> "=\"" <> mconcat (renderContent <$> content) <> "\"" <> renderAttrs t-        renderContent (ContentText t) = t-        renderContent (ContentEntity t) = t+atomText :: MonadThrow m => Text -> ConduitM Event o m (Maybe AtomText)+atomText name = tagName' name (optional (requireAttr "type") <* ignoreAttrs) handler+  where handler (Just "xhtml") = AtomXHTMLText <$> force "<div>" (tagIgnoreAttrs' "div" xhtmlContent)+        handler (Just "html") = AtomPlainText TypeHTML <$> content+        handler _ = AtomPlainText TypeText <$> content  -- | Parse an @atom:generator@ element. -- Example:@@ -205,9 +215,9 @@ -- > <generator uri="/myblog.php" version="1.0"> -- >   Example Toolkit -- > </generator>-atomGenerator :: (MonadCatch m) => ConduitParser Event m AtomGenerator-atomGenerator = tagName' "generator" generatorAttrs $ \(uri, version) -> AtomGenerator uri version <$> (asNonNull =<< textContent)-  where generatorAttrs = (,) <$> optional (attr "uri" asURIReference) <*> (textAttr "version" <|> pure mempty) <* ignoreAttrs+atomGenerator :: MonadThrow m => ConduitM Event o m (Maybe AtomGenerator)+atomGenerator = tagName' "generator" generatorAttrs $ \(uri, version) -> AtomGenerator uri version <$> (asNonNull =<< content)+  where generatorAttrs = (,) <$> optional (requireAttr "uri" >>= asURIReference) <*> (requireAttr "version" <|> pure mempty) <* ignoreAttrs   data SourcePiece = SourceAuthor AtomPerson@@ -222,7 +232,6 @@                  | SourceSubtitle AtomText                  | SourceTitle AtomText                  | SourceUpdated UTCTime-                 | SourceUnknown  makeTraversals ''SourcePiece @@ -235,44 +244,41 @@ -- >   <updated>2003-12-13T18:30:02Z</updated> -- >   <rights>© 2005 Example, Inc.</rights> -- > </source>-atomSource :: (MonadCatch m) => ConduitParser Event m AtomSource-atomSource = named "Atom <source> element" $ tagIgnoreAttrs' "source" $ do-  p <- many piece-  flip foldM p $ AtomSource-    <$> generalize (handles _SourceAuthor list)-    <*> generalize (handles _SourceCategory list)-    <*> generalize (handles _SourceContributor list)-    <*> generalize (handles _SourceGenerator last)-    <*> generalize (handles _SourceIcon last)-    <*> generalize (handles _SourceId $ lastDef "")-    <*> generalize (handles _SourceLink list)-    <*> generalize (handles _SourceLogo last)-    <*> generalize (handles _SourceRights last)-    <*> generalize (handles _SourceSubtitle last)-    <*> generalize (handles _SourceTitle last)-    <*> generalize (handles _SourceUpdated last)-  where piece :: (MonadCatch m) => ConduitParser Event m SourcePiece-        piece = choice [ SourceAuthor <$> atomPerson "author"-                       , SourceCategory <$> atomCategory-                       , SourceContributor <$> atomPerson "contributor"-                       , SourceGenerator <$> atomGenerator-                       , SourceIcon <$> atomIcon-                       , SourceId . toNullable <$> atomId-                       , SourceLink <$> atomLink-                       , SourceLogo <$> atomLogo-                       , SourceRights <$> atomText "rights"-                       , SourceSubtitle <$> atomText "subtitle"-                       , SourceTitle <$> atomText "title"-                       , SourceUpdated <$> tagDate "updated"-                       , SourceUnknown <$ unknownTag-                       ]+atomSource :: MonadThrow m => ConduitM Event o m (Maybe AtomSource)+atomSource = tagIgnoreAttrs' "source" $ manyYield' (choose piece) =$= zipConduit where+  zipConduit = getZipConduit $ AtomSource+    <$> ZipConduit (projectC _SourceAuthor =$= sinkList)+    <*> ZipConduit (projectC _SourceCategory =$= sinkList)+    <*> ZipConduit (projectC _SourceContributor =$= sinkList)+    <*> ZipConduit (projectC _SourceGenerator =$= headC)+    <*> ZipConduit (projectC _SourceIcon =$= headC)+    <*> ZipConduit (projectC _SourceId =$= headDefC "")+    <*> ZipConduit (projectC _SourceLink =$= sinkList)+    <*> ZipConduit (projectC _SourceLogo =$= headC)+    <*> ZipConduit (projectC _SourceRights =$= headC)+    <*> ZipConduit (projectC _SourceSubtitle =$= headC)+    <*> ZipConduit (projectC _SourceTitle =$= headC)+    <*> ZipConduit (projectC _SourceUpdated =$= headC)+  piece = [ fmap SourceAuthor <$> atomPerson "author"+          , fmap SourceCategory <$> atomCategory+          , fmap SourceContributor <$> atomPerson "contributor"+          , fmap SourceGenerator <$> atomGenerator+          , fmap SourceIcon <$> atomIcon+          , fmap SourceId <$> atomId+          , fmap SourceLink <$> atomLink+          , fmap SourceLogo <$> atomLogo+          , fmap SourceRights <$> atomText "rights"+          , fmap SourceSubtitle <$> atomText "subtitle"+          , fmap SourceTitle <$> atomText "title"+          , fmap SourceUpdated <$> tagDate "updated"+          ]   data EntryPiece = EntryAuthor      AtomPerson                 | EntryCategory    AtomCategory                 | EntryContent     AtomContent                 | EntryContributor AtomPerson-                | EntryId          (NonNull Text)+                | EntryId          Text                 | EntryLink        AtomLink                 | EntryPublished   UTCTime                 | EntryRights      AtomText@@ -280,42 +286,38 @@                 | EntrySummary     AtomText                 | EntryTitle       AtomText                 | EntryUpdated     UTCTime-                | EntryUnknown  makeTraversals ''EntryPiece  -- | Parse an @atom:entry@ element.-atomEntry :: (MonadCatch m) => ConduitParser Event m AtomEntry-atomEntry = named "Atom <entry> element" $ tagIgnoreAttrs' "entry" $ do-  p <- many piece-  flip foldM p $ AtomEntry-    <$> generalize (handles _EntryAuthor list)-    <*> generalize (handles _EntryCategory list)-    <*> generalize (handles _EntryContent last)-    <*> generalize (handles _EntryContributor list)-    <*> handlesM _EntryId (lastRequired "Missing or invalid <id> element.")-    <*> generalize (handles _EntryLink list)-    <*> generalize (handles _EntryPublished last)-    <*> generalize (handles _EntryRights last)-    <*> generalize (handles _EntrySource last)-    <*> generalize (handles _EntrySummary last)-    <*> handlesM _EntryTitle (lastRequired "Missing or invalid <title> element.")-    <*> handlesM _EntryUpdated (lastRequired "Missing or invalid <updated> element.")-  where piece :: (MonadCatch m) => ConduitParser Event m EntryPiece-        piece = choice [ EntryAuthor <$> atomPerson "author"-                       , EntryCategory <$> atomCategory-                       , EntryContent <$> atomContent-                       , EntryContributor <$> atomPerson "contributor"-                       , EntryId <$> atomId-                       , EntryLink <$> atomLink-                       , EntryPublished <$> tagDate "published"-                       , EntryRights <$> atomText "rights"-                       , EntrySource <$> atomSource-                       , EntrySummary <$> atomText "summary"-                       , EntryTitle <$> atomText "title"-                       , EntryUpdated <$> tagDate "updated"-                       , EntryUnknown <$ unknownTag-                       ]+atomEntry :: MonadThrow m => ConduitM Event o m (Maybe AtomEntry)+atomEntry = tagIgnoreAttrs' "entry" $ manyYield' (choose piece) =$= zipConduit where+  zipConduit = getZipConduit $ AtomEntry+    <$> ZipConduit (projectC _EntryAuthor =$= sinkList)+    <*> ZipConduit (projectC _EntryCategory =$= sinkList)+    <*> ZipConduit (projectC _EntryContent =$= headC)+    <*> ZipConduit (projectC _EntryContributor =$= sinkList)+    <*> ZipConduit (projectC _EntryId =$= headRequiredC "Missing <id> element")+    <*> ZipConduit (projectC _EntryLink =$= sinkList)+    <*> ZipConduit (projectC _EntryPublished =$= headC)+    <*> ZipConduit (projectC _EntryRights =$= headC)+    <*> ZipConduit (projectC _EntrySource =$= headC)+    <*> ZipConduit (projectC _EntrySummary =$= headC)+    <*> ZipConduit (projectC _EntryTitle =$= headRequiredC "Missing or invalid <title> element.")+    <*> ZipConduit (projectC _EntryUpdated =$= headRequiredC "Missing or invalid <updated> element.")+  piece = [ fmap EntryAuthor <$> atomPerson "author"+          , fmap EntryCategory <$> atomCategory+          , fmap EntryContent <$> atomContent+          , fmap EntryContributor <$> atomPerson "contributor"+          , fmap EntryId <$> atomId+          , fmap EntryLink <$> atomLink+          , fmap EntryPublished <$> tagDate "published"+          , fmap EntryRights <$> atomText "rights"+          , fmap EntrySource <$> atomSource+          , fmap EntrySummary <$> atomText "summary"+          , fmap EntryTitle <$> atomText "title"+          , fmap EntryUpdated <$> tagDate "updated"+          ]   data FeedPiece = FeedAuthor AtomPerson@@ -324,48 +326,44 @@                | FeedEntry AtomEntry                | FeedGenerator AtomGenerator                | FeedIcon AtomURI-               | FeedId (NonNull Text)+               | FeedId Text                | FeedLink AtomLink                | FeedLogo AtomURI                | FeedRights AtomText                | FeedSubtitle AtomText                | FeedTitle AtomText                | FeedUpdated UTCTime-               | FeedUnknown  makeTraversals ''FeedPiece  -- | Parse an @atom:feed@ element.-atomFeed :: (MonadCatch m) => ConduitParser Event m AtomFeed-atomFeed = named "Atom <feed> element" $ tagIgnoreAttrs' "feed" $ do-  p <- many piece-  flip foldM p $ AtomFeed-    <$> generalize (handles _FeedAuthor list)-    <*> generalize (handles _FeedCategory list)-    <*> generalize (handles _FeedContributor list)-    <*> generalize (handles _FeedEntry list)-    <*> generalize (handles _FeedGenerator last)-    <*> generalize (handles _FeedIcon last)-    <*> handlesM _FeedId (lastRequired "Missing or empty <id> element.")-    <*> generalize (handles _FeedLink list)-    <*> generalize (handles _FeedLogo last)-    <*> generalize (handles _FeedRights last)-    <*> generalize (handles _FeedSubtitle last)-    <*> handlesM _FeedTitle (lastRequired "Missing <title> element.")-    <*> handlesM _FeedUpdated (lastRequired "Missing <updated> element.")-  where piece :: MonadCatch m => ConduitParser Event m FeedPiece-        piece = choice [ FeedAuthor <$> atomPerson "author"-                       , FeedCategory <$> atomCategory-                       , FeedContributor <$> atomPerson "contributor"-                       , FeedEntry <$> atomEntry-                       , FeedGenerator <$> atomGenerator-                       , FeedIcon <$> atomIcon-                       , FeedId <$> atomId-                       , FeedLink <$> atomLink-                       , FeedLogo <$> atomLogo-                       , FeedRights <$> atomText "rights"-                       , FeedSubtitle <$> atomText "subtitle"-                       , FeedTitle <$> atomText "title"-                       , FeedUpdated <$> tagDate "updated"-                       , FeedUnknown <$ unknownTag-                       ]+atomFeed :: MonadThrow m => ConduitM Event o m (Maybe AtomFeed)+atomFeed = tagIgnoreAttrs' "feed" $ manyYield' (choose piece) =$= zipConduit where+  zipConduit = getZipConduit $ AtomFeed+    <$> ZipConduit (projectC _FeedAuthor =$= sinkList)+    <*> ZipConduit (projectC _FeedCategory =$= sinkList)+    <*> ZipConduit (projectC _FeedContributor =$= sinkList)+    <*> ZipConduit (projectC _FeedEntry =$= sinkList)+    <*> ZipConduit (projectC _FeedGenerator =$= headC)+    <*> ZipConduit (projectC _FeedIcon =$= headC)+    <*> ZipConduit (projectC _FeedId =$= headRequiredC "Missing <id> element")+    <*> ZipConduit (projectC _FeedLink =$= sinkList)+    <*> ZipConduit (projectC _FeedLogo =$= headC)+    <*> ZipConduit (projectC _FeedRights =$= headC)+    <*> ZipConduit (projectC _FeedSubtitle =$= headC)+    <*> ZipConduit (projectC _FeedTitle =$= headRequiredC "Missing <title> element.")+    <*> ZipConduit (projectC _FeedUpdated =$= headRequiredC "Missing <updated> element.")+  piece = [ fmap FeedAuthor <$> atomPerson "author"+          , fmap FeedCategory <$> atomCategory+          , fmap FeedContributor <$> atomPerson "contributor"+          , fmap FeedEntry <$> atomEntry+          , fmap FeedGenerator <$> atomGenerator+          , fmap FeedIcon <$> atomIcon+          , fmap FeedId <$> atomId+          , fmap FeedLink <$> atomLink+          , fmap FeedLogo <$> atomLogo+          , fmap FeedRights <$> atomText "rights"+          , fmap FeedSubtitle <$> atomText "subtitle"+          , fmap FeedTitle <$> atomText "title"+          , fmap FeedUpdated <$> tagDate "updated"+          ]
Text/Atom/Conduit/Render.hs view
@@ -49,7 +49,7 @@   forM_ (f^.feedEntriesL) renderAtomEntry   forM_ (f^.feedGeneratorL) renderAtomGenerator   forM_ (feedIcon f) $ tag "icon" mempty . content . decodeUtf8 . withAtomURI serializeURIRef'-  tag "id" mempty . content . toNullable $ f^.feedIdL+  tag "id" mempty . content $ f^.feedIdL   forM_ (f^.feedLinksL) renderAtomLink   forM_ (feedLogo f) $ tag "logo" mempty . content . decodeUtf8 . withAtomURI serializeURIRef'   forM_ (f^.feedRightsL) $ renderAtomText "rights"@@ -64,7 +64,7 @@   forM_ (e^.entryCategoriesL) renderAtomCategory   forM_ (e^.entryContentL) renderAtomContent   forM_ (e^.entryContributorsL) $ renderAtomPerson "contributor"-  tag "id" mempty . content . toNullable $ e^.entryIdL+  tag "id" mempty . content $ e^.entryIdL   forM_ (e^.entryLinksL) renderAtomLink   forM_ (e^.entryPublishedL) $ dateTag "published"   forM_ (e^.entryRightsL) $ renderAtomText "rights"
Text/Atom/Types.hs view
@@ -62,7 +62,7 @@  -- | An atom text construct. data AtomText = AtomPlainText TextType Text-              | AtomXHTMLText Text+              | AtomXHTMLText Text -- ^ XHTML special characters will be in encoded form  deriving instance Eq AtomText deriving instance Generic AtomText@@ -153,7 +153,7 @@   , entryCategories   :: [AtomCategory]   , entryContent      :: Maybe AtomContent   , entryContributors :: [AtomPerson]-  , entryId           :: NonNull Text+  , entryId           :: Text   , entryLinks        :: [AtomLink]   , entryPublished    :: Maybe UTCTime   , entryRights       :: Maybe AtomText@@ -175,7 +175,7 @@   , feedEntries      :: [AtomEntry]   , feedGenerator    :: Maybe AtomGenerator   , feedIcon         :: Maybe AtomURI-  , feedId           :: NonNull Text+  , feedId           :: Text   , feedLinks        :: [AtomLink]   , feedLogo         :: Maybe AtomURI   , feedRights       :: Maybe AtomText
atom-conduit.cabal view
@@ -1,8 +1,8 @@ name:                atom-conduit-version:             0.3.1.2+version:             0.4.0.0 synopsis:            Streaming parser/renderer for the Atom 1.0 standard (RFC 4287). description:         Please refer to README.-license:             OtherLicense+license:             PublicDomain license-file:        LICENSE author:              koral maintainer:          koral att mailoo dott org@@ -24,10 +24,10 @@   -- other-modules:   build-depends:       base >= 4.8 && < 5+    , blaze-builder     , conduit-    , conduit-parse-    , exceptions-    , foldl+    , conduit-combinators >= 1.0.5+    , safe-exceptions     , lens-simple     , mono-traversable >= 1.0.0.1     , parsers@@ -35,8 +35,7 @@     , time >= 1.5     , timerep >= 2.0     , uri-bytestring >= 0.2-    , xml-conduit >= 1.3-    , xml-conduit-parse >= 0.3+    , xml-conduit >= 1.4     , xml-types   default-language:    Haskell2010 @@ -47,9 +46,8 @@   build-depends: atom-conduit,                  base >= 4.8,                  conduit,-                 conduit-parse,                  data-default,-                 exceptions,+                 safe-exceptions,                  hlint,                  lens-simple,                  mono-traversable >= 1.0.0.1,@@ -62,7 +60,6 @@                  time >= 1.5,                  text,                  uri-bytestring >= 0.2,-                 xml-conduit >= 1.3,-                 xml-conduit-parse >= 0.3,+                 xml-conduit >= 1.4,                  xml-types   default-language:    Haskell2010
test/Main.hs view
@@ -1,14 +1,11 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} import           Control.Monad-import           Control.Monad.Catch.Pure import           Control.Monad.Trans.Resource  import           Data.Char import           Data.Conduit import           Data.Conduit.List-import           Data.Conduit.Parser-import           Data.Conduit.Parser.XML      as XML import           Data.Default import           Data.Functor.Identity import           Data.Monoid@@ -22,6 +19,7 @@ import           Lens.Simple  import qualified Language.Haskell.HLint       as HLint (hlint)+ import           Test.QuickCheck.Instances import           Test.Tasty import           Test.Tasty.HUnit@@ -32,13 +30,14 @@ import           Text.Atom.Lens import           Text.Atom.Types import           Text.Parser.Combinators+import qualified Text.XML.Stream.Parse        as XML  import           URI.ByteString  main :: IO () main = defaultMain $ testGroup "Tests"   [ unitTests-  , properties+--  , properties   , hlint   ] @@ -67,14 +66,14 @@  linkCase :: TestTree linkCase = testCase "Link element" $ do-  result <- runResourceT . runConduit $ sourceList input =$= XML.parseText def =$= runConduitParser atomLink+  result <- runResourceT . runConduit $ sourceList input =$= XML.parseText' def =$= XML.force "Invalid <link>" atomLink   result ^. linkHrefL @?= AtomURI (RelativeRef Nothing "/feed" (Query []) Nothing)   (result ^. linkRelL) @?= "self"   where input = ["<link rel=\"self\" href=\"/feed\" />"]  personCase :: TestTree personCase = testCase "Person construct" $ do-  result <- runResourceT . runConduit $ sourceList input =$= XML.parseText def =$= runConduitParser (atomPerson "author")+  result <- runResourceT . runConduit $ sourceList input =$= XML.parseText' def =$= XML.force "Invalid <author>" (atomPerson "author")   toNullable (result ^. personNameL) @?= "John Doe"   result ^. personEmailL @?= "JohnDoe@example.com"   result ^. personUriL @?= Just (AtomURI $ URI (Scheme "http") (Just $ Authority Nothing (Host "example.com") Nothing) "/~johndoe" (Query []) Nothing)@@ -88,7 +87,7 @@  generatorCase :: TestTree generatorCase = testCase "Generator element" $ do-  result <- runResourceT . runConduit $ sourceList input =$= XML.parseText def =$= runConduitParser atomGenerator+  result <- runResourceT . runConduit $ sourceList input =$= XML.parseText' def =$= XML.force "Invalid <generator>" atomGenerator   result ^. generatorUriL @?= Just (AtomURI $ RelativeRef Nothing "/myblog.php" (Query []) Nothing)   (result ^. generatorVersionL) @?= "1.0"   toNullable (result ^. generatorContentL) @?= "Example Toolkit"@@ -100,7 +99,7 @@  sourceCase :: TestTree sourceCase = testCase "Source element" $ do-  result <- runResourceT . runConduit $ sourceList input =$= XML.parseText def =$= runConduitParser atomSource+  result <- runResourceT . runConduit $ sourceList input =$= XML.parseText' def =$= XML.force "Invalid <source>" atomSource   (result ^. sourceIdL) @?= "http://example.org/"   (result ^. sourceTitleL) @?= Just (AtomPlainText TypeText "Fourty-Two")   show <$> (result ^. sourceUpdatedL) @?= Just "2003-12-13 18:30:02 UTC"@@ -116,30 +115,30 @@  textConstructCase :: TestTree textConstructCase = testCase "Text construct" $ do-  (a, b, c) <- runResourceT . runConduit $ sourceList input =$= XML.parseText def =$= runConduitParser ((,,) <$> atomText "title1" <*> atomText "title2" <*> atomText "title3")+  a:b:c:_ <- runResourceT . runConduit $ sourceList input =$= XML.parseText' def =$= XML.many (atomText "title")   a @?= AtomPlainText TypeText "AT&T bought by SBC!"   b @?= AtomPlainText TypeHTML "AT&amp;T bought <b>by SBC</b>!"-  c @?= AtomXHTMLText "AT&T bought <b>by SBC</b>!"+  c @?= AtomXHTMLText "AT&amp;T bought <b xmlns=\"http://www.w3.org/1999/xhtml\"><em>by SBC</em></b>!"   where input =-          [ "<title1 type=\"text\">AT&amp;T bought by SBC!</title1>"-          , "<title2 type=\"html\">"+          [ "<title type=\"text\">AT&amp;T bought by SBC!</title>"+          , "<title type=\"html\">"           , "AT&amp;amp;T bought &lt;b&gt;by SBC&lt;/b&gt;!"-          , "</title2>"-          , "<title3 type=\"xhtml\">"+          , "</title>"+          , "<title type=\"xhtml\">"           , "<div xmlns=\"http://www.w3.org/1999/xhtml\">"-          , "AT&amp;T bought <b>by SBC</b>!"+          , "AT&amp;T bought <b><em>by SBC</em></b>!"           , "</div>"-          , "</title3>"+          , "</title>"           ]  simpleCase :: TestTree simpleCase = testCase "Simple case" $ do-  result <- runResourceT . runConduit $ sourceList input =$= XML.parseText def =$= runConduitParser atomFeed+  result <- runResourceT . runConduit $ sourceList input =$= XML.parseText' def =$= XML.force "Invalid <feed>" atomFeed   return ()   where input =           [ "<?xml version=\"1.0\" encoding=\"utf-8\"?>"           , "<feed xmlns=\"http://www.w3.org/2005/Atom\">"-          , "<title>Example Feed</title>"+          , "<title type=\"text\">&lt;em&gt;Example&lt;/em&gt; Feed</title>"           , "<link href=\"http://example.org/\"/>"           , "<updated>2003-12-13T18:30:02Z</updated>"           , "<author>"@@ -164,31 +163,31 @@   roundtripAtomTextProperty :: TestTree-roundtripAtomTextProperty = testProperty "parse . render = id (AtomText)" $ \i -> either (const False) (i ==) (runIdentity . runCatchT . runConduit $ renderAtomText "test" i =$= runConduitParser (atomText "test"))+roundtripAtomTextProperty = testProperty "parse . render = id (AtomText)" $ \i -> either (const False) (Just i ==) (runConduit $ renderAtomText "test" i =$= atomText "test")  roundtripAtomPersonProperty :: TestTree-roundtripAtomPersonProperty = testProperty "parse . render = id (AtomPerson)" $ \i -> either (const False) (i ==) (runIdentity . runCatchT . runConduit $ renderAtomPerson "test" i =$= runConduitParser (atomPerson "test"))+roundtripAtomPersonProperty = testProperty "parse . render = id (AtomPerson)" $ \i -> either (const False) (i ==) (runConduit $ renderAtomPerson "test" i =$= XML.force "Invalid <test>" (atomPerson "test"))  roundtripAtomCategoryProperty :: TestTree-roundtripAtomCategoryProperty = testProperty "parse . render = id (AtomCategory)" $ \i -> either (const False) (i ==) (runIdentity . runCatchT . runConduit $ renderAtomCategory i =$= runConduitParser atomCategory)+roundtripAtomCategoryProperty = testProperty "parse . render = id (AtomCategory)" $ \i -> either (const False) (i ==) (runConduit $ renderAtomCategory i =$= XML.force "Invalid <category>" atomCategory)  roundtripAtomLinkProperty :: TestTree-roundtripAtomLinkProperty = testProperty "parse . render = id (AtomLink)" $ \i -> either (const False) (i ==) (runIdentity . runCatchT . runConduit $ renderAtomLink i =$= runConduitParser atomLink)+roundtripAtomLinkProperty = testProperty "parse . render = id (AtomLink)" $ \i -> either (const False) (i ==) (runConduit $ renderAtomLink i =$= XML.force "Invalid <link>" atomLink)  roundtripAtomGeneratorProperty :: TestTree-roundtripAtomGeneratorProperty = testProperty "parse . render = id (AtomGenerator)" $ \i -> either (const False) (i ==) (runIdentity . runCatchT . runConduit $ renderAtomGenerator i =$= runConduitParser atomGenerator)+roundtripAtomGeneratorProperty = testProperty "parse . render = id (AtomGenerator)" $ \i -> either (const False) (i ==) (runConduit $ renderAtomGenerator i =$= XML.force "Invalid <generator>" atomGenerator)  roundtripAtomSourceProperty :: TestTree-roundtripAtomSourceProperty = testProperty "parse . render = id (AtomSource)" $ \i -> either (const False) (i ==) (runIdentity . runCatchT . runConduit $ renderAtomSource i =$= runConduitParser atomSource)+roundtripAtomSourceProperty = testProperty "parse . render = id (AtomSource)" $ \i -> either (const False) (i ==) (runConduit $ renderAtomSource i =$= XML.force "Invalid <source>" atomSource)  roundtripAtomContentProperty :: TestTree-roundtripAtomContentProperty = testProperty "parse . render = id (AtomContent)" $ \i -> either (const False) (i ==) (runIdentity . runCatchT . runConduit $ renderAtomContent i =$= runConduitParser atomContent)+roundtripAtomContentProperty = testProperty "parse . render = id (AtomContent)" $ \i -> either (const False) (i ==) (runConduit $ renderAtomContent i =$= XML.force "Invalid content" atomContent)  roundtripAtomFeedProperty :: TestTree-roundtripAtomFeedProperty = testProperty "parse . render = id (AtomFeed)" $ \i -> either (const False) (i ==) (runIdentity . runCatchT . runConduit $ renderAtomFeed i =$= runConduitParser atomFeed)+roundtripAtomFeedProperty = testProperty "parse . render = id (AtomFeed)" $ \i -> either (const False) (i ==) (runConduit $ renderAtomFeed i =$= XML.force "Invalid <feed>" atomFeed)  roundtripAtomEntryProperty :: TestTree-roundtripAtomEntryProperty = testProperty "parse . render = id (AtomEntry)" $ \i -> either (const False) (i ==) (runIdentity . runCatchT . runConduit $ renderAtomEntry i =$= runConduitParser atomEntry)+roundtripAtomEntryProperty = testProperty "parse . render = id (AtomEntry)" $ \i -> either (const False) (i ==) (runConduit $ renderAtomEntry i =$= XML.force "Invalid <entry>" atomEntry)   letter = choose ('a', 'z')