module Stackage.DiffPlans
( diffPlans
) where
import Data.Map (filterWithKey)
import Data.Text (justifyLeft)
import Data.Yaml (decodeFileEither)
import Network.HTTP.Client
import Network.HTTP.Simple (httpSink)
import Stackage.Prelude
import Data.Maybe
import Lucid
import System.Directory
data Change = Added | Deleted | MajorBump | MinorBump | Unchanged
deriving (Show, Eq, Ord)
data AndOr a = Old a | New a | Both a a
deriving Show
instance Semigroup (AndOr a) where
Old x <> New y = Both x y
New y <> Old x = Both x y
Old x <> Old _ = Old x
New x <> New _ = New x
Both x y <> _ = Both x y
Old x <> Both _ y = Both x y
New y <> Both x _ = Both x y
type DiffMap = Map Change (Map PackageName (Text,Maybe Text))
diffPlans :: FilePath
-> FilePath
-> Bool
-> Bool
-> Bool
-> Bool
-> IO ()
diffPlans oldFP newFP diffsOnly useColor True asHtml = do
(oldFP', newFP') <- (,) <$> getLTS oldFP <*> getLTS newFP
diffPlans oldFP' newFP' diffsOnly useColor False asHtml
delFile oldFP'
delFile newFP'
where
delFile fp = removeFile fp `catch` \(_::SomeException) -> return ()
diffPlans oldFP newFP diffsOnly useColor False asHtml = do
old <- fmap Old <$> parse oldFP
new <- fmap New <$> parse newFP
let combined = unionWith (<>) old new
m :: DiffMap
m = f . unionsWith mappend . map go $ mapToList combined
f = if diffsOnly
then filterWithKey (\k _ -> k /= Unchanged)
else id
if asHtml
then print $ htmlOut True m
else consoleOut useColor m
where
parse fp = decodeFileEither fp
>>= either throwIO (return . toSimple)
toSimple = fmap ppVersion . bpPackages
go (name, Old x) = singletonMap Deleted $ singletonMap name (display x, Nothing)
go (name, New x) = singletonMap Added $ singletonMap name (display x, Nothing)
go (name, Both x y)
| x == y = singletonMap Unchanged $ singletonMap name (display x, Nothing)
| otherwise = singletonMap
(if isMajor x y then MajorBump else MinorBump)
(singletonMap name $ (display x, Just $ display y))
isMajor :: Version -> Version -> Bool
isMajor (versionNumbers -> old) (versionNumbers -> new) =
toPair old /= toPair new
where
toPair [] = (0, 0)
toPair [i] = (i, 0)
toPair (i:j:_) = (i, j)
getLTS :: String -> IO FilePath
getLTS lts = do
createDirectoryIfMissing True tmpDir
req <- parseUrlThrow $ ltsRepo <> lts <> ".yaml"
runResourceT $ httpSink req $ const $ sinkFile fName
return fName
where
fName = tmpDir <> lts <> ".yaml"
ltsRepo = "https://raw.githubusercontent.com/fpco/lts-haskell/master/"
tmpDir = "/tmp/stackage-curator/"
colorize :: Bool -> Change -> Text -> Text
colorize useHtml change s =
case change of
Deleted -> red s
Added -> green s
Unchanged -> s
MajorBump -> yellow s
MinorBump -> blue s
where
showInColor consCol htmlColor s'
| useHtml = "color: " <> htmlColor
| otherwise = "\ESC[" <> consCol <> "m" <> s' <> "\ESC[0m"
red = showInColor "31" "red"
green = showInColor "32" "green"
yellow = showInColor "33" "yellow"
blue = showInColor "34" "blue"
consoleOut :: Bool -> DiffMap -> IO ()
consoleOut useColor m =
forM_ (mapToList m) $ \(change, m') -> do
print change
forM_ (mapToList m') $ \(pkg, (x,y)) ->
let pkgName' = (if useColor then colorize False change else id)
$ justifyLeft 25 ' ' $ display pkg
in putStrLn $ pkgName' <>
justifyLeft 9 ' ' x <>
if isJust y
then " => " <> fromJust y
else ""
putStrLn ""
htmlOut :: Bool -> DiffMap -> Html ()
htmlOut fullPage m = do
when fullPage $
doctypehtml_$ head_ $ do
meta_ [charset_ "utf-8"]
style_ "table, th, td {border : 1px solid black; border-collapse: collapse;}\
\th, td {padding: 5px; text-align: left;}"
body_ $
div_ [class_ "ltsDiffs"] $ do
h3_ "Differences"
forM_ (mapToList m) $ \(change, m') -> do
p_ [style_ $ colorize True change ""] $ toHtml $ show change
table_ $ forM_ (mapToList m') $ \(pkg, (x,y)) ->
tr_ $ do
td_ $ toHtml $ display pkg
td_ $ toHtml $ x
when (isJust y) $
td_ $ toHtml $ fromJust y
br_ []