packages feed

feed 1.0.1.0 → 1.3.2.1

raw patch · 27 files changed

Files

CHANGELOG.md view
@@ -1,3 +1,30 @@+#### 1.3.2.1+* text 2.0 support, thanks to Alexander Batischev.+* Moved the repository to https://github.com/haskell-party/++### 1.3.2.0+* Expose RSS/Atom item content through queries by using `getItemContent`.++### 1.3.0.1+* Add a test to check that validation works on a simple entry.+* Change attribute handling when validating so that type attribute is recognised properly on content.++#### 1.3.0.0+* Reverted changes to `EntryContent` that came in 1.2.0.0. Thanks to Tomas Janousek.++#### 1.2.0.1++* Get rid of xmlns:ns and ns:-prefixed attributes that confused some applications, thanks to Tomas Janousek.+* GHC 8.8 support++## 1.2.0.0++Updated `EntryContent`'s `HTMLContent` to wrap an `XML.Element` instead of `Text`. Thanks to Jake Keuhlen.++## 1.1.0.0++* `parseFeedFromFile` now returns `IO (Maybe Feed)` instead of `IO Feed` to distinguish IO exceptions from parse failures. Thanks to Jake Keuhlen.+ #### 1.0.1.0  * Support for GHC 8.6.x libraries
README.lhs view
@@ -1,18 +1,20 @@ # Feed  [![feed](https://img.shields.io/hackage/v/feed.svg)](http://hackage.haskell.org/package/feed)-[![Build Status](https://travis-ci.org/bergmark/feed.svg?branch=master)](https://travis-ci.org/bergmark/feed)+![Build Status](https://github.com/haskell-party/feed/actions/workflows/haskell-ci.yml/badge.svg) + ## Goal  Interfacing with *RSS* (v 0.9x, 2.x, 1.0) + *Atom* feeds.  - Parsers-- Pretty Printers+- Constructors+- Rendering - Querying  To help working with the multiple feed formats we've ended up with-this set of modules providing parsers, pretty printers and some utility+this set of modules providing parsers, printers and some utility code for querying and just generally working with a concrete representation of feeds in Haskell. @@ -37,35 +39,50 @@ module Main where  import Prelude.Compat hiding (take)-+import Data.Maybe import Data.Text import Data.XML.Types as XML import qualified Data.Text.Lazy as Lazy+import Text.Feed.Types++import Text.XML (def, rsPretty) import qualified Text.Atom.Feed as Atom-import qualified Text.Atom.Feed.Export as Export (textFeed)+import qualified Text.Feed.Export as Export (textFeedWith)  myFeed :: Atom.Feed myFeed = Atom.nullFeed-    "http://example.com/atom.xml"       -- ^ id-    (Atom.TextString "Example Website") -- ^ title-    "2017-08-01"                        -- ^ last updated+    "http://example.com/atom.xml"+    (Atom.TextString "Example Website")+    "2017-08-01" ```  Now we can export the feed to `Text`.  ```haskell-renderFeed :: Atom.Feed -> Maybe Lazy.Text-renderFeed = Export.textFeed+renderFeed :: Atom.Feed -> Lazy.Text+renderFeed = fromJust . Export.textFeedWith def{rsPretty = True} . AtomFeed ``` -```-> renderFeed myFeed-<?xml version="1.0" encoding="UTF-8"?>-<feed xmlns="http://www.w3.org/2005/Atom">-  <title xmlns:ns="http://www.w3.org/2005/Atom" ns:type="text">Example Website</title>-  <id>http://example.com/atom.xml</id>-  <updated>2017-08-01</updated>-</feed>+We can now render our feed:++```haskell+-- |+-- $setup+-- >>> import qualified Data.Text.Lazy.IO as Lazy+--+-- >>> Lazy.putStr $ renderFeed myFeed+-- <?xml version="1.0" encoding="UTF-8"?>+-- <feed xmlns="http://www.w3.org/2005/Atom">+--     <title type="text">+--         Example Website+--     </title>+--     <id>+--         http://example.com/atom.xml+--     </id>+--     <updated>+--         2017-08-01+--     </updated>+-- </feed> ```  The `TextContent` sum type allows us to specify which type of text we're providing.@@ -120,37 +137,65 @@          } ``` -```-> renderFeed feed-<?xml version="1.0" encoding="UTF-8"?>-<feed xmlns="http://www.w3.org/2005/Atom">-  <title xmlns:ns="http://www.w3.org/2005/Atom" ns:type="text">Example Website</title>-  <id>http://example.com/atom.xml</id>-  <updated>2017-08-01</updated>-  <link xmlns:ns="http://www.w3.org/2005/Atom" ns:href="http://example.com/"/>-  <entry>-    <id>http://example.com/2</id>-    <title xmlns:ns="http://www.w3.org/2005/Atom" ns:type="text">Bar.</title>-    <updated>2000-02-02T18:30:00Z</updated>-    <author>-      <name>J. Smith</name>-    </author>-    <content xmlns:ns="http://www.w3.org/2005/Atom" ns:type="html">Bar.</content>-    <link xmlns:ns="http://www.w3.org/2005/Atom" ns:href="http://example.com/2"/>-  </entry>-  <entry>-    <id>http://example.com/1</id>-    <title xmlns:ns="http://www.w3.org/2005/Atom" ns:type="text">Foo.</title>-    <updated>2000-01-01T18:30:00Z</updated>-    <author>-      <name>J. Smith</name>-    </author>-    <content xmlns:ns="http://www.w3.org/2005/Atom" ns:type="html">Foo.</content>-    <link xmlns:ns="http://www.w3.org/2005/Atom" ns:href="http://example.com/1"/>-  </entry>-</feed>+```haskell+-- |+-- >>> Lazy.putStr $ renderFeed feed+-- <?xml version="1.0" encoding="UTF-8"?>+-- <feed xmlns="http://www.w3.org/2005/Atom">+--     <title type="text">+--         Example Website+--     </title>+--     <id>+--         http://example.com/atom.xml+--     </id>+--     <updated>+--         2017-08-01+--     </updated>+--     <link href="http://example.com/"/>+--     <entry>+--         <id>+--             http://example.com/2+--         </id>+--         <title type="text">+--             Bar.+--         </title>+--         <updated>+--             2000-02-02T18:30:00Z+--         </updated>+--         <author>+--             <name>+--                 J. Smith+--             </name>+--         </author>+--         <content type="html">+--             Bar.+--         </content>+--         <link href="http://example.com/2"/>+--     </entry>+--     <entry>+--         <id>+--             http://example.com/1+--         </id>+--         <title type="text">+--             Foo.+--         </title>+--         <updated>+--             2000-01-01T18:30:00Z+--         </updated>+--         <author>+--             <name>+--                 J. Smith+--             </name>+--         </author>+--         <content type="html">+--             Foo.+--         </content>+--         <link href="http://example.com/1"/>+--     </entry>+-- </feed> ```-See [here](https://github.com/bergmark/feed/blob/master/tests/Example/CreateAtom.hs) for this content as an uninterrupted running example.++See [here](https://github.com/haskell-party/feed/blob/master/tests/Example/CreateAtom.hs) for this content as an uninterrupted running example.  ```haskell -- Dummy main needed to compile this file with markdown-unlit
README.md view
@@ -1,18 +1,20 @@ # Feed  [![feed](https://img.shields.io/hackage/v/feed.svg)](http://hackage.haskell.org/package/feed)-[![Build Status](https://travis-ci.org/bergmark/feed.svg?branch=master)](https://travis-ci.org/bergmark/feed)+![Build Status](https://github.com/haskell-party/feed/actions/workflows/haskell-ci.yml/badge.svg) + ## Goal  Interfacing with *RSS* (v 0.9x, 2.x, 1.0) + *Atom* feeds.  - Parsers-- Pretty Printers+- Constructors+- Rendering - Querying  To help working with the multiple feed formats we've ended up with-this set of modules providing parsers, pretty printers and some utility+this set of modules providing parsers, printers and some utility code for querying and just generally working with a concrete representation of feeds in Haskell. @@ -37,35 +39,50 @@ module Main where  import Prelude.Compat hiding (take)-+import Data.Maybe import Data.Text import Data.XML.Types as XML import qualified Data.Text.Lazy as Lazy+import Text.Feed.Types++import Text.XML (def, rsPretty) import qualified Text.Atom.Feed as Atom-import qualified Text.Atom.Feed.Export as Export (textFeed)+import qualified Text.Feed.Export as Export (textFeedWith)  myFeed :: Atom.Feed myFeed = Atom.nullFeed-    "http://example.com/atom.xml"       -- ^ id-    (Atom.TextString "Example Website") -- ^ title-    "2017-08-01"                        -- ^ last updated+    "http://example.com/atom.xml"+    (Atom.TextString "Example Website")+    "2017-08-01" ```  Now we can export the feed to `Text`.  ```haskell-renderFeed :: Atom.Feed -> Maybe Lazy.Text-renderFeed = Export.textFeed+renderFeed :: Atom.Feed -> Lazy.Text+renderFeed = fromJust . Export.textFeedWith def{rsPretty = True} . AtomFeed ``` -```-> renderFeed myFeed-<?xml version="1.0" encoding="UTF-8"?>-<feed xmlns="http://www.w3.org/2005/Atom">-  <title xmlns:ns="http://www.w3.org/2005/Atom" ns:type="text">Example Website</title>-  <id>http://example.com/atom.xml</id>-  <updated>2017-08-01</updated>-</feed>+We can now render our feed:++```haskell+-- |+-- $setup+-- >>> import qualified Data.Text.Lazy.IO as Lazy+--+-- >>> Lazy.putStr $ renderFeed myFeed+-- <?xml version="1.0" encoding="UTF-8"?>+-- <feed xmlns="http://www.w3.org/2005/Atom">+--     <title type="text">+--         Example Website+--     </title>+--     <id>+--         http://example.com/atom.xml+--     </id>+--     <updated>+--         2017-08-01+--     </updated>+-- </feed> ```  The `TextContent` sum type allows us to specify which type of text we're providing.@@ -120,37 +137,65 @@          } ``` -```-> renderFeed feed-<?xml version="1.0" encoding="UTF-8"?>-<feed xmlns="http://www.w3.org/2005/Atom">-  <title xmlns:ns="http://www.w3.org/2005/Atom" ns:type="text">Example Website</title>-  <id>http://example.com/atom.xml</id>-  <updated>2017-08-01</updated>-  <link xmlns:ns="http://www.w3.org/2005/Atom" ns:href="http://example.com/"/>-  <entry>-    <id>http://example.com/2</id>-    <title xmlns:ns="http://www.w3.org/2005/Atom" ns:type="text">Bar.</title>-    <updated>2000-02-02T18:30:00Z</updated>-    <author>-      <name>J. Smith</name>-    </author>-    <content xmlns:ns="http://www.w3.org/2005/Atom" ns:type="html">Bar.</content>-    <link xmlns:ns="http://www.w3.org/2005/Atom" ns:href="http://example.com/2"/>-  </entry>-  <entry>-    <id>http://example.com/1</id>-    <title xmlns:ns="http://www.w3.org/2005/Atom" ns:type="text">Foo.</title>-    <updated>2000-01-01T18:30:00Z</updated>-    <author>-      <name>J. Smith</name>-    </author>-    <content xmlns:ns="http://www.w3.org/2005/Atom" ns:type="html">Foo.</content>-    <link xmlns:ns="http://www.w3.org/2005/Atom" ns:href="http://example.com/1"/>-  </entry>-</feed>+```haskell+-- |+-- >>> Lazy.putStr $ renderFeed feed+-- <?xml version="1.0" encoding="UTF-8"?>+-- <feed xmlns="http://www.w3.org/2005/Atom">+--     <title type="text">+--         Example Website+--     </title>+--     <id>+--         http://example.com/atom.xml+--     </id>+--     <updated>+--         2017-08-01+--     </updated>+--     <link href="http://example.com/"/>+--     <entry>+--         <id>+--             http://example.com/2+--         </id>+--         <title type="text">+--             Bar.+--         </title>+--         <updated>+--             2000-02-02T18:30:00Z+--         </updated>+--         <author>+--             <name>+--                 J. Smith+--             </name>+--         </author>+--         <content type="html">+--             Bar.+--         </content>+--         <link href="http://example.com/2"/>+--     </entry>+--     <entry>+--         <id>+--             http://example.com/1+--         </id>+--         <title type="text">+--             Foo.+--         </title>+--         <updated>+--             2000-01-01T18:30:00Z+--         </updated>+--         <author>+--             <name>+--                 J. Smith+--             </name>+--         </author>+--         <content type="html">+--             Foo.+--         </content>+--         <link href="http://example.com/1"/>+--     </entry>+-- </feed> ```-See [here](https://github.com/bergmark/feed/blob/master/tests/Example/CreateAtom.hs) for this content as an uninterrupted running example.++See [here](https://github.com/haskell-party/feed/blob/master/tests/Example/CreateAtom.hs) for this content as an uninterrupted running example.  ```haskell -- Dummy main needed to compile this file with markdown-unlit
feed.cabal view
@@ -1,5 +1,5 @@ name:                feed-version:             1.0.1.0+version:             1.3.2.1 license:             BSD3 license-file:        LICENSE category:            Text@@ -13,25 +13,28 @@                      of feeds in Haskell.                      .                      See here for an example of how to create an Atom feed:-                     <https://github.com/bergmark/feed/blob/master/tests/Example/CreateAtom.hs>+                     <https://github.com/haskell-party/feed/blob/master/tests/Example/CreateAtom.hs>                      .                      For basic reading and editing of feeds, consult                      the documentation of the Text.Feed.* hierarchy. author:              Sigbjorn Finne <sof@forkIO.com> maintainer:          Adam Bergmark <adam@bergmark.nl>-homepage:            https://github.com/bergmark/feed-bug-reports:         https://github.com/bergmark/feed/issues-cabal-version:       >= 1.8+homepage:            https://github.com/haskell-party/feed+bug-reports:         https://github.com/haskell-party/feed/issues+cabal-version:       2.0 build-type:          Simple tested-with:-    GHC == 7.4.2-  , GHC == 7.6.3+    GHC == 7.6.3   , GHC == 7.8.4   , GHC == 7.10.3   , GHC == 8.0.2-  , GHC == 8.2.1-  , GHC == 8.4.1-  , GHC == 8.6.1+  , GHC == 8.2.2+  , GHC == 8.4.4+  , GHC == 8.6.5+  , GHC == 8.8.4+  , GHC == 8.10.7+  , GHC == 9.0.1+  , GHC == 9.2.1 data-files:   tests/files/*.xml extra-source-files:@@ -40,12 +43,13 @@  source-repository head   type:              git-  location:          https://github.com/bergmark/feed.git+  location:          https://github.com/haskell-party/feed.git  library   ghc-options:       -Wall   hs-source-dirs:    src-  extensions:+  default-language:  Haskell2010+  default-extensions:     NoImplicitPrelude     OverloadedStrings   exposed-modules:@@ -75,32 +79,37 @@     Data.Text.Util     Data.XML.Compat   build-depends:-      base >= 4 && < 4.13-    , base-compat >= 0.9 && < 0.11-    , bytestring >= 0.9 && < 0.11+      base >= 4 && < 4.17+    , base-compat >= 0.9 && < 0.13+    , bytestring >= 0.9 && < 0.12     , old-locale == 1.0.*     , old-time >= 1 && < 1.2     , safe == 0.3.*-    , text < 1.3-    , time < 1.9+    , text < 1.3 || ==2.0.*+    , time < 1.12     , time-locale-compat == 0.1.*     , utf8-string < 1.1     , xml-types >= 0.3.6 && < 0.4-    , xml-conduit >= 1.3 && < 1.9+    , xml-conduit >= 1.3 && < 1.10  test-suite tests   ghc-options:       -Wall   hs-source-dirs:    tests   main-is:           Main.hs   type:              exitcode-stdio-1.0-  extensions:+  default-language:  Haskell2010+  default-extensions:     NoImplicitPrelude     OverloadedStrings+  autogen-modules:+    Paths_feed   other-modules:     Paths_feed     Example     Example.CreateAtom+    ImportExport     Text.Atom.Tests+    Text.Atom.Validate.Tests     Text.Feed.Util.Tests     Text.RSS.Equals     Text.RSS.Export.Tests@@ -108,32 +117,49 @@     Text.RSS.Tests     Text.RSS.Utils   build-depends:-      base >= 4 && < 4.13-    , base-compat >= 0.9 && < 0.11+      base >= 4.6 && < 4.17+    , base-compat >= 0.9 && < 0.13     , HUnit >= 1.2 && < 1.7     , feed     , old-time >= 1 && < 1.2+    , syb     , test-framework == 0.8.*     , test-framework-hunit == 0.3.*-    , text < 1.3-    , time < 1.9+    , text < 1.3 || ==2.0.*+    , time < 1.12     , xml-types >= 0.3.6 && < 0.4-    , xml-conduit >= 1.3 && < 1.9+    , xml-conduit >= 1.3 && < 1.10  test-suite readme   ghc-options:       -Wall -pgmL markdown-unlit   main-is:           README.lhs-  extensions:+  default-language:  Haskell2010+  default-extensions:     NoImplicitPrelude     OverloadedStrings   type:              exitcode-stdio-1.0   build-depends:-      base >= 4 && < 4.13-    , base-compat >= 0.9 && < 0.11+      base >= 4.6+    , base-compat >= 0.9 && < 0.13     , text-    , xml-types     , feed     , xml-conduit     , xml-types   build-tool-depends:-    markdown-unlit:markdown-unlit == 0.4.*+    markdown-unlit:markdown-unlit >= 0.4 && < 0.6++test-suite readme-doctests+  hs-source-dirs: tests+  main-is: doctest-driver.hs+  type: exitcode-stdio-1.0+  default-language: Haskell2010+  if impl(ghc < 8)+     buildable: False+  build-depends:+      base >= 4.6+    , doctest+    , doctest-driver-gen+    , feed+  build-tool-depends:+      markdown-unlit:markdown-unlit >= 0.4 && < 0.6+    , doctest-driver-gen:doctest-driver-gen
src/Data/Text/Util.hs view
@@ -1,6 +1,7 @@ module Data.Text.Util   ( readInt   , renderFeed+  , renderFeedWith   ) where  import Prelude.Compat@@ -8,9 +9,9 @@ import Data.Text import Data.Text.Read +import qualified Data.Text.Lazy as TL import qualified Data.XML.Types as XT -- from xml-types import qualified Text.XML as XC -- from xml-conduit-import qualified Data.Text.Lazy as TL  readInt :: Text -> Maybe Integer readInt s =@@ -19,14 +20,17 @@     _ -> Nothing  renderFeed :: (a -> XT.Element) -> a -> Maybe TL.Text-renderFeed cf f = let e = cf f-                      d = elToDoc e-                  in XC.renderText XC.def <$> d+renderFeed = renderFeedWith XC.def +renderFeedWith :: XC.RenderSettings -> (a -> XT.Element) -> a -> Maybe TL.Text+renderFeedWith opts cf f =+  let e = cf f+      d = elToDoc e+   in XC.renderText opts <$> d  -- Ancillaries --- elToDoc :: XT.Element -> Maybe XC.Document-elToDoc el = let txd = XT.Document (XC.Prologue [] Nothing []) el []-                 cxd = XC.fromXMLDocument txd-             in either (const Nothing) Just cxd+elToDoc el =+  let txd = XT.Document (XC.Prologue [] Nothing []) el []+      cxd = XC.fromXMLDocument txd+   in either (const Nothing) Just cxd
src/Text/Atom/Feed.hs view
@@ -54,77 +54,85 @@  type MediaType = Text -data Feed = Feed-  { feedId :: URI-  , feedTitle :: TextContent-  , feedUpdated :: Date-  , feedAuthors :: [Person]-  , feedCategories :: [Category]-  , feedContributors :: [Person]-  , feedGenerator :: Maybe Generator-  , feedIcon :: Maybe URI-  , feedLinks :: [Link]-  , feedLogo :: Maybe URI-  , feedRights :: Maybe TextContent-  , feedSubtitle :: Maybe TextContent-  , feedEntries :: [Entry]-  , feedAttrs :: [Attr]-  , feedOther :: [XML.Element]-  } deriving (Show)+data Feed =+  Feed+    { feedId :: URI+    , feedTitle :: TextContent+    , feedUpdated :: Date+    , feedAuthors :: [Person]+    , feedCategories :: [Category]+    , feedContributors :: [Person]+    , feedGenerator :: Maybe Generator+    , feedIcon :: Maybe URI+    , feedLinks :: [Link]+    , feedLogo :: Maybe URI+    , feedRights :: Maybe TextContent+    , feedSubtitle :: Maybe TextContent+    , feedEntries :: [Entry]+    , feedAttrs :: [Attr]+    , feedOther :: [XML.Element]+    }+  deriving (Show) -data Entry = Entry-  { entryId :: URI-  , entryTitle :: TextContent-  , entryUpdated :: Date-  , entryAuthors :: [Person]-  , entryCategories :: [Category]-  , entryContent :: Maybe EntryContent-  , entryContributor :: [Person]-  , entryLinks :: [Link]-  , entryPublished :: Maybe Date-  , entryRights :: Maybe TextContent-  , entrySource :: Maybe Source-  , entrySummary :: Maybe TextContent-  , entryInReplyTo :: Maybe InReplyTo-  , entryInReplyTotal :: Maybe InReplyTotal-  , entryAttrs :: [Attr]-  , entryOther :: [XML.Element]-  } deriving (Show)+data Entry =+  Entry+    { entryId :: URI+    , entryTitle :: TextContent+    , entryUpdated :: Date+    , entryAuthors :: [Person]+    , entryCategories :: [Category]+    , entryContent :: Maybe EntryContent+    , entryContributor :: [Person]+    , entryLinks :: [Link]+    , entryPublished :: Maybe Date+    , entryRights :: Maybe TextContent+    , entrySource :: Maybe Source+    , entrySummary :: Maybe TextContent+    , entryInReplyTo :: Maybe InReplyTo+    , entryInReplyTotal :: Maybe InReplyTotal+    , entryAttrs :: [Attr]+    , entryOther :: [XML.Element]+    }+  deriving (Show)  data EntryContent   = TextContent Text   | HTMLContent Text   | XHTMLContent XML.Element-  | MixedContent (Maybe Text)-                 [XML.Node]-  | ExternalContent (Maybe MediaType)-                    URI+  | MixedContent (Maybe Text) [XML.Node]+  | ExternalContent (Maybe MediaType) URI   deriving (Show) -data Category = Category-  { catTerm :: Text -- ^ the tag\/term of the category.-  , catScheme :: Maybe URI -- ^ optional URL for identifying the categorization scheme.-  , catLabel :: Maybe Text -- ^ human-readable label of the category-  , catOther :: [XML.Element] -- ^ unknown elements, for extensibility.-  } deriving (Show)+data Category =+  Category+    { catTerm :: Text -- ^ the tag\/term of the category.+    , catScheme :: Maybe URI -- ^ optional URL for identifying the categorization scheme.+    , catLabel :: Maybe Text -- ^ human-readable label of the category+    , catOther :: [XML.Element] -- ^ unknown elements, for extensibility.+    }+  deriving (Show) -data Generator = Generator-  { genURI :: Maybe URI-  , genVersion :: Maybe Text-  , genText :: Text-  } deriving (Eq, Show)+data Generator =+  Generator+    { genURI :: Maybe URI+    , genVersion :: Maybe Text+    , genText :: Text+    }+  deriving (Eq, Show) -data Link = Link-  { linkHref :: URI+data Link =+  Link+    { linkHref :: URI          -- ToDo: make the switch over to using the Atom.Feed.Link relation type.-  , linkRel :: Maybe (Either NCName URI)-  , linkType :: Maybe MediaType-  , linkHrefLang :: Maybe Text-  , linkTitle :: Maybe Text-  , linkLength :: Maybe Text-  , linkAttrs :: [Attr]-  , linkOther :: [XML.Element]-  } deriving (Show)+    , linkRel :: Maybe (Either NCName URI)+    , linkType :: Maybe MediaType+    , linkHrefLang :: Maybe Text+    , linkTitle :: Maybe Text+    , linkLength :: Maybe Text+    , linkAttrs :: [Attr]+    , linkOther :: [XML.Element]+    }+  deriving (Show)  data TextContent   = TextString Text@@ -137,41 +145,49 @@ txtToString (HTMLString s) = unpack s txtToString (XHTMLString x) = show x -data Source = Source-  { sourceAuthors :: [Person]-  , sourceCategories :: [Category]-  , sourceGenerator :: Maybe Generator-  , sourceIcon :: Maybe URI-  , sourceId :: Maybe URI-  , sourceLinks :: [Link]-  , sourceLogo :: Maybe URI-  , sourceRights :: Maybe TextContent-  , sourceSubtitle :: Maybe TextContent-  , sourceTitle :: Maybe TextContent-  , sourceUpdated :: Maybe Date-  , sourceOther :: [XML.Element]-  } deriving (Show)+data Source =+  Source+    { sourceAuthors :: [Person]+    , sourceCategories :: [Category]+    , sourceGenerator :: Maybe Generator+    , sourceIcon :: Maybe URI+    , sourceId :: Maybe URI+    , sourceLinks :: [Link]+    , sourceLogo :: Maybe URI+    , sourceRights :: Maybe TextContent+    , sourceSubtitle :: Maybe TextContent+    , sourceTitle :: Maybe TextContent+    , sourceUpdated :: Maybe Date+    , sourceOther :: [XML.Element]+    }+  deriving (Show) -data Person = Person-  { personName :: Text-  , personURI :: Maybe URI-  , personEmail :: Maybe Text-  , personOther :: [XML.Element]-  } deriving (Show)+data Person =+  Person+    { personName :: Text+    , personURI :: Maybe URI+    , personEmail :: Maybe Text+    , personOther :: [XML.Element]+    }+  deriving (Show) -data InReplyTo = InReplyTo-  { replyToRef :: URI-  , replyToHRef :: Maybe URI-  , replyToType :: Maybe MediaType-  , replyToSource :: Maybe URI-  , replyToOther :: [Attr]-  , replyToContent :: [Node]-  } deriving (Show)+data InReplyTo =+  InReplyTo+    { replyToRef :: URI+    , replyToHRef :: Maybe URI+    , replyToType :: Maybe MediaType+    , replyToSource :: Maybe URI+    , replyToOther :: [Attr]+    , replyToContent :: [Node]+    }+  deriving (Show) -data InReplyTotal = InReplyTotal-  { replyToTotal :: Integer -- non-negative :)-  , replyToTotalOther :: [Attr]-  } deriving (Show)+data InReplyTotal =+  InReplyTotal+    { replyToTotal :: Integer -- non-negative :)+    , replyToTotalOther :: [Attr]+    }+  deriving (Show)  -- *Smart Constructors newCategory ::
src/Text/Atom/Feed/Export.hs view
@@ -57,10 +57,10 @@ import Prelude.Compat  import Data.Text (Text, pack)-import Data.XML.Types as XML-import Text.Atom.Feed import qualified Data.Text.Lazy as TL import qualified Data.Text.Util as U+import Data.XML.Types as XML+import Text.Atom.Feed  atom_prefix :: Maybe Text atom_prefix = Nothing -- Just "atom"@@ -107,7 +107,8 @@ atomName nc = Name {nameLocalName = nc, nameNamespace = Just atomNS, namePrefix = atom_prefix}  atomAttr :: Text -> Text -> Attr-atomAttr x y = (atomName x, [ContentText y])+atomAttr x y =+  (Name {nameLocalName = x, nameNamespace = Nothing, namePrefix = atom_prefix}, [ContentText y])  atomNode :: Text -> [Node] -> XML.Element atomNode x = blank_element (atomName x)
src/Text/Atom/Feed/Import.hs view
@@ -71,15 +71,17 @@ pQLeaf x es = (T.concat . elementText) `fmap` pQNode x es  pAttr :: Text -> XML.Element -> Maybe Text-pAttr x e = (`attributeText` e) =<< fst <$> find sameAttr (elementAttributes e)-  where-    ax = atomName x-    sameAttr (k, _) = k == ax || (isNothing (nameNamespace k) && nameLocalName k == x)+pAttr x e = (`attributeText` e) =<< find (sameAtomAttr x) (map fst $ elementAttributes e)  pAttrs :: Text -> XML.Element -> [Text] pAttrs x e = [t | ContentText t <- cnts]   where-    cnts = concat [v | (k, v) <- elementAttributes e, k == atomName x]+    cnts = concat [v | (k, v) <- elementAttributes e, sameAtomAttr x k]++sameAtomAttr :: Text -> Name -> Bool+sameAtomAttr x k = k == ax || (isNothing (nameNamespace k) && nameLocalName k == x)+  where+    ax = atomName x  pQAttr :: Name -> XML.Element -> Maybe Text pQAttr = attributeText
src/Text/Atom/Feed/Validate.hs view
@@ -53,8 +53,7 @@ import Data.Maybe  data VTree a-  = VNode [a]-          [VTree a]+  = VNode [a] [VTree a]   | VLeaf [a]   deriving (Eq, Show) @@ -149,8 +148,7 @@   case pNodes "link" (elementChildren e) of     xs ->       case map fst $-           filter (\(_, n) -> n == "alternate") $-           mapMaybe (\ex -> (ex,) <$> pAttr "rel" ex) xs of+           filter (\(_, n) -> n == "alternate") $ mapMaybe (\ex -> (ex, ) <$> pAttr "rel" ex) xs of         xs1 ->           let jmb (Just x) (Just y) = Just (x, y)               jmb _ _ = Nothing
src/Text/Atom/Pub.hs view
@@ -26,32 +26,34 @@ import Data.XML.Types as XML import Text.Atom.Feed (Category, TextContent, URI) -data Service = Service-  { serviceWorkspaces :: [Workspace]-  , serviceOther :: [XML.Element]-  }+data Service =+  Service+    { serviceWorkspaces :: [Workspace]+    , serviceOther :: [XML.Element]+    } -data Workspace = Workspace-  { workspaceTitle :: TextContent-  , workspaceCols :: [Collection]-  , workspaceOther :: [XML.Element]-  }+data Workspace =+  Workspace+    { workspaceTitle :: TextContent+    , workspaceCols :: [Collection]+    , workspaceOther :: [XML.Element]+    } -data Collection = Collection-  { collectionURI :: URI-  , collectionTitle :: TextContent-  , collectionAccept :: [Accept]-  , collectionCats :: [Categories]-  , collectionOther :: [XML.Element]-  }+data Collection =+  Collection+    { collectionURI :: URI+    , collectionTitle :: TextContent+    , collectionAccept :: [Accept]+    , collectionCats :: [Categories]+    , collectionOther :: [XML.Element]+    }  data Categories   = CategoriesExternal URI-  | Categories (Maybe Bool)-               (Maybe URI)-               [Category]+  | Categories (Maybe Bool) (Maybe URI) [Category]   deriving (Show) -newtype Accept = Accept-  { acceptType :: Text-  }+newtype Accept =+  Accept+    { acceptType :: Text+    }
src/Text/DublinCore/Types.hs view
@@ -24,10 +24,12 @@ import Data.Text  -- | A DCItem pairs a specific element with its (string) value.-data DCItem = DCItem-  { dcElt :: DCInfo-  , dcText :: Text-  } deriving (Eq, Show)+data DCItem =+  DCItem+    { dcElt :: DCInfo+    , dcText :: Text+    }+  deriving (Eq, Show)  -- | The Dublin Core Metadata Element Set, all 15 of them (plus an extension constructor.) data DCInfo
src/Text/Feed/Export.hs view
@@ -14,19 +14,21 @@ module Text.Feed.Export   ( Text.Feed.Export.xmlFeed -- :: Feed -> XML.Element   , Text.Feed.Export.textFeed -- :: Feed -> TL.Text+  , Text.Feed.Export.textFeedWith   ) where  import Prelude.Compat  import Text.Feed.Types +import qualified Data.Text.Util as U import Text.Atom.Feed.Export as Atom import Text.RSS.Export as RSS import Text.RSS1.Export as RSS1-import qualified Data.Text.Util as U -import Data.XML.Types as XML import qualified Data.Text.Lazy as TL+import Data.XML.Types as XML+import Text.XML (RenderSettings)  -- | 'xmlFeed f' serializes a @Feed@ document into a conforming -- XML toplevel element.@@ -40,3 +42,6 @@  textFeed :: Feed -> Maybe TL.Text textFeed = U.renderFeed Text.Feed.Export.xmlFeed++textFeedWith :: RenderSettings -> Feed -> Maybe TL.Text+textFeedWith settings = U.renderFeedWith settings Text.Feed.Export.xmlFeed
src/Text/Feed/Import.hs view
@@ -68,12 +68,8 @@  -- | 'parseFeedFromFile fp' reads in the contents of the file at @fp@; -- the assumed encoding is UTF-8.-parseFeedFromFile :: FilePath -> IO Feed-parseFeedFromFile fp = do-  ls <- utf8readFile fp-  case parseFeedString ls of-    Nothing -> fail ("parseFeedFromFile: not a well-formed XML content in: " ++ fp)-    Just f -> return f+parseFeedFromFile :: FilePath -> IO (Maybe Feed)+parseFeedFromFile fp = parseFeedString <$> utf8readFile fp  -- | 'parseFeedWithParser tries to parse the string @str@ -- as one of the feed formats. First as Atom, then RSS2 before
src/Text/Feed/Query.hs view
@@ -42,6 +42,7 @@   , getItemCategories -- :: ItemGetter [Text]   , getItemRights -- :: ItemGetter Text   , getItemSummary -- :: ItemGetter Text+  , getItemContent -- :: ItemGetter Text   , getItemDescription -- :: ItemGetter Text (synonym of previous.)   ) where @@ -430,6 +431,14 @@   where     isRights dc = dcElt dc == DC_Rights +getItemContent :: ItemGetter Text+getItemContent it =+  case it of+    Feed.AtomItem e -> atomContentToStr <$> Atom.entryContent e+    Feed.RSSItem e -> RSS.rssItemContent e+    Feed.RSS1Item _ -> Nothing+    Feed.XMLItem i -> strContent <$> findElement (atomName "content") i+ getItemSummary :: ItemGetter Text getItemSummary = getItemDescription @@ -446,6 +455,15 @@ toStr Nothing = "" toStr (Just (Left x)) = x toStr (Just (Right x)) = x++atomContentToStr :: EntryContent -> Text+atomContentToStr entry =+  case entry of+    HTMLContent e -> e+    XHTMLContent e -> T.unlines $ elementText e+    MixedContent text _ -> fromMaybe "" text+    ExternalContent _ b -> b+    TextContent text -> text  contentToStr :: TextContent -> Text contentToStr x =
src/Text/RSS/Export.hs view
@@ -33,10 +33,10 @@  import Prelude.Compat +import qualified Data.Text.Util as U import Data.XML.Compat import Data.XML.Types as XML import Text.RSS.Syntax-import qualified Data.Text.Util as U  import Data.Text (Text, pack) import qualified Data.Text.Lazy as TL
src/Text/RSS/Import.hs view
@@ -221,6 +221,7 @@       , rssItemAuthor = pLeaf "author" es `mplus` pQLeaf (dcName "creator") es       , rssItemCategories = pMany "category" elementToCategory es       , rssItemComments = pLeaf "comments" es+      , rssItemContent = pLeaf "content" es       , rssItemEnclosure = pNode "enclosure" es >>= elementToEnclosure       , rssItemGuid = pNode "guid" es >>= elementToGuid       , rssItemPubDate = pLeaf "pubDate" es `mplus` pQLeaf (dcName "date") es
src/Text/RSS/Syntax.hs view
@@ -48,109 +48,130 @@ -- * Core Types -- ^The Radio Userland version of RSS documents\/feeds. -- (versions 0.9x, 2.x)-data RSS = RSS-  { rssVersion :: Text-  , rssAttrs :: [Attr]-  , rssChannel :: RSSChannel-  , rssOther :: [XML.Element]-  } deriving (Show)+data RSS =+  RSS+    { rssVersion :: Text+    , rssAttrs :: [Attr]+    , rssChannel :: RSSChannel+    , rssOther :: [XML.Element]+    }+  deriving (Show)  type URLString = Text  -- | RFC 822 conforming. type DateString = Text -data RSSChannel = RSSChannel-  { rssTitle :: Text-  , rssLink :: URLString-  , rssDescription :: Text-  , rssItems :: [RSSItem]-  , rssLanguage :: Maybe Text-  , rssCopyright :: Maybe Text-  , rssEditor :: Maybe Text-  , rssWebMaster :: Maybe Text-  , rssPubDate :: Maybe DateString -- ^ rfc 822 conforming.-  , rssLastUpdate :: Maybe DateString -- ^ rfc 822 conforming.-  , rssCategories :: [RSSCategory]-  , rssGenerator :: Maybe Text-  , rssDocs :: Maybe URLString-  , rssCloud :: Maybe RSSCloud-  , rssTTL :: Maybe Integer-  , rssImage :: Maybe RSSImage-  , rssRating :: Maybe Text-  , rssTextInput :: Maybe RSSTextInput-  , rssSkipHours :: Maybe [Integer]-  , rssSkipDays :: Maybe [Text]-  , rssChannelOther :: [XML.Element]-  } deriving (Show)+data RSSChannel =+  RSSChannel+    { rssTitle :: Text+    , rssLink :: URLString+    , rssDescription :: Text+    , rssItems :: [RSSItem]+    , rssLanguage :: Maybe Text+    , rssCopyright :: Maybe Text+    , rssEditor :: Maybe Text+    , rssWebMaster :: Maybe Text+    , rssPubDate :: Maybe DateString -- ^ rfc 822 conforming.+    , rssLastUpdate :: Maybe DateString -- ^ rfc 822 conforming.+    , rssCategories :: [RSSCategory]+    , rssGenerator :: Maybe Text+    , rssDocs :: Maybe URLString+    , rssCloud :: Maybe RSSCloud+    , rssTTL :: Maybe Integer+    , rssImage :: Maybe RSSImage+    , rssRating :: Maybe Text+    , rssTextInput :: Maybe RSSTextInput+    , rssSkipHours :: Maybe [Integer]+    , rssSkipDays :: Maybe [Text]+    , rssChannelOther :: [XML.Element]+    }+  deriving (Show) -data RSSItem = RSSItem-  { rssItemTitle :: Maybe Text-  , rssItemLink :: Maybe URLString-  , rssItemDescription :: Maybe Text -- ^if not present, the title is. (per spec, at least.)-  , rssItemAuthor :: Maybe Text-  , rssItemCategories :: [RSSCategory]-  , rssItemComments :: Maybe URLString-  , rssItemEnclosure :: Maybe RSSEnclosure-  , rssItemGuid :: Maybe RSSGuid-  , rssItemPubDate :: Maybe DateString-  , rssItemSource :: Maybe RSSSource-  , rssItemAttrs :: [Attr]-  , rssItemOther :: [XML.Element]-  } deriving (Show)+data RSSItem =+  RSSItem+    { rssItemTitle :: Maybe Text+    , rssItemLink :: Maybe URLString+    , rssItemDescription :: Maybe Text -- ^if not present, the title is. (per spec, at least.)+    , rssItemAuthor :: Maybe Text+    , rssItemCategories :: [RSSCategory]+    , rssItemComments :: Maybe URLString+    , rssItemContent :: Maybe Text+    , rssItemEnclosure :: Maybe RSSEnclosure+    , rssItemGuid :: Maybe RSSGuid+    , rssItemPubDate :: Maybe DateString+    , rssItemSource :: Maybe RSSSource+    , rssItemAttrs :: [Attr]+    , rssItemOther :: [XML.Element]+    }+  deriving (Show) -data RSSSource = RSSSource-  { rssSourceURL :: URLString-  , rssSourceAttrs :: [Attr]-  , rssSourceTitle :: Text-  } deriving (Show)+data RSSSource =+  RSSSource+    { rssSourceURL :: URLString+    , rssSourceAttrs :: [Attr]+    , rssSourceTitle :: Text+    }+  deriving (Show) -data RSSEnclosure = RSSEnclosure-  { rssEnclosureURL :: URLString-  , rssEnclosureLength :: Maybe Integer-  , rssEnclosureType :: Text-  , rssEnclosureAttrs :: [Attr]-  } deriving (Show)+data RSSEnclosure =+  RSSEnclosure+    { rssEnclosureURL :: URLString+    , rssEnclosureLength :: Maybe Integer+    , rssEnclosureType :: Text+    , rssEnclosureAttrs :: [Attr]+    }+  deriving (Show) -data RSSCategory = RSSCategory-  { rssCategoryDomain :: Maybe Text-  , rssCategoryAttrs :: [Attr]-  , rssCategoryValue :: Text-  } deriving (Show)+data RSSCategory =+  RSSCategory+    { rssCategoryDomain :: Maybe Text+    , rssCategoryAttrs :: [Attr]+    , rssCategoryValue :: Text+    }+  deriving (Show) -data RSSGuid = RSSGuid-  { rssGuidPermanentURL :: Maybe Bool-  , rssGuidAttrs :: [Attr]-  , rssGuidValue :: Text-  } deriving (Show)+data RSSGuid =+  RSSGuid+    { rssGuidPermanentURL :: Maybe Bool+    , rssGuidAttrs :: [Attr]+    , rssGuidValue :: Text+    }+  deriving (Show) -data RSSImage = RSSImage-  { rssImageURL :: URLString -- the URL to the image resource.-  , rssImageTitle :: Text-  , rssImageLink :: URLString -- URL that the image resource should be an href to.-  , rssImageWidth :: Maybe Integer-  , rssImageHeight :: Maybe Integer-  , rssImageDesc :: Maybe Text-  , rssImageOther :: [XML.Element]-  } deriving (Show)+data RSSImage =+  RSSImage+    { rssImageURL :: URLString -- the URL to the image resource.+    , rssImageTitle :: Text+    , rssImageLink :: URLString -- URL that the image resource should be an href to.+    , rssImageWidth :: Maybe Integer+    , rssImageHeight :: Maybe Integer+    , rssImageDesc :: Maybe Text+    , rssImageOther :: [XML.Element]+    }+  deriving (Show) -data RSSCloud = RSSCloud-  { rssCloudDomain :: Maybe Text-  , rssCloudPort :: Maybe Text -- on purpose (i.e., not an int)-  , rssCloudPath :: Maybe Text-  , rssCloudRegisterProcedure :: Maybe Text-  , rssCloudProtocol :: Maybe Text-  , rssCloudAttrs :: [Attr]-  } deriving (Show)+data RSSCloud =+  RSSCloud+    { rssCloudDomain :: Maybe Text+    , rssCloudPort :: Maybe Text -- on purpose (i.e., not an int)+    , rssCloudPath :: Maybe Text+    , rssCloudRegisterProcedure :: Maybe Text+    , rssCloudProtocol :: Maybe Text+    , rssCloudAttrs :: [Attr]+    }+  deriving (Show) -data RSSTextInput = RSSTextInput-  { rssTextInputTitle :: Text-  , rssTextInputDesc :: Text-  , rssTextInputName :: Text-  , rssTextInputLink :: URLString-  , rssTextInputAttrs :: [Attr]-  , rssTextInputOther :: [XML.Element]-  } deriving (Show)+data RSSTextInput =+  RSSTextInput+    { rssTextInputTitle :: Text+    , rssTextInputDesc :: Text+    , rssTextInputName :: Text+    , rssTextInputLink :: URLString+    , rssTextInputAttrs :: [Attr]+    , rssTextInputOther :: [XML.Element]+    }+  deriving (Show)  -- * Default Constructors: nullRSS ::@@ -200,6 +221,7 @@     , rssItemAuthor = Nothing     , rssItemCategories = []     , rssItemComments = Nothing+    , rssItemContent = Nothing     , rssItemEnclosure = Nothing     , rssItemGuid = Nothing     , rssItemPubDate = Nothing
src/Text/RSS1/Export.hs view
@@ -17,10 +17,10 @@  import Prelude.Compat +import qualified Data.Text.Util as U import Text.DublinCore.Types import Text.RSS1.Syntax import Text.RSS1.Utils-import qualified Data.Text.Util as U  import Data.List.Compat import Data.Text (Text)
src/Text/RSS1/Syntax.hs view
@@ -47,79 +47,91 @@  type TextString = Text -data Feed = Feed-  { feedVersion :: Text-  , feedChannel :: Channel-  , feedImage :: Maybe Image-  , feedItems :: [Item]-  , feedTextInput :: Maybe TextInputInfo-  , feedTopics :: [TaxonomyTopic]-  , feedOther :: [XML.Element]-  , feedAttrs :: [Attr]-  } deriving (Show)+data Feed =+  Feed+    { feedVersion :: Text+    , feedChannel :: Channel+    , feedImage :: Maybe Image+    , feedItems :: [Item]+    , feedTextInput :: Maybe TextInputInfo+    , feedTopics :: [TaxonomyTopic]+    , feedOther :: [XML.Element]+    , feedAttrs :: [Attr]+    }+  deriving (Show) -data Channel = Channel-  { channelURI :: URIString-  , channelTitle :: TitleString-  , channelLink :: URIString-  , channelDesc :: TextString+data Channel =+  Channel+    { channelURI :: URIString+    , channelTitle :: TitleString+    , channelLink :: URIString+    , channelDesc :: TextString            -- these are indirect RDF associations to elements declared            -- outside the channel element in the RDF \/ feed document.-  , channelImageURI :: Maybe URIString-  , channelItemURIs :: [URIString]-  , channelTextInputURI :: Maybe URIString-  , channelDC :: [DCItem]-  , channelUpdatePeriod :: Maybe UpdatePeriod-  , channelUpdateFreq :: Maybe Integer-  , channelUpdateBase :: Maybe TimeString -- format is yyyy-mm-ddThh:mm-  , channelContent :: [ContentInfo]-  , channelTopics :: [URIString]-  , channelOther :: [XML.Element]-  , channelAttrs :: [Attr]-  } deriving (Show)+    , channelImageURI :: Maybe URIString+    , channelItemURIs :: [URIString]+    , channelTextInputURI :: Maybe URIString+    , channelDC :: [DCItem]+    , channelUpdatePeriod :: Maybe UpdatePeriod+    , channelUpdateFreq :: Maybe Integer+    , channelUpdateBase :: Maybe TimeString -- format is yyyy-mm-ddThh:mm+    , channelContent :: [ContentInfo]+    , channelTopics :: [URIString]+    , channelOther :: [XML.Element]+    , channelAttrs :: [Attr]+    }+  deriving (Show) -data Image = Image-  { imageURI :: URIString -- the image resource, most likely.-  , imageTitle :: TextString -- the "alt"ernative text.-  , imageURL :: URIString-  , imageLink :: URIString -- the href of the rendered img resource.-  , imageDC :: [DCItem]-  , imageOther :: [XML.Element]-  , imageAttrs :: [Attr]-  } deriving (Show)+data Image =+  Image+    { imageURI :: URIString -- the image resource, most likely.+    , imageTitle :: TextString -- the "alt"ernative text.+    , imageURL :: URIString+    , imageLink :: URIString -- the href of the rendered img resource.+    , imageDC :: [DCItem]+    , imageOther :: [XML.Element]+    , imageAttrs :: [Attr]+    }+  deriving (Show) -data Item = Item-  { itemURI :: URIString-  , itemTitle :: TextString-  , itemLink :: URIString-  , itemDesc :: Maybe TextString-  , itemDC :: [DCItem]-  , itemTopics :: [URIString]-  , itemContent :: [ContentInfo]-  , itemOther :: [XML.Element]-  , itemAttrs :: [Attr]-  } deriving (Show)+data Item =+  Item+    { itemURI :: URIString+    , itemTitle :: TextString+    , itemLink :: URIString+    , itemDesc :: Maybe TextString+    , itemDC :: [DCItem]+    , itemTopics :: [URIString]+    , itemContent :: [ContentInfo]+    , itemOther :: [XML.Element]+    , itemAttrs :: [Attr]+    }+  deriving (Show) -data TextInputInfo = TextInputInfo-  { textInputURI :: URIString-  , textInputTitle :: TextString-  , textInputDesc :: TextString-  , textInputName :: TextString-  , textInputLink :: URIString-  , textInputDC :: [DCItem]-  , textInputOther :: [XML.Element]-  , textInputAttrs :: [Attr]-  } deriving (Show)+data TextInputInfo =+  TextInputInfo+    { textInputURI :: URIString+    , textInputTitle :: TextString+    , textInputDesc :: TextString+    , textInputName :: TextString+    , textInputLink :: URIString+    , textInputDC :: [DCItem]+    , textInputOther :: [XML.Element]+    , textInputAttrs :: [Attr]+    }+  deriving (Show) -data TaxonomyTopic = TaxonomyTopic-  { taxonomyURI :: URIString-  , taxonomyLink :: URIString-  , taxonomyTitle :: Maybe Text-  , taxonomyDesc :: Maybe Text-  , taxonomyTopics :: [URIString]-  , taxonomyDC :: [DCItem]-  , taxonomyOther :: [XML.Element]-  } deriving (Show)+data TaxonomyTopic =+  TaxonomyTopic+    { taxonomyURI :: URIString+    , taxonomyLink :: URIString+    , taxonomyTitle :: Maybe Text+    , taxonomyDesc :: Maybe Text+    , taxonomyTopics :: [URIString]+    , taxonomyDC :: [DCItem]+    , taxonomyOther :: [XML.Element]+    }+  deriving (Show)  data UpdatePeriod   = Update_Hourly@@ -129,12 +141,14 @@   | Update_Yearly   deriving (Eq, Show) -data ContentInfo = ContentInfo-  { contentURI :: Maybe URIString-  , contentFormat :: Maybe URIString-  , contentEncoding :: Maybe URIString-  , contentValue :: Maybe Text -- should be: RDFValue-  } deriving (Eq, Show)+data ContentInfo =+  ContentInfo+    { contentURI :: Maybe URIString+    , contentFormat :: Maybe URIString+    , contentEncoding :: Maybe URIString+    , contentValue :: Maybe Text -- should be: RDFValue+    }+  deriving (Eq, Show)  --default constructors: nullFeed :: URIString -> TitleString -> Feed
tests/Example/CreateAtom.hs view
@@ -15,11 +15,12 @@ atomFeed :: Maybe Text atomFeed = renderFeed examplePosts -data Post = Post-  { _postedOn :: Text-  , _url :: Text-  , _content :: Text-  }+data Post =+  Post+    { _postedOn :: Text+    , _url :: Text+    , _content :: Text+    }  examplePosts :: [Post] examplePosts =
+ tests/ImportExport.hs view
@@ -0,0 +1,44 @@+module ImportExport+  ( importExportTests+  ) where++import Prelude.Compat++import Data.Generics (everywhere, mkT)+import Data.Text (strip)+import qualified Data.Text.Lazy.IO as T+import qualified Data.XML.Types as XML+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)+import Test.HUnit ((@=?))+import qualified Text.XML as C++import Text.Feed.Export (xmlFeed)+import Text.Feed.Import (readAtom)+import Text.Feed.Types (Feed)+import Text.RSS.Utils (elementToDoc)++import Paths_feed++importExportTests :: Test+importExportTests =+  testGroup "ImportExport" [testImportExport readAtom "tests/files/import_export_atom.xml"]++testImportExport :: (XML.Element -> Maybe Feed) -> FilePath -> Test+testImportExport readFeed fileName =+  testCase fileName $ do+    input <- T.readFile =<< getDataFileName fileName+    let inputXml = C.parseText_ C.def input+    let Just feed = readFeed $ C.toXMLElement $ C.documentRoot inputXml+    let Just outputXml = elementToDoc $ xmlFeed feed+    let output = C.renderText C.def outputXml+    let input' = C.renderText C.def $ stripXmlWhitespace inputXml+    input' @=? output++stripXmlWhitespace :: C.Document -> C.Document+stripXmlWhitespace = everywhere (mkT stripWhitespaceNodes)+  where+    stripWhitespaceNodes e = e {C.elementNodes = filter (not . isWhite) (C.elementNodes e)}+    isWhite (C.NodeContent t) = strip t == ""+    isWhite (C.NodeComment _) = True+    isWhite _ = False
tests/Main.hs view
@@ -5,10 +5,14 @@ import Prelude.Compat  import Example (exampleTests)+import ImportExport (importExportTests) import Test.Framework (defaultMain) import Text.Atom.Tests (atomTests)+import Text.Atom.Validate.Tests (atomValidateTests) import Text.Feed.Util.Tests (feedUtilTests) import Text.RSS.Tests (rssTests)  main :: IO ()-main = defaultMain [rssTests, atomTests, feedUtilTests, exampleTests]+main =+  defaultMain+    [rssTests, atomTests, atomValidateTests, feedUtilTests, exampleTests, importExportTests]
tests/Text/Atom/Tests.hs view
@@ -31,7 +31,7 @@     testAtom :: Assertion     testAtom = do       contents <- parseFeedFromFile =<< getDataFileName "tests/files/atom.xml"-      let res = fmap (renderText def) . elementToDoc . xmlFeed $ contents+      let res = fmap (renderText def) . (>>= elementToDoc) . fmap xmlFeed $ contents       assertBool "Atom Parsing" $ isJust res  testAtomAlternate :: Test
+ tests/Text/Atom/Validate/Tests.hs view
@@ -0,0 +1,34 @@+module Text.Atom.Validate.Tests+  ( atomValidateTests+  ) where++import Prelude.Compat++import Data.Text (Text)+import Data.Text.Lazy (fromStrict)++import Data.XML.Types++import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)+import Test.HUnit (Assertion, assertEqual)++import Text.Atom.Feed.Validate++import qualified Text.XML as C++atomValidateTests :: Test+atomValidateTests = testGroup "Text.Atom.Validate" [testAtomValidate]++sampleEntryText :: Text+sampleEntryText =+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?><entry xmlns=\"http://www.w3.org/2005/Atom\"><id>http://example.com</id><title type=\"text\">example</title><updated>2000-01-01T00:00:00Z</updated><author><name>Nobody</name></author><content type=\"xhtml\"><div xmlns=\"http://www.w3.org/1999/xhtml\">This is <b>XHTML</b> content.</div></content></entry>"++testAtomValidate :: Test+testAtomValidate = testCase "simple entry is valid" testValid+  where+    testValid :: Assertion+    testValid = do+      let document = C.toXMLDocument $ C.parseText_ C.def $ fromStrict sampleEntryText+      let entry = documentRoot document+      assertEqual "" [] $ flattenT $ validateEntry entry
tests/Text/RSS/Tests.hs view
@@ -30,5 +30,5 @@     testRss20 :: Assertion     testRss20 = do       contents <- parseFeedFromFile =<< getDataFileName "tests/files/rss20.xml"-      let res = fmap (renderText def) . elementToDoc . xmlFeed $ contents+      let res = fmap (renderText def) . (>>= elementToDoc) . fmap xmlFeed $ contents       assertBool "RSS 2.0 Parsing" $ isJust res
+ tests/doctest-driver.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF doctest-driver-gen -optF README.lhs -optF -pgmL -optF markdown-unlit -optF -XOverloadedStrings -optF -XNoImplicitPrelude -optF -package -optF text -optF -package -optF xml-types #-}
+ tests/files/import_export_atom.xml view
@@ -0,0 +1,35 @@+<?xml version="1.0" encoding="UTF-8"?>+<feed xmlns="http://www.w3.org/2005/Atom">+  <title type="text">Example Feed</title>+  <id>urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6</id>+  <updated>2003-12-13T18:30:02Z</updated>+  <link href="http://example.org/feed/" rel="self"/>+  <link href="http://example.org/"/>+  <subtitle type="text">A subtitle.</subtitle>+  <entry>+    <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>+    <title type="text">Atom-Powered Robots Run Amok</title>+    <updated>2003-12-13T18:30:00Z</updated>+    <author>+      <name>John Doe</name>+      <email>johndoe@example.com</email>+    </author>+    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>This is the entry content.</p></div></content>+    <link href="http://example.org/2003/12/13/atom03"/>+    <link href="http://example.org/2003/12/13/atom03.html" rel="alternate" type="text/html"/>+    <link href="http://example.org/2003/12/13/atom03/edit" rel="edit"/>+    <summary type="html">&lt;div&gt;&lt;p&gt;This is the entry content.&lt;/p&gt;&lt;/div&gt;</summary>+  </entry>+  <entry>+    <id>urn:uuid:f5ae72c9-b02e-42bc-957e-bbfe60b2c4a9</id>+    <title type="text">Robot-Powered Atoms Run Amok</title>+    <updated>2003-12-13T19:00:00Z</updated>+    <author>+      <name>John Doe</name>+      <email>johndoe@example.com</email>+    </author>+    <content type="html">&lt;div&gt;&lt;p&gt;This is the entry content.&lt;/p&gt;&lt;/div&gt;</content>+    <link href="http://example.org/2003/12/13/atom04"/>+    <summary type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>This is the entry content.</p></div></summary>+  </entry>+</feed>