ureader 0.1.0.0 → 0.2.0.0
raw patch · 7 files changed
+167/−30 lines, 7 filesdep +deepseqdep +opml
Dependencies added: deepseq, opml
Files
- LICENSE +2/−2
- src/Main.hs +27/−16
- src/UReader/Localization.hs +13/−0
- src/UReader/Options.hs +51/−5
- src/UReader/RSS.hs +9/−0
- src/UReader/Rendering.hs +56/−2
- ureader.cabal +9/−5
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2013, Sam T.+Copyright (c) 2013, Sam Truzjan All rights reserved. @@ -13,7 +13,7 @@ disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Sam T. nor the names of other+ * Neither the name of Sam Truzjan nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission.
src/Main.hs view
@@ -1,5 +1,7 @@ module Main (main) where +import Prelude as P+ import Control.Applicative as A import Control.Concurrent import Control.Concurrent.Async@@ -8,6 +10,7 @@ import Data.Default import Data.List as L import Data.Maybe+import Data.Monoid import Data.Time import Data.Time.Clock.POSIX import Data.Version (showVersion)@@ -22,27 +25,21 @@ import UReader.Localization import UReader.Options+import UReader.Outline import UReader.Rendering import UReader.RSS -parseFeedList :: String -> [URI]-parseFeedList = mapMaybe parseURI . L.lines--getFeedList :: FilePath -> IO [URI]-getFeedList feedList = parseFeedList <$> Prelude.readFile feedList-- getLastSeen :: FilePath -> IO UTCTime getLastSeen lastPath = do exist <- doesFileExist lastPath if exist then do- !mLastSeen <- parsePubDate <$> Prelude.readFile lastPath- Prelude.writeFile lastPath . formatPubDate =<< getCurrentTime+ !mLastSeen <- parsePubDate <$> P.readFile lastPath+ P.writeFile lastPath . formatPubDate =<< getCurrentTime return $ fromMaybe epochStart mLastSeen else do- Prelude.writeFile lastPath . formatPubDate =<< getCurrentTime+ P.writeFile lastPath . formatPubDate =<< getCurrentTime return epochStart where epochStart = posixSecondsToUTCTime 0@@ -69,7 +66,8 @@ let userFeeds = L.map (filterItems isNew) feeds unless (L.all emptyFeed userFeeds) $ do localTime <- utcToLocalTime <$> getCurrentTimeZone <*> pure lastSeen- print $ green $ "Showed from:" <+> text (formatPubDate localTime)+ print $ green $ linebreak <>+ "Showed from:" <+> text (formatPubDate localTime) return userFeeds previewFeed :: URI -> IO ()@@ -79,6 +77,7 @@ showBatch style @ Style {..} feedList uris = do renderRSS style =<< setCurrentZone =<< (if newOnly then filterNew feedList else fetch Nothing) uris+ putStrLn ([] :: String) streamStyle :: Style streamStyle = Style@@ -105,12 +104,24 @@ streamFeeds feedList interval uris = pollBy interval $ do updateStream feedList uris +addFeed :: FilePath -> URI -> String -> String -> IO ()+addFeed filePath uri grp topic+ = modifyOPML filePath (insertURI [grp, topic] uri)++putVersion :: IO ()+putVersion = P.putStrLn $ "ureader version " ++ showVersion version+ run :: Options -> IO ()-run Add {..} = appendFile feedList $ show feedURI ++ "\n"-run Batch {..} = getFeedList feedList >>= showBatch feedStyle feedList-run Preview {..} = previewFeed feedURI-run Stream {..} = getFeedList feedList >>= streamFeeds feedList feedInterval-run Version = putStrLn $ "ureader version " ++ showVersion version+run Add {..} = addFeed feedList feedURI+ feedParent feedTopic+run Batch {..} = getFeedList feedList feedGroup+ >>= showBatch feedStyle feedList+run Index {..} = getIndex feedList feedGroup+ >>= renderFeedList+run Preview {..} = previewFeed feedURI+run Stream {..} = getFeedList feedList feedGroup+ >>= streamFeeds feedList feedInterval+run Version = putVersion main :: IO () main = getOptions >>= run
src/UReader/Localization.hs view
@@ -1,3 +1,16 @@+-- |+-- Copyright : (c) Sam Truzjan 2013+-- License : BSD3+-- Maintainer : pxqr.sta@gmail.com+-- Stability : experimental+-- Portability : portable+--+-- This module provides simple localization for feed items.+-- Currently an UTC time (pubdate) translated to the LocalTime which+-- user expect to see.+--+-- +0000(UTC) -> +0004(MSK)+-- {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE OverlappingInstances #-} module UReader.Localization
src/UReader/Options.hs view
@@ -1,7 +1,16 @@+-- |+-- Copyright : (c) Sam Truzjan 2013+-- License : BSD3+-- Maintainer : pxqr.sta@gmail.com+-- Stability : experimental+-- Portability : portable+--+-- All stuff relate to command line options should be in this+-- module: parsers, help, reasonable defaults.+-- module UReader.Options ( Order (..) , Style (..)- , Options (..) , getOptions ) where@@ -15,6 +24,7 @@ import System.FilePath import UReader.Rendering+import UReader.Outline styleParser :: Parser Style@@ -43,7 +53,7 @@ ) feedListParser :: Implicit_ FilePath => Parser FilePath-feedListParser = option+feedListParser = strOption ( long "feeds" <> metavar "PATH" <> value param_ <> showDefault@@ -65,22 +75,49 @@ <> help "Minimal interval between feed updates" ) +feedGroupParser :: Parser Selector+feedGroupParser = optional $ argument Just+ ( metavar "GROUP"+ <> help "Feed group used to specify some subset of feeds"+ )++feedParentParser :: Parser String+feedParentParser = argument Just+ ( metavar "GROUP"+ <> help "The group feed belongs to"+ )++feedTopicParser :: Parser String+feedTopicParser = argument Just+ ( metavar "TOPIC"+ <> help "The main feed topic used as feed name"+ )+ data Options = Add { feedList :: FilePath+ , feedParent :: String+ , feedTopic :: String , feedURI :: URI } | Batch { feedList :: FilePath , feedStyle :: Style+ , feedGroup :: Selector } | Preview { feedURI :: URI } | Stream { feedList :: FilePath , feedInterval :: Int+ , feedGroup :: Selector }+ | Index { feedList :: FilePath+ , feedGroup :: Selector+ } | Version deriving (Show, Eq) addParser :: Implicit_ String => Parser Options-addParser = Add <$> feedListParser <*> feedLinkParser+addParser = Add <$> feedListParser+ <*> feedParentParser <*> feedTopicParser+ <*> feedLinkParser addInfo :: Implicit_ String => ParserInfo Options addInfo = info (helper <*> addParser) modifier@@ -88,7 +125,7 @@ modifier = progDesc "Add a feed to the feed list" streamParser :: Implicit_ String => Parser Options-streamParser = Stream <$> feedListParser <*> intervalTime+streamParser = Stream <$> feedListParser <*> intervalTime <*> feedGroupParser streamInfo :: Implicit_ String => ParserInfo Options streamInfo = info (helper <*> streamParser) modifier@@ -96,7 +133,7 @@ modifier = progDesc "Show feed as never ending stream" feedParser :: Implicit_ String => Parser Options-feedParser = Batch <$> feedListParser <*> styleParser+feedParser = Batch <$> feedListParser <*> styleParser <*> feedGroupParser feedInfo :: Implicit_ String => ParserInfo Options feedInfo = info (helper <*> feedParser) modifier@@ -111,6 +148,14 @@ where modifier = progDesc "Show feed specified by URI or file path" +indexParser :: Implicit_ FilePath => Parser Options+indexParser = Index <$> feedListParser <*> feedGroupParser++indexInfo :: Implicit_ FilePath => ParserInfo Options+indexInfo = info (helper <*> indexParser) modifier+ where+ modifier = progDesc "Show your feed list"+ versionParser :: Parser Options versionParser = flag' Version ( long "version"@@ -123,6 +168,7 @@ optionsParser = subparser $ mconcat [ command "add" addInfo , command "feed" feedInfo+ , command "index" indexInfo , command "stream" streamInfo , command "view" previewInfo ]
src/UReader/RSS.hs view
@@ -1,3 +1,12 @@+-- |+-- Copyright : (c) Sam Truzjan 2013+-- License : BSD3+-- Maintainer : pxqr.sta@gmail.com+-- Stability : experimental+-- Portability : portable+--+-- This module provides network related stuff.+-- module UReader.RSS ( filterItems , reverseItems
src/UReader/Rendering.hs view
@@ -1,8 +1,19 @@+-- |+-- Copyright : (c) Sam Truzjan 2013+-- License : BSD3+-- Maintainer : pxqr.sta@gmail.com+-- Stability : experimental+-- Portability : portable+--+-- This module provides colored (ansi-terminal compatible) rendering+-- for OPML, RSS, (Atom not yet) and HTML.+-- {-# OPTIONS -fno-warn-orphans #-} module UReader.Rendering ( Order (..) , Style (..) , renderRSS+ , renderFeedList ) where import Control.Applicative@@ -16,16 +27,59 @@ import Data.List.Split as L import Data.Set as S import Text.HTML.TagSoup+import Text.OPML.Syntax import Text.PrettyPrint.ANSI.Leijen hiding ((<$>), (<>), width) import Text.RSS.Syntax import Text.XML.Light.Types+import Network.URI import System.IO import System.Console.Terminal.Size as Terminal import UReader.RSS import UReader.Localization+import UReader.Outline +{-----------------------------------------------------------------------+ Feed list+-----------------------------------------------------------------------} +renderFeedList :: OPML -> IO ()+renderFeedList = print . pretty++instance Pretty Outline where+ pretty Outline {..} =+ fill 28 (topicTy (pretty opmlText))+ <+> ppURI opmlOutlineAttrs <>+ if L.null opmlOutlineChildren then mempty else linebreak <>+ indent 4 (vsep $ L.map pretty opmlOutlineChildren)+-- , "type" <+> pretty opmlType+-- , "categories" <+> pretty opmlCategories+-- , "comment" <+> pretty opmlIsComment+-- , "breakpoint" <+> pretty opmlIsBreakpoint+-- , "other" <+> pretty (show opmlOutlineOther)+ where+ topicTy+ | L.null opmlOutlineChildren = blue . underline+ | otherwise = magenta . bold++ ppURI (lookupAttr uriQName -> Just uriStr)+ | Just uri <- parseURI uriStr = "<" <> text (show uri) <> ">"+ | otherwise = red "invalid URL"+ ppURI _ = mempty++instance Pretty OPMLHead where+ pretty OPMLHead {..} = pretty opmlTitle++instance Pretty OPML where+ pretty OPML {..}+ = --"version" <+> text opmlVersion </>+ --"head " <+> pretty opmlHead </>+ vsep (L.map pretty opmlBody)++{-----------------------------------------------------------------------+ Feed+-----------------------------------------------------------------------}+ data Order = NewFirst | OldFirst deriving (Show, Read, Eq, Ord, Bounded, Enum)@@ -138,8 +192,8 @@ instance Pretty RSSCategory where pretty RSSCategory {..} =- dullyellow (maybe mempty text rssCategoryDomain) <>- dullred (hsep $ L.map pretty rssCategoryAttrs) <>+ dullyellow (maybe mempty text rssCategoryDomain) <>+ dullred (hsep $ L.map pretty rssCategoryAttrs) <> dullblue (text rssCategoryValue) instance Pretty Attr where
ureader.cabal view
@@ -1,10 +1,10 @@ name: ureader-version: 0.1.0.0+version: 0.2.0.0 license: BSD3 license-file: LICENSE-author: Sam T.-maintainer: Sam T. <pxqr.sta@gmail.com>-copyright: (c) 2013, Sam T.+author: Sam Truzjan+maintainer: Sam Truzjan <pxqr.sta@gmail.com>+copyright: (c) 2013, Sam Truzjan category: RSS build-type: Simple cabal-version: >= 1.10@@ -18,11 +18,13 @@ and color support. Everything it does is fetch RSS documents, merge them according to specified options, format and flush resulting feed to stdout. So `ureader` could be used with- pagers like `more(1)` or in linux terminal.+ pagers like more(1) or in linux terminal. . [/Release Notes/] . * /0.1.0.0:/ Initial version.+ .+ * /0.2.0.0:/ Use OPML for subscription list. source-repository head@@ -49,6 +51,7 @@ build-depends: base == 4.* , implicit-params == 0.2.* , data-default+ , deepseq , async , parallel-io == 0.3.*@@ -71,6 +74,7 @@ , optparse-applicative == 0.5.* , terminal-size == 0.2.* + , opml == 0.4.* , xml == 1.3.* , feed == 0.3.* , tagsoup == 0.12.*