rss-conduit 0.5.1.1 → 0.6.0.0
raw patch · 9 files changed
+230/−191 lines, 9 filesdep +microlensdep +microlens-thdep +template-haskelldep −lens-simpledep ~xml-conduitPVP ok
version bump matches the API change (PVP)
Dependencies added: microlens, microlens-th, template-haskell
Dependencies removed: lens-simple
Dependency ranges changed: xml-conduit
API changes (from Hackage documentation)
- Text.RSS.Lens: categoryDomainL :: Functor f => (Text -> f Text) -> RssCategory -> f RssCategory
+ Text.RSS.Lens: categoryDomainL :: Lens' RssCategory Text
- Text.RSS.Lens: categoryNameL :: Functor f => (Text -> f Text) -> RssCategory -> f RssCategory
+ Text.RSS.Lens: categoryNameL :: Lens' RssCategory Text
- Text.RSS.Lens: channelCategoriesL :: Traversal' (RssDocument e) RssCategory
+ Text.RSS.Lens: channelCategoriesL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) [RssCategory]
- Text.RSS.Lens: channelCloudL :: Functor f => (Maybe RssCloud -> f (Maybe RssCloud)) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelCloudL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) (Maybe RssCloud)
- Text.RSS.Lens: channelCopyrightL :: Functor f => (Text -> f Text) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelCopyrightL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) Text
- Text.RSS.Lens: channelDescriptionL :: Functor f => (Text -> f Text) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelDescriptionL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) Text
- Text.RSS.Lens: channelDocsL :: Functor f => (Maybe RssURI -> f (Maybe RssURI)) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelDocsL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) (Maybe RssURI)
- Text.RSS.Lens: channelExtensionsL :: Functor f => (RssChannelExtension extensions -> f (RssChannelExtension extensions)) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelExtensionsL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) (RssChannelExtension extensions_afpr)
- Text.RSS.Lens: channelGeneratorL :: Functor f => (Text -> f Text) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelGeneratorL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) Text
- Text.RSS.Lens: channelImageL :: Functor f => (Maybe RssImage -> f (Maybe RssImage)) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelImageL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) (Maybe RssImage)
- Text.RSS.Lens: channelItemsL :: Traversal' (RssDocument e) (RssItem e)
+ Text.RSS.Lens: channelItemsL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) [RssItem extensions_afpr]
- Text.RSS.Lens: channelLanguageL :: Functor f => (Text -> f Text) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelLanguageL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) Text
- Text.RSS.Lens: channelLastBuildDateL :: Functor f => (Maybe UTCTime -> f (Maybe UTCTime)) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelLastBuildDateL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) (Maybe UTCTime)
- Text.RSS.Lens: channelLinkL :: Functor f => (RssURI -> f RssURI) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelLinkL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) RssURI
- Text.RSS.Lens: channelManagingEditorL :: Functor f => (Text -> f Text) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelManagingEditorL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) Text
- Text.RSS.Lens: channelPubDateL :: Functor f => (Maybe UTCTime -> f (Maybe UTCTime)) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelPubDateL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) (Maybe UTCTime)
- Text.RSS.Lens: channelRatingL :: Functor f => (Text -> f Text) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelRatingL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) Text
- Text.RSS.Lens: channelSkipDaysL :: Functor f => (Set Day -> f (Set Day)) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelSkipDaysL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) (Set Day)
- Text.RSS.Lens: channelSkipHoursL :: Functor f => (Set Hour -> f (Set Hour)) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelSkipHoursL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) (Set Hour)
- Text.RSS.Lens: channelTextInputL :: Functor f => (Maybe RssTextInput -> f (Maybe RssTextInput)) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelTextInputL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) (Maybe RssTextInput)
- Text.RSS.Lens: channelTitleL :: Functor f => (Text -> f Text) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelTitleL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) Text
- Text.RSS.Lens: channelTtlL :: Functor f => (Maybe Int -> f (Maybe Int)) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelTtlL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) (Maybe Int)
- Text.RSS.Lens: channelWebmasterL :: Functor f => (Text -> f Text) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: channelWebmasterL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) Text
- Text.RSS.Lens: cloudProtocolL :: Functor f => (CloudProtocol -> f CloudProtocol) -> RssCloud -> f RssCloud
+ Text.RSS.Lens: cloudProtocolL :: Lens' RssCloud CloudProtocol
- Text.RSS.Lens: cloudRegisterProcedureL :: Functor f => (Text -> f Text) -> RssCloud -> f RssCloud
+ Text.RSS.Lens: cloudRegisterProcedureL :: Lens' RssCloud Text
- Text.RSS.Lens: cloudUriL :: Functor f => (RssURI -> f RssURI) -> RssCloud -> f RssCloud
+ Text.RSS.Lens: cloudUriL :: Lens' RssCloud RssURI
- Text.RSS.Lens: documentVersionL :: Functor f => (Version -> f Version) -> RssDocument extensions -> f (RssDocument extensions)
+ Text.RSS.Lens: documentVersionL :: forall extensions_afpr. Lens' (RssDocument extensions_afpr) Version
- Text.RSS.Lens: enclosureLengthL :: Functor f => (Int -> f Int) -> RssEnclosure -> f RssEnclosure
+ Text.RSS.Lens: enclosureLengthL :: Lens' RssEnclosure Int
- Text.RSS.Lens: enclosureTypeL :: Functor f => (Text -> f Text) -> RssEnclosure -> f RssEnclosure
+ Text.RSS.Lens: enclosureTypeL :: Lens' RssEnclosure Text
- Text.RSS.Lens: enclosureUrlL :: Functor f => (RssURI -> f RssURI) -> RssEnclosure -> f RssEnclosure
+ Text.RSS.Lens: enclosureUrlL :: Lens' RssEnclosure RssURI
- Text.RSS.Lens: imageDescriptionL :: Functor f => (Text -> f Text) -> RssImage -> f RssImage
+ Text.RSS.Lens: imageDescriptionL :: Lens' RssImage Text
- Text.RSS.Lens: imageHeightL :: Functor f => (Maybe Int -> f (Maybe Int)) -> RssImage -> f RssImage
+ Text.RSS.Lens: imageHeightL :: Lens' RssImage (Maybe Int)
- Text.RSS.Lens: imageLinkL :: Functor f => (RssURI -> f RssURI) -> RssImage -> f RssImage
+ Text.RSS.Lens: imageLinkL :: Lens' RssImage RssURI
- Text.RSS.Lens: imageTitleL :: Functor f => (Text -> f Text) -> RssImage -> f RssImage
+ Text.RSS.Lens: imageTitleL :: Lens' RssImage Text
- Text.RSS.Lens: imageUriL :: Functor f => (RssURI -> f RssURI) -> RssImage -> f RssImage
+ Text.RSS.Lens: imageUriL :: Lens' RssImage RssURI
- Text.RSS.Lens: imageWidthL :: Functor f => (Maybe Int -> f (Maybe Int)) -> RssImage -> f RssImage
+ Text.RSS.Lens: imageWidthL :: Lens' RssImage (Maybe Int)
- Text.RSS.Lens: itemAuthorL :: Functor f => (Text -> f Text) -> RssItem extensions -> f (RssItem extensions)
+ Text.RSS.Lens: itemAuthorL :: forall extensions_aftx. Lens' (RssItem extensions_aftx) Text
- Text.RSS.Lens: itemCategoriesL :: Traversal' (RssItem e) RssCategory
+ Text.RSS.Lens: itemCategoriesL :: forall extensions_aftx. Lens' (RssItem extensions_aftx) [RssCategory]
- Text.RSS.Lens: itemCommentsL :: Functor f => (Maybe RssURI -> f (Maybe RssURI)) -> RssItem extensions -> f (RssItem extensions)
+ Text.RSS.Lens: itemCommentsL :: forall extensions_aftx. Lens' (RssItem extensions_aftx) (Maybe RssURI)
- Text.RSS.Lens: itemDescriptionL :: Functor f => (Text -> f Text) -> RssItem extensions -> f (RssItem extensions)
+ Text.RSS.Lens: itemDescriptionL :: forall extensions_aftx. Lens' (RssItem extensions_aftx) Text
- Text.RSS.Lens: itemEnclosureL :: Traversal' (RssItem e) RssEnclosure
+ Text.RSS.Lens: itemEnclosureL :: forall extensions_aftx. Lens' (RssItem extensions_aftx) [RssEnclosure]
- Text.RSS.Lens: itemExtensionsL :: Functor f => (RssItemExtension extensions1 -> f (RssItemExtension extensions2)) -> RssItem extensions1 -> f (RssItem extensions2)
+ Text.RSS.Lens: itemExtensionsL :: forall extensions_aftx extensions_azY8. Lens (RssItem extensions_aftx) (RssItem extensions_azY8) (RssItemExtension extensions_aftx) (RssItemExtension extensions_azY8)
- Text.RSS.Lens: itemGuidL :: Functor f => (Maybe RssGuid -> f (Maybe RssGuid)) -> RssItem extensions -> f (RssItem extensions)
+ Text.RSS.Lens: itemGuidL :: forall extensions_aftx. Lens' (RssItem extensions_aftx) (Maybe RssGuid)
- Text.RSS.Lens: itemLinkL :: Functor f => (Maybe RssURI -> f (Maybe RssURI)) -> RssItem extensions -> f (RssItem extensions)
+ Text.RSS.Lens: itemLinkL :: forall extensions_aftx. Lens' (RssItem extensions_aftx) (Maybe RssURI)
- Text.RSS.Lens: itemPubDateL :: Functor f => (Maybe UTCTime -> f (Maybe UTCTime)) -> RssItem extensions -> f (RssItem extensions)
+ Text.RSS.Lens: itemPubDateL :: forall extensions_aftx. Lens' (RssItem extensions_aftx) (Maybe UTCTime)
- Text.RSS.Lens: itemSourceL :: Functor f => (Maybe RssSource -> f (Maybe RssSource)) -> RssItem extensions -> f (RssItem extensions)
+ Text.RSS.Lens: itemSourceL :: forall extensions_aftx. Lens' (RssItem extensions_aftx) (Maybe RssSource)
- Text.RSS.Lens: itemTitleL :: Functor f => (Text -> f Text) -> RssItem extensions -> f (RssItem extensions)
+ Text.RSS.Lens: itemTitleL :: forall extensions_aftx. Lens' (RssItem extensions_aftx) Text
- Text.RSS.Lens: sourceNameL :: Functor f => (Text -> f Text) -> RssSource -> f RssSource
+ Text.RSS.Lens: sourceNameL :: Lens' RssSource Text
- Text.RSS.Lens: sourceUrlL :: Functor f => (RssURI -> f RssURI) -> RssSource -> f RssSource
+ Text.RSS.Lens: sourceUrlL :: Lens' RssSource RssURI
- Text.RSS.Lens: textInputDescriptionL :: Functor f => (Text -> f Text) -> RssTextInput -> f RssTextInput
+ Text.RSS.Lens: textInputDescriptionL :: Lens' RssTextInput Text
- Text.RSS.Lens: textInputLinkL :: Functor f => (RssURI -> f RssURI) -> RssTextInput -> f RssTextInput
+ Text.RSS.Lens: textInputLinkL :: Lens' RssTextInput RssURI
- Text.RSS.Lens: textInputNameL :: Functor f => (Text -> f Text) -> RssTextInput -> f RssTextInput
+ Text.RSS.Lens: textInputNameL :: Lens' RssTextInput Text
- Text.RSS.Lens: textInputTitleL :: Functor f => (Text -> f Text) -> RssTextInput -> f RssTextInput
+ Text.RSS.Lens: textInputTitleL :: Lens' RssTextInput Text
Files
- rss-conduit.cabal +7/−3
- src/Text/RSS/Conduit/Parse.hs +92/−65
- src/Text/RSS/Conduit/Render.hs +6/−5
- src/Text/RSS/Extensions/DublinCore.hs +34/−23
- src/Text/RSS/Extensions/Syndication.hs +10/−7
- src/Text/RSS/Lens.hs +10/−39
- src/Text/RSS/Lens/Rules.hs +12/−0
- src/Text/RSS1/Conduit/Parse.hs +57/−47
- test/Main.hs +2/−2
rss-conduit.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: rss-conduit-version: 0.5.1.1+version: 0.6.0.0 build-type: Simple license: CC0-1.0 license-file: LICENSE@@ -42,6 +42,8 @@ Text.RSS.Extensions.Syndication Text.RSS.Lens Text.RSS.Types+ other-modules:+ Text.RSS.Lens.Rules build-depends: atom-conduit >=0.5, conduit >= 1.3,@@ -49,9 +51,11 @@ containers -any, dublincore-xml-conduit -any, safe-exceptions -any,- lens-simple -any,+ microlens -any,+ microlens-th -any, safe -any, text -any,+ template-haskell -any, time >=1.5, timerep >=2.0, uri-bytestring >=0.2,@@ -73,7 +77,7 @@ data-default -any, dublincore-xml-conduit -any, filepath -any,- lens-simple -any,+ microlens -any, mono-traversable -any, resourcet -any, safe-exceptions -any,
src/Text/RSS/Conduit/Parse.hs view
@@ -44,7 +44,8 @@ import Data.Time.RFC822 import Data.Version import Data.XML.Types-import Lens.Simple+import Lens.Micro+import Lens.Micro.TH import Safe import Text.ParserCombinators.ReadP (readP_to_S) import Text.Read (readMaybe)@@ -73,7 +74,7 @@ asBool t = throwM $ InvalidBool t asVersion :: MonadThrow m => Text -> m Version-asVersion t = maybe (throwM $ InvalidVersion t) return . fmap fst . headMay . readP_to_S parseVersion $ unpack t+asVersion t = maybe (throwM $ InvalidVersion t) (return . fst) . headMay . readP_to_S parseVersion $ unpack t asCloudProtocol :: MonadThrow m => Text -> m CloudProtocol asCloudProtocol "xml-rpc" = return ProtocolXmlRpc@@ -94,7 +95,7 @@ headRequiredC :: MonadThrow m => Text -> ConduitT a b m a headRequiredC e = maybe (throw $ MissingElement e) return =<< headC -projectC :: Monad m => Fold a a' b b' -> ConduitT a b m ()+projectC :: Monad m => Traversal' a b -> ConduitT a b m () projectC prism = fix $ \recurse -> do item <- await case (item, item ^? (_Just . prism)) of@@ -115,19 +116,21 @@ fromList <$> (manyYield' (tagIgnoreAttrs "day" $ content >>= asDay) .| sinkList) -data TextInputPiece = TextInputTitle Text | TextInputDescription Text- | TextInputName Text | TextInputLink RssURI+data TextInputPiece = TextInputTitle { __textInputTitle :: Text }+ | TextInputDescription { __textInputDescription :: Text }+ | TextInputName { __textInputName :: Text }+ | TextInputLink { __textInputLink :: RssURI } -makeTraversals ''TextInputPiece+makeLenses ''TextInputPiece -- | Parse a @\<textInput\>@ element. rssTextInput :: MonadThrow m => ConduitM Event o m (Maybe RssTextInput) rssTextInput = tagIgnoreAttrs "textInput" $ (manyYield' (choose piece) .| parser) <* many ignoreAnyTreeContent where parser = getZipConduit $ RssTextInput- <$> ZipConduit (projectC _TextInputTitle .| headRequiredC "Missing <title> element")- <*> ZipConduit (projectC _TextInputDescription .| headRequiredC "Missing <description> element")- <*> ZipConduit (projectC _TextInputName .| headRequiredC "Missing <name> element")- <*> ZipConduit (projectC _TextInputLink .| headRequiredC "Missing <link> element")+ <$> ZipConduit (projectC _textInputTitle .| headRequiredC "Missing <title> element")+ <*> ZipConduit (projectC _textInputDescription .| headRequiredC "Missing <description> element")+ <*> ZipConduit (projectC _textInputName .| headRequiredC "Missing <name> element")+ <*> ZipConduit (projectC _textInputLink .| headRequiredC "Missing <link> element") piece = [ fmap TextInputTitle <$> tagIgnoreAttrs "title" content , fmap TextInputDescription <$> tagIgnoreAttrs "description" content , fmap TextInputName <$> tagIgnoreAttrs "name" content@@ -136,21 +139,25 @@ -data ImagePiece = ImageUri RssURI | ImageTitle Text | ImageLink RssURI | ImageWidth Int- | ImageHeight Int | ImageDescription Text+data ImagePiece = ImageUri { __imageUri :: RssURI }+ | ImageTitle { __imageTitle :: Text }+ | ImageLink { __imageLink :: RssURI }+ | ImageWidth { __imageWidth :: Int }+ | ImageHeight { __imageHeight :: Int }+ | ImageDescription { __imageDescription :: Text } -makeTraversals ''ImagePiece+makeLenses ''ImagePiece -- | Parse an @\<image\>@ element. rssImage :: (MonadThrow m) => ConduitM Event o m (Maybe RssImage) rssImage = tagIgnoreAttrs "image" $ (manyYield' (choose piece) .| parser) <* many ignoreAnyTreeContent where parser = getZipConduit $ RssImage- <$> ZipConduit (projectC _ImageUri .| headRequiredC "Missing <url> element")- <*> ZipConduit (projectC _ImageTitle .| headDefC "Unnamed image") -- Lenient- <*> ZipConduit (projectC _ImageLink .| headDefC nullURI) -- Lenient- <*> ZipConduit (projectC _ImageWidth .| headC)- <*> ZipConduit (projectC _ImageHeight .| headC)- <*> ZipConduit (projectC _ImageDescription .| headDefC "")+ <$> ZipConduit (projectC _imageUri .| headRequiredC "Missing <url> element")+ <*> ZipConduit (projectC _imageTitle .| headDefC "Unnamed image") -- Lenient+ <*> ZipConduit (projectC _imageLink .| headDefC nullURI) -- Lenient+ <*> ZipConduit (projectC _imageWidth .| headC)+ <*> ZipConduit (projectC _imageHeight .| headC)+ <*> ZipConduit (projectC _imageDescription .| headDefC "") piece = [ fmap ImageUri <$> tagIgnoreAttrs "url" (content >>= asRssURI) , fmap ImageTitle <$> tagIgnoreAttrs "title" content , fmap ImageLink <$> tagIgnoreAttrs "link" (content >>= asRssURI)@@ -195,12 +202,19 @@ handler uri = RssSource uri <$> content -data ItemPiece = ItemTitle Text | ItemLink RssURI | ItemDescription Text- | ItemAuthor Text | ItemCategory RssCategory | ItemComments RssURI- | ItemEnclosure RssEnclosure | ItemGuid RssGuid | ItemPubDate UTCTime- | ItemSource RssSource | ItemOther (NonEmpty Event)+data ItemPiece = ItemTitle { __itemTitle :: Text }+ | ItemLink { __itemLink :: RssURI }+ | ItemDescription { __itemDescription :: Text }+ | ItemAuthor { __itemAuthor :: Text }+ | ItemCategory { __itemCategory :: RssCategory }+ | ItemComments { __itemComments :: RssURI }+ | ItemEnclosure { __itemEnclosure :: RssEnclosure }+ | ItemGuid { __itemGuid :: RssGuid }+ | ItemPubDate { __itemPubDate :: UTCTime }+ | ItemSource { __itemSource :: RssSource }+ | ItemOther { __itemOther :: NonEmpty Event } -makeTraversals ''ItemPiece+makeLenses ''ItemPiece -- | Parse an @\<item\>@ element. --@@ -208,17 +222,17 @@ rssItem :: ParseRssExtension e => MonadThrow m => ConduitM Event o m (Maybe (RssItem e)) rssItem = tagIgnoreAttrs "item" $ (manyYield' (choose piece) .| parser) <* many ignoreAnyTreeContent where parser = getZipConduit $ RssItem- <$> ZipConduit (projectC _ItemTitle .| headDefC "")- <*> ZipConduit (projectC _ItemLink .| headC)- <*> ZipConduit (projectC _ItemDescription .| headDefC "")- <*> ZipConduit (projectC _ItemAuthor .| headDefC "")- <*> ZipConduit (projectC _ItemCategory .| sinkList)- <*> ZipConduit (projectC _ItemComments .| headC)- <*> ZipConduit (projectC _ItemEnclosure .| sinkList)- <*> ZipConduit (projectC _ItemGuid .| headC)- <*> ZipConduit (projectC _ItemPubDate .| headC)- <*> ZipConduit (projectC _ItemSource .| headC)- <*> ZipConduit (projectC _ItemOther .| concatC .| parseRssItemExtension)+ <$> ZipConduit (projectC _itemTitle .| headDefC "")+ <*> ZipConduit (projectC _itemLink .| headC)+ <*> ZipConduit (projectC _itemDescription .| headDefC "")+ <*> ZipConduit (projectC _itemAuthor .| headDefC "")+ <*> ZipConduit (projectC _itemCategory .| sinkList)+ <*> ZipConduit (projectC _itemComments .| headC)+ <*> ZipConduit (projectC _itemEnclosure .| sinkList)+ <*> ZipConduit (projectC _itemGuid .| headC)+ <*> ZipConduit (projectC _itemPubDate .| headC)+ <*> ZipConduit (projectC _itemSource .| headC)+ <*> ZipConduit (projectC _itemOther .| concatC .| parseRssItemExtension) piece = [ fmap ItemTitle <$> tagIgnoreAttrs "title" content , fmap ItemLink <$> tagIgnoreAttrs "link" (content >>= asRssURI) , fmap ItemDescription <$> tagIgnoreAttrs "description" content@@ -233,16 +247,29 @@ ] -data ChannelPiece e = ChannelTitle Text | ChannelLink RssURI | ChannelDescription Text- | ChannelItem (RssItem e) | ChannelLanguage Text | ChannelCopyright Text- | ChannelManagingEditor Text | ChannelWebmaster Text | ChannelPubDate UTCTime- | ChannelLastBuildDate UTCTime | ChannelCategory RssCategory- | ChannelGenerator Text | ChannelDocs RssURI | ChannelCloud RssCloud- | ChannelTtl Int | ChannelImage RssImage | ChannelRating Text- | ChannelTextInput RssTextInput | ChannelSkipHours (Set Hour)- | ChannelSkipDays (Set Day) | ChannelOther (NonEmpty Event)+data ChannelPiece e = ChannelTitle { __channelTitle :: Text }+ | ChannelLink { __channelLink :: RssURI }+ | ChannelDescription { __channelDescription :: Text }+ | ChannelItem { __channelItem :: RssItem e }+ | ChannelLanguage { __channelLanguage :: Text }+ | ChannelCopyright { __channelCopyright :: Text }+ | ChannelManagingEditor { __channelManagingEditor :: Text }+ | ChannelWebmaster { __channelWebmaster :: Text }+ | ChannelPubDate { __channelPubDate :: UTCTime }+ | ChannelLastBuildDate { __channelLastBuildDate :: UTCTime }+ | ChannelCategory { __channelCategory :: RssCategory }+ | ChannelGenerator { __channelGenerator :: Text }+ | ChannelDocs { __channelDocs :: RssURI }+ | ChannelCloud { __channelCloud :: RssCloud }+ | ChannelTtl { __channelTtl :: Int }+ | ChannelImage { __channelImage :: RssImage }+ | ChannelRating { __channelRating :: Text }+ | ChannelTextInput { __channelTextInput :: RssTextInput }+ | ChannelSkipHours { __channelSkipHours :: Set Hour }+ | ChannelSkipDays { __channelSkipDays :: Set Day }+ | ChannelOther { __channelOther :: NonEmpty Event } -makeTraversals ''ChannelPiece+makeLenses ''ChannelPiece -- | Parse an @\<rss\>@ element. --@@ -250,27 +277,27 @@ rssDocument :: ParseRssExtension e => MonadThrow m => ConduitM Event o m (Maybe (RssDocument e)) rssDocument = tagName' "rss" attributes $ \version -> force "Missing <channel>" $ tagIgnoreAttrs "channel" (manyYield' (choose piece) .| parser version) <* many ignoreAnyTreeContent where parser version = getZipConduit $ RssDocument version- <$> ZipConduit (projectC _ChannelTitle .| headRequiredC "Missing <title> element")- <*> ZipConduit (projectC _ChannelLink .| headRequiredC "Missing <link> element")- <*> ZipConduit (projectC _ChannelDescription .| headDefC "") -- Lenient- <*> ZipConduit (projectC _ChannelItem .| sinkList)- <*> ZipConduit (projectC _ChannelLanguage .| headDefC "")- <*> ZipConduit (projectC _ChannelCopyright .| headDefC "")- <*> ZipConduit (projectC _ChannelManagingEditor .| headDefC "")- <*> ZipConduit (projectC _ChannelWebmaster .| headDefC "")- <*> ZipConduit (projectC _ChannelPubDate .| headC)- <*> ZipConduit (projectC _ChannelLastBuildDate .| headC)- <*> ZipConduit (projectC _ChannelCategory .| sinkList)- <*> ZipConduit (projectC _ChannelGenerator .| headDefC "")- <*> ZipConduit (projectC _ChannelDocs .| headC)- <*> ZipConduit (projectC _ChannelCloud .| headC)- <*> ZipConduit (projectC _ChannelTtl .| headC)- <*> ZipConduit (projectC _ChannelImage .| headC)- <*> ZipConduit (projectC _ChannelRating .| headDefC "")- <*> ZipConduit (projectC _ChannelTextInput .| headC)- <*> ZipConduit (projectC _ChannelSkipHours .| headDefC mempty)- <*> ZipConduit (projectC _ChannelSkipDays .| headDefC mempty)- <*> ZipConduit (projectC _ChannelOther .| concatC .| parseRssChannelExtension)+ <$> ZipConduit (projectC _channelTitle .| headRequiredC "Missing <title> element")+ <*> ZipConduit (projectC _channelLink .| headRequiredC "Missing <link> element")+ <*> ZipConduit (projectC _channelDescription .| headDefC "") -- Lenient+ <*> ZipConduit (projectC _channelItem .| sinkList)+ <*> ZipConduit (projectC _channelLanguage .| headDefC "")+ <*> ZipConduit (projectC _channelCopyright .| headDefC "")+ <*> ZipConduit (projectC _channelManagingEditor .| headDefC "")+ <*> ZipConduit (projectC _channelWebmaster .| headDefC "")+ <*> ZipConduit (projectC _channelPubDate .| headC)+ <*> ZipConduit (projectC _channelLastBuildDate .| headC)+ <*> ZipConduit (projectC _channelCategory .| sinkList)+ <*> ZipConduit (projectC _channelGenerator .| headDefC "")+ <*> ZipConduit (projectC _channelDocs .| headC)+ <*> ZipConduit (projectC _channelCloud .| headC)+ <*> ZipConduit (projectC _channelTtl .| headC)+ <*> ZipConduit (projectC _channelImage .| headC)+ <*> ZipConduit (projectC _channelRating .| headDefC "")+ <*> ZipConduit (projectC _channelTextInput .| headC)+ <*> ZipConduit (projectC _channelSkipHours .| headDefC mempty)+ <*> ZipConduit (projectC _channelSkipDays .| headDefC mempty)+ <*> ZipConduit (projectC _channelOther .| concatC .| parseRssChannelExtension) piece = [ fmap ChannelTitle <$> tagIgnoreAttrs "title" content , fmap ChannelLink <$> tagIgnoreAttrs "link" (content >>= asRssURI) , fmap ChannelDescription <$> tagIgnoreAttrs "description" content
src/Text/RSS/Conduit/Render.hs view
@@ -33,7 +33,8 @@ import Data.Time.RFC822 import Data.Version import Data.XML.Types-import Lens.Simple+import Lens.Micro+import Lens.Micro.Extras import Safe import Text.XML.Stream.Render import URI.ByteString@@ -52,7 +53,7 @@ optionalTextTag "webMaster" $ d^.channelWebmasterL forM_ (d^.channelPubDateL) $ dateTag "pubDate" forM_ (d^.channelLastBuildDateL) $ dateTag "lastBuildDate"- forM_ (d^..channelCategoriesL) renderRssCategory+ forM_ (d^.channelCategoriesL) renderRssCategory optionalTextTag "generator" $ d^.channelGeneratorL forM_ (d^.channelDocsL) $ textTag "docs" . renderRssURI forM_ (d^.channelCloudL) renderRssCloud@@ -62,7 +63,7 @@ forM_ (d^.channelTextInputL) renderRssTextInput renderRssSkipHours $ d^.channelSkipHoursL renderRssSkipDays $ d^.channelSkipDaysL- forM_ (d^..channelItemsL) renderRssItem+ forM_ (d^.channelItemsL) renderRssItem renderRssChannelExtension $ d^.channelExtensionsL -- | Render an @\<item\>@ element.@@ -72,9 +73,9 @@ forM_ (i^.itemLinkL) $ textTag "link" . renderRssURI optionalTextTag "description" $ i^.itemDescriptionL optionalTextTag "author" $ i^.itemAuthorL- forM_ (i^..itemCategoriesL) renderRssCategory+ forM_ (i^.itemCategoriesL) renderRssCategory forM_ (i^.itemCommentsL) $ textTag "comments" . renderRssURI- forM_ (i^..itemEnclosureL) renderRssEnclosure+ forM_ (i^.itemEnclosureL) renderRssEnclosure forM_ (i^.itemGuidL) renderRssGuid forM_ (i^.itemPubDateL) $ dateTag "pubDate" forM_ (i^.itemSourceL) renderRssSource
src/Text/RSS/Extensions/DublinCore.hs view
@@ -33,7 +33,8 @@ import Data.Time.RFC3339 import Data.XML.Types import GHC.Generics-import Lens.Simple+import Lens.Micro+import Lens.Micro.TH import qualified Text.XML.DublinCore.Conduit.Parse as DC import Text.XML.DublinCore.Conduit.Render import Text.XML.Stream.Parse@@ -41,7 +42,7 @@ -- }}} -- {{{ Utils-projectC :: Monad m => Fold a a' b b' -> ConduitT a b m ()+projectC :: Monad m => Traversal' a b -> ConduitT a b m () projectC prism = fix $ \recurse -> do item <- await case (item, item ^? (_Just . prism)) of@@ -73,33 +74,43 @@ mkDcMetaData = DcMetaData mempty mempty mempty Nothing mempty mempty mempty mempty mempty mempty mempty mempty mempty mempty mempty -data ElementPiece = ElementContributor Text | ElementCoverage Text | ElementCreator Text- | ElementDate UTCTime | ElementDescription Text | ElementFormat Text- | ElementIdentifier Text | ElementLanguage Text | ElementPublisher Text- | ElementRelation Text | ElementRights Text | ElementSource Text- | ElementSubject Text | ElementTitle Text | ElementType Text+data ElementPiece = ElementContributor { __elementContributor :: Text }+ | ElementCoverage { __elementCoverage :: Text }+ | ElementCreator { __elementCreator :: Text }+ | ElementDate { __elementDate :: UTCTime }+ | ElementDescription { __elementDescription :: Text }+ | ElementFormat { __elementFormat :: Text }+ | ElementIdentifier { __elementIdentifier :: Text }+ | ElementLanguage { __elementLanguage :: Text }+ | ElementPublisher { __elementPublisher :: Text }+ | ElementRelation { __elementRelation :: Text }+ | ElementRights { __elementRights :: Text }+ | ElementSource { __elementSource :: Text }+ | ElementSubject { __elementSubject :: Text }+ | ElementTitle { __elementTitle :: Text }+ | ElementType { __elementType :: Text } -makeTraversals ''ElementPiece+makeLenses ''ElementPiece -- | Parse a set of Dublin Core metadata elements. dcMetadata :: MonadThrow m => ConduitT Event o m DcMetaData dcMetadata = manyYield' (choose piece) .| parser where parser = getZipConduit $ DcMetaData- <$> ZipConduit (projectC _ElementContributor .| headDefC "")- <*> ZipConduit (projectC _ElementCoverage .| headDefC "")- <*> ZipConduit (projectC _ElementCreator .| headDefC "")- <*> ZipConduit (projectC _ElementDate .| headC)- <*> ZipConduit (projectC _ElementDescription .| headDefC "")- <*> ZipConduit (projectC _ElementFormat .| headDefC "")- <*> ZipConduit (projectC _ElementIdentifier .| headDefC "")- <*> ZipConduit (projectC _ElementLanguage .| headDefC "")- <*> ZipConduit (projectC _ElementPublisher .| headDefC "")- <*> ZipConduit (projectC _ElementRelation .| headDefC "")- <*> ZipConduit (projectC _ElementRights .| headDefC "")- <*> ZipConduit (projectC _ElementSource .| headDefC "")- <*> ZipConduit (projectC _ElementSubject .| headDefC "")- <*> ZipConduit (projectC _ElementTitle .| headDefC "")- <*> ZipConduit (projectC _ElementType .| headDefC "")+ <$> ZipConduit (projectC _elementContributor .| headDefC "")+ <*> ZipConduit (projectC _elementCoverage .| headDefC "")+ <*> ZipConduit (projectC _elementCreator .| headDefC "")+ <*> ZipConduit (projectC _elementDate .| headC)+ <*> ZipConduit (projectC _elementDescription .| headDefC "")+ <*> ZipConduit (projectC _elementFormat .| headDefC "")+ <*> ZipConduit (projectC _elementIdentifier .| headDefC "")+ <*> ZipConduit (projectC _elementLanguage .| headDefC "")+ <*> ZipConduit (projectC _elementPublisher .| headDefC "")+ <*> ZipConduit (projectC _elementRelation .| headDefC "")+ <*> ZipConduit (projectC _elementRights .| headDefC "")+ <*> ZipConduit (projectC _elementSource .| headDefC "")+ <*> ZipConduit (projectC _elementSubject .| headDefC "")+ <*> ZipConduit (projectC _elementTitle .| headDefC "")+ <*> ZipConduit (projectC _elementType .| headDefC "") piece = [ fmap ElementContributor <$> DC.elementContributor , fmap ElementCoverage <$> DC.elementCoverage , fmap ElementCreator <$> DC.elementCreator
src/Text/RSS/Extensions/Syndication.hs view
@@ -51,7 +51,8 @@ import Data.Time.RFC822 import Data.XML.Types import GHC.Generics-import Lens.Simple+import Lens.Micro+import Lens.Micro.TH import Text.Read import Text.XML.Stream.Parse import qualified Text.XML.Stream.Render as Render@@ -69,7 +70,7 @@ asInt :: MonadThrow m => Text -> m Int asInt t = maybe (throwM $ InvalidInt t) return . readMaybe $ unpack t -projectC :: Monad m => Fold a a' b b' -> ConduitT a b m ()+projectC :: Monad m => Traversal' a b -> ConduitT a b m () projectC prism = fix $ \recurse -> do item <- await case (item, item ^? (_Just . prism)) of@@ -132,17 +133,19 @@ mkSyndicationInfo = SyndicationInfo mzero mzero mzero -data ElementPiece = ElementPeriod SyndicationPeriod | ElementFrequency Int | ElementBase UTCTime+data ElementPiece = ElementPeriod { __elementPeriod :: SyndicationPeriod }+ | ElementFrequency { __elementFrequency :: Int }+ | ElementBase { __elementBase :: UTCTime } -makeTraversals ''ElementPiece+makeLenses ''ElementPiece -- | Parse all __Syndication__ elements. syndicationInfo :: MonadThrow m => ConduitT Event o m SyndicationInfo syndicationInfo = manyYield' (choose piece) .| parser where parser = getZipConduit $ SyndicationInfo- <$> ZipConduit (projectC _ElementPeriod .| headC)- <*> ZipConduit (projectC _ElementFrequency .| headC)- <*> ZipConduit (projectC _ElementBase .| headC)+ <$> ZipConduit (projectC _elementPeriod .| headC)+ <*> ZipConduit (projectC _elementFrequency .| headC)+ <*> ZipConduit (projectC _elementBase .| headC) piece = [ fmap ElementPeriod <$> syndicationPeriod , fmap ElementFrequency <$> syndicationFrequency , fmap ElementBase <$> syndicationBase
src/Text/RSS/Lens.hs view
@@ -3,47 +3,18 @@ module Text.RSS.Lens (module Text.RSS.Lens) where -- {{{ Imports+import Text.RSS.Lens.Rules import Text.RSS.Types -import Lens.Simple+import Lens.Micro.TH import URI.ByteString -- }}} --$(makeLensesBy (\n -> Just (n ++ "L")) ''RssCategory)-$(makeLensesBy (\n -> Just (n ++ "L")) ''RssEnclosure)-$(makeLensesBy (\n -> Just (n ++ "L")) ''RssSource)--$(makeLensesBy- (let f "itemCategories" = Nothing- f "itemEnclosure" = Nothing- f n = Just (n ++ "L")- in f)- ''RssItem)--itemCategoriesL :: Traversal' (RssItem e) RssCategory-itemCategoriesL inj a@RssItem { itemCategories = c } = (\x -> a { itemCategories = c }) <$> traverse inj c-{-# INLINE itemCategoriesL #-}--itemEnclosureL :: Traversal' (RssItem e) RssEnclosure-itemEnclosureL inj a@RssItem { itemEnclosure = e } = (\x -> a { itemEnclosure = e }) <$> traverse inj e-{-# INLINE itemEnclosureL #-}---$(makeLensesBy (\n -> Just (n ++ "L")) ''RssTextInput)-$(makeLensesBy (\n -> Just (n ++ "L")) ''RssCloud)-$(makeLensesBy (\n -> Just (n ++ "L")) ''RssImage)-$(makeLensesBy- (let f "channelItems" = Nothing- f "channelCategories" = Nothing- f n = Just (n ++ "L")- in f)- ''RssDocument)--channelItemsL :: Traversal' (RssDocument e) (RssItem e)-channelItemsL inj a@RssDocument { channelItems = i } = (\x -> a { channelItems = i }) <$> traverse inj i-{-# INLINE channelItemsL #-}--channelCategoriesL :: Traversal' (RssDocument e) RssCategory-channelCategoriesL inj a@RssDocument { channelCategories = c } = (\x -> a { channelCategories = c }) <$> traverse inj c-{-# INLINE channelCategoriesL #-}+makeLensesWith rules ''RssCategory+makeLensesWith rules ''RssEnclosure+makeLensesWith rules ''RssSource+makeLensesWith rules ''RssItem+makeLensesWith rules ''RssTextInput+makeLensesWith rules ''RssCloud+makeLensesWith rules ''RssImage+makeLensesWith rules ''RssDocument
+ src/Text/RSS/Lens/Rules.hs view
@@ -0,0 +1,12 @@+{-# LANGUAGE FlexibleContexts #-}+module Text.RSS.Lens.Rules where++-- {{{ Imports+import Language.Haskell.TH.Syntax+import Lens.Micro+import Lens.Micro.TH+-- }}}++rules :: LensRules+rules = set lensField f lensRules where+ f _ _ name = [TopName $ mkName (nameBase name <> "L")]
src/Text/RSS1/Conduit/Parse.hs view
@@ -32,7 +32,8 @@ import Data.Time.RFC3339 import Data.Version import Data.XML.Types-import Lens.Simple+import Lens.Micro+import Lens.Micro.TH import Text.XML.Stream.Parse import URI.ByteString -- }}}@@ -55,7 +56,7 @@ headRequiredC :: MonadThrow m => Text -> ConduitT a b m a headRequiredC e = maybe (throw $ MissingElement e) return =<< headC -projectC :: Monad m => Fold a a' b b' -> ConduitT a b m ()+projectC :: Monad m => Traversal' a b -> ConduitT a b m () projectC prism = fix $ \recurse -> do item <- await case (item, item ^? (_Just . prism)) of@@ -90,19 +91,21 @@ -- }}} -data TextInputPiece = TextInputTitle Text | TextInputDescription Text- | TextInputName Text | TextInputLink RssURI+data TextInputPiece = TextInputTitle { __textInputTitle :: Text }+ | TextInputDescription { __textInputDescription :: Text }+ | TextInputName { __textInputName :: Text }+ | TextInputLink { __textInputLink :: RssURI } -makeTraversals ''TextInputPiece+makeLenses ''TextInputPiece -- | Parse a @\<textinput\>@ element. rss1TextInput :: MonadThrow m => ConduitM Event o m (Maybe RssTextInput) rss1TextInput = rss1Tag "textinput" attributes $ \uri -> (manyYield' (choose piece) .| parser uri) <* many ignoreAnyTreeContent where parser uri = getZipConduit $ RssTextInput- <$> ZipConduit (projectC _TextInputTitle .| headRequiredC "Missing <title> element")- <*> ZipConduit (projectC _TextInputDescription .| headRequiredC "Missing <description> element")- <*> ZipConduit (projectC _TextInputName .| headRequiredC "Missing <name> element")- <*> ZipConduit (projectC _TextInputLink .| headDefC uri) -- Lenient+ <$> ZipConduit (projectC _textInputTitle .| headRequiredC "Missing <title> element")+ <*> ZipConduit (projectC _textInputDescription .| headRequiredC "Missing <description> element")+ <*> ZipConduit (projectC _textInputName .| headRequiredC "Missing <name> element")+ <*> ZipConduit (projectC _textInputLink .| headDefC uri) -- Lenient piece = [ fmap TextInputTitle <$> rss1Tag "title" ignoreAttrs (const content) , fmap TextInputDescription <$> rss1Tag "description" ignoreAttrs (const content) , fmap TextInputName <$> rss1Tag "name" ignoreAttrs (const content)@@ -111,10 +114,15 @@ attributes = (requireAttr (rdfName "about") >>= asRssURI) <* ignoreAttrs -data ItemPiece = ItemTitle Text | ItemLink RssURI | ItemDescription Text | ItemCreator Text- | ItemDate UTCTime | ItemContent Text | ItemOther (NonEmpty Event)+data ItemPiece = ItemTitle { __itemTitle :: Text }+ | ItemLink { __itemLink :: RssURI }+ | ItemDescription { __itemDescription :: Text }+ | ItemCreator { __itemCreator :: Text }+ | ItemDate { __itemDate :: UTCTime }+ | ItemContent { __itemContent :: Text }+ | ItemOther { __itemOther :: NonEmpty Event } -makeTraversals ''ItemPiece+makeLenses ''ItemPiece -- | Parse an @\<item\>@ element. --@@ -122,17 +130,17 @@ rss1Item :: ParseRssExtension e => MonadCatch m => ConduitM Event o m (Maybe (RssItem e)) rss1Item = rss1Tag "item" attributes $ \uri -> (manyYield' (choose piece) .| parser uri) <* many ignoreAnyTreeContent where parser uri = getZipConduit $ RssItem- <$> ZipConduit (projectC _ItemTitle .| headDefC mempty)- <*> (Just <$> ZipConduit (projectC _ItemLink .| headDefC uri))- <*> ZipConduit (projectC _ItemDescription .| headDefC mempty)- <*> ZipConduit (projectC _ItemCreator .| headDefC mempty)+ <$> ZipConduit (projectC _itemTitle .| headDefC mempty)+ <*> (Just <$> ZipConduit (projectC _itemLink .| headDefC uri))+ <*> ZipConduit (projectC _itemDescription .| headDefC mempty)+ <*> ZipConduit (projectC _itemCreator .| headDefC mempty) <*> pure mempty <*> pure mzero <*> pure mempty <*> pure mzero- <*> ZipConduit (projectC _ItemDate .| headC)+ <*> ZipConduit (projectC _itemDate .| headC) <*> pure mzero- <*> ZipConduit (projectC _ItemOther .| concatC .| parseRssItemExtension)+ <*> ZipConduit (projectC _itemOther .| concatC .| parseRssItemExtension) piece = [ fmap ItemTitle <$> rss1Tag "title" ignoreAttrs (const content) , fmap ItemLink <$> rss1Tag "link" ignoreAttrs (const $ content >>= asRssURI) , fmap ItemDescription <$> (rss1Tag "description" ignoreAttrs (const content) `orE` contentTag "encoded" ignoreAttrs (const content))@@ -143,17 +151,19 @@ attributes = (requireAttr (rdfName "about") >>= asRssURI) <* ignoreAttrs -data ImagePiece = ImageUri RssURI | ImageTitle Text | ImageLink RssURI+data ImagePiece = ImageUri { __imageUri :: RssURI }+ | ImageTitle { __imageTitle :: Text }+ | ImageLink { __imageLink :: RssURI } -makeTraversals ''ImagePiece+makeLenses ''ImagePiece -- | Parse an @\<image\>@ element. rss1Image :: (MonadThrow m) => ConduitM Event o m (Maybe RssImage) rss1Image = rss1Tag "image" attributes $ \uri -> (manyYield' (choose piece) .| parser uri) <* many ignoreAnyTreeContent where parser uri = getZipConduit $ RssImage- <$> ZipConduit (projectC _ImageUri .| headDefC uri) -- Lenient- <*> ZipConduit (projectC _ImageTitle .| headDefC "Unnamed image") -- Lenient- <*> ZipConduit (projectC _ImageLink .| headDefC nullURI) -- Lenient+ <$> ZipConduit (projectC _imageUri .| headDefC uri) -- Lenient+ <*> ZipConduit (projectC _imageTitle .| headDefC "Unnamed image") -- Lenient+ <*> ZipConduit (projectC _imageLink .| headDefC nullURI) -- Lenient <*> pure mzero <*> pure mzero <*> pure mempty@@ -181,15 +191,15 @@ , channelExtensions' :: RssChannelExtension extensions } -data ChannelPiece = ChannelTitle Text- | ChannelLink RssURI- | ChannelDescription Text- | ChannelImage RssImage- | ChannelItems [Text]- | ChannelTextInput RssURI- | ChannelOther (NonEmpty Event)+data ChannelPiece = ChannelTitle { __channelTitle :: Text }+ | ChannelLink { __channelLink :: RssURI }+ | ChannelDescription { __channelDescription :: Text }+ | ChannelImage { __channelImage :: RssImage }+ | ChannelItems { __channelItems :: [Text] }+ | ChannelTextInput { __channelTextInput :: RssURI }+ | ChannelOther { __channelOther :: NonEmpty Event } -makeTraversals ''ChannelPiece+makeLenses ''ChannelPiece -- | Parse a @\<channel\>@ element.@@ -198,13 +208,13 @@ rss1Channel :: ParseRssExtension e => MonadThrow m => ConduitM Event o m (Maybe (Rss1Channel e)) rss1Channel = rss1Tag "channel" attributes $ \channelId -> (manyYield' (choose piece) .| parser channelId) <* many ignoreAnyTreeContent where parser channelId = getZipConduit $ Rss1Channel channelId- <$> ZipConduit (projectC _ChannelTitle .| headRequiredC "Missing <title> element")- <*> ZipConduit (projectC _ChannelLink .| headRequiredC "Missing <link> element")- <*> ZipConduit (projectC _ChannelDescription .| headDefC "") -- Lenient- <*> ZipConduit (projectC _ChannelItems .| concatC .| sinkList)- <*> ZipConduit (projectC _ChannelImage .| headC)- <*> ZipConduit (projectC _ChannelTextInput .| headC)- <*> ZipConduit (projectC _ChannelOther .| concatC .| parseRssChannelExtension)+ <$> ZipConduit (projectC _channelTitle .| headRequiredC "Missing <title> element")+ <*> ZipConduit (projectC _channelLink .| headRequiredC "Missing <link> element")+ <*> ZipConduit (projectC _channelDescription .| headDefC "") -- Lenient+ <*> ZipConduit (projectC _channelItems .| concatC .| sinkList)+ <*> ZipConduit (projectC _channelImage .| headC)+ <*> ZipConduit (projectC _channelTextInput .| headC)+ <*> ZipConduit (projectC _channelOther .| concatC .| parseRssChannelExtension) piece = [ fmap ChannelTitle <$> rss1Tag "title" ignoreAttrs (const content) , fmap ChannelLink <$> rss1Tag "link" ignoreAttrs (const $ content >>= asRssURI) , fmap ChannelDescription <$> rss1Tag "description" ignoreAttrs (const content)@@ -243,12 +253,12 @@ mempty (channelExtensions' channel) -data DocumentPiece e = DocumentChannel (Rss1Channel e)- | DocumentImage RssImage- | DocumentItem (RssItem e)- | DocumentTextInput RssTextInput+data DocumentPiece e = DocumentChannel { __documentChannel :: Rss1Channel e }+ | DocumentImage { __documentImage :: RssImage }+ | DocumentItem { __documentItem :: RssItem e }+ | DocumentTextInput { __documentTextInput :: RssTextInput } -makeTraversals ''DocumentPiece+makeLenses ''DocumentPiece -- | Parse an @\<RDF\>@ element.@@ -257,10 +267,10 @@ rss1Document :: ParseRssExtension e => MonadCatch m => ConduitM Event o m (Maybe (RssDocument e)) rss1Document = fmap (fmap rss1ToRss2) $ rdfTag "RDF" ignoreAttrs $ const $ (manyYield' (choose piece) .| parser) <* many ignoreAnyTreeContent where parser = getZipConduit $ Rss1Document- <$> ZipConduit (projectC _DocumentChannel .| headRequiredC "Missing <channel> element")- <*> ZipConduit (projectC _DocumentImage .| headC)- <*> ZipConduit (projectC _DocumentItem .| sinkList)- <*> ZipConduit (projectC _DocumentTextInput .| headC)+ <$> ZipConduit (projectC _documentChannel .| headRequiredC "Missing <channel> element")+ <*> ZipConduit (projectC _documentImage .| headC)+ <*> ZipConduit (projectC _documentItem .| sinkList)+ <*> ZipConduit (projectC _documentTextInput .| headC) piece = [ fmap DocumentChannel <$> rss1Channel , fmap DocumentImage <$> rss1Image , fmap DocumentItem <$> rss1Item
test/Main.hs view
@@ -32,7 +32,7 @@ import Data.Version import Data.Void import Data.XML.Types-import Lens.Simple+import Lens.Micro import System.FilePath import System.IO import System.Timeout@@ -316,7 +316,7 @@ result^?channelImageL._Just.imageTitleL @?= Just "XML.com" result^?channelImageL._Just.imageLinkL @?= Just imageLink result^?channelImageL._Just.imageUriL @?= Just imageUri- length (result^..channelItemsL) @?= 2+ length (result^.channelItemsL) @?= 2 result^?channelTextInputL._Just.textInputTitleL @?= Just "Search XML.com" result^?channelTextInputL._Just.textInputDescriptionL @?= Just "Search XML.com's XML collection" result^?channelTextInputL._Just.textInputNameL @?= Just "s"