packages feed

hsec-sync-0.1.0.0: src/Security/Advisories/Sync/Atom.hs

module Security.Advisories.Sync.Atom
  ( latestUpdate,
  )
where

import Control.Exception (try)
import Control.Lens
import Data.Either.Extra (maybeToEither)
import qualified Data.Text as T
import Data.Time (UTCTime, defaultTimeLocale, parseTimeM, rfc822DateFormat)
import Data.Time.Format.ISO8601 (iso8601ParseM)
import Network.HTTP.Client (HttpException (..), HttpExceptionContent (..))
import Network.Wreq
import qualified Text.Atom.Feed as FeedAtom
import qualified Text.Feed.Import as FeedImport
import qualified Text.Feed.Types as FeedTypes
import qualified Text.RSS.Syntax as FeedRSS

latestUpdate :: String -> String -> IO (Either String UTCTime)
latestUpdate repoUrl branch = do
  resultE <- try $ get $ repoUrl </> "commits" </> branch </> "advisories.atom"
  return $
    case resultE of
      Left e ->
        Left $
          case e of
            InvalidUrlException url reason ->
              "Invalid URL " <> show url <> ": " <> show reason
            HttpExceptionRequest _ content ->
              case content of
                StatusCodeException response body ->
                  "Request failed with " <> show (response ^. responseStatus) <> ": " <> show body
                _ ->
                  "Request failed: " <> show content
      Right result ->
        case FeedImport.parseFeedSource $ result ^. responseBody of
          Just (FeedTypes.AtomFeed x) ->
            maybeToEither "Invalid feed date" $
              iso8601ParseM $
                T.unpack $
                  FeedAtom.feedUpdated x
          Just (FeedTypes.RSSFeed x) ->
            maybeToEither "Invalid feed date" $
              FeedRSS.rssLastUpdate (FeedRSS.rssChannel x)
                >>= parseTimeM True defaultTimeLocale rfc822DateFormat . T.unpack
          Just (FeedTypes.RSS1Feed _) -> Left "RSS1 feed are not supported"
          Just (FeedTypes.XMLFeed _) -> Left "XML feed are not supported"
          Nothing -> Left "No feed found"

infixr 5 </>

(</>) :: String -> String -> String
"/" </> ('/' : ys) = '/' : ys
"/" </> ys = '/' : ys
"" </> ('/' : ys) = '/' : ys
"" </> ys = '/' : ys
[x] </> ('/' : ys) = x : '/' : ys
[x] </> ys = x : '/' : ys
(x0 : x1 : xs) </> ys = x0 : ((x1 : xs) </> ys)