{-# LANGUAGE DeriveGeneric, GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoImplicitPrelude #-}
module OpenSuse.Types.ChangeLog
( ChangeLog(..), Entry(..)
, parseEntry, parseDashedLine, parseDateAddressLine, parseDescription
)
where
import OpenSuse.Prelude
import OpenSuse.Prelude.Parser as Parse
import qualified OpenSuse.Prelude.PrettyPrinting as Pretty ( )
import OpenSuse.Types.EMailAddress
import Data.Time.Format
newtype ChangeLog = ChangeLog [Entry]
deriving (Show, Eq, Ord, Generic, NFData, Semigroup, Monoid)
instance HasParser ChangeLog where
parser = ChangeLog <$> (parseDashedLine *> many parser)
data Entry = Entry
{ changedAt :: UTCTime
, changedBy :: EMailAddress
, changeDescription :: Text
}
deriving (Show, Eq, Ord, Generic)
instance NFData Entry
instance HasParser Entry where
parser = parseEntry
-- * Useful parsers for ChangeLog elements
{-# ANN parseEntry "HLint: ignore Use <$>" #-}
parseEntry :: CharParser st input m Entry
parseEntry = do
(ts,author) <- parseDateAddressLine
parseEmptyLine
txt <- parseDescription
return (Entry ts author (packText txt))
parseDashedLine :: CharParser st input m ()
parseDashedLine = do
_ <- string "------------------------------------------------------------"
skipMany1 (char '-')
_ <- endOfLine
return ()
-- | Note that the input must be terminated by a newline.
--
-- >>> parseTest parseDateAddressLine "Wed Jun 27 09:25:07 UTC 2018 - foo@example.org\n"
-- (2018-06-27 09:25:07 UTC,EMailAddress "foo@example.org")
parseDateAddressLine :: CharParser st input m (UTCTime, EMailAddress)
parseDateAddressLine = do
ts <- many1 (alphaNum <|> oneOf ": ")
_ <- char '-'
addr <- parser
_ <- endOfLine
utct <- parseTimeM True defaultTimeLocale changeLogDateFormat ts
return (utct,addr)
-- | Consume an empty line, i.e. a line that contains only whitespace.
parseEmptyLine :: CharParser st input m ()
parseEmptyLine = skipMany (oneOf " \t") <* endOfLine
-- | Consume all text until the end of the file or a dashed line is found. In
-- the latter case, the dashed line is consumed as well. This is unfortunate,
-- but it's how the 'notFollowedBy' combinator works, unfortunately,
parseDescription :: CharParser st input m String
parseDescription = manyTill anyChar (try parseDashedLine <|> eof)
-- | Appropriate format parameter for 'formatTime' and 'parseTimeM'.
changeLogDateFormat :: String
changeLogDateFormat = "%a %b %e %H:%M:%S %Z %Y"