{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Repology where
import Data.Aeson
import Data.HashMap.Strict
import Data.List
import Data.Proxy
import qualified Data.Text as T
import qualified Data.Text.IO
import qualified Data.Vector as V
import GHC.Generics
import Network.HTTP.Client.TLS (newTlsManager)
import OurPrelude
import Servant.API
import Servant.Client (BaseUrl (..), mkClientEnv, ClientM, Scheme (..), client, runClientM)
import System.IO
baseUrl :: BaseUrl
baseUrl = BaseUrl Https "repology.org" 443 "/api/v1"
type Metapackage = Vector Package
compareMetapackage :: Metapackage -> Metapackage -> Ordering
compareMetapackage ps1 ps2 = compareMetapackage' (ps1 V.!? 0) (ps2 V.!? 0)
where
compareMetapackage' (Just p1) (Just p2) = compare (name p1) (name p2)
compareMetapackage' Nothing (Just _) = LT
compareMetapackage' (Just _) Nothing = GT
compareMetapackage' _ _ = EQ
type Metapackages = HashMap Text Metapackage
type API =
"metapackage" :> Capture "metapackage_name" Text :> Get '[JSON] Metapackage :<|> "metapackages" :> QueryParam "search" Text :> QueryParam "maintainers" Text :> QueryParam "category" Text :> QueryParam "inrepo" Text :> QueryParam "outdated" Bool :> QueryParam "notinrepo" Text :> QueryParam "minspread" Integer :> QueryParam "maxspread" Integer :> Get '[JSON] Metapackages :<|> "metapackages" :> Capture "name" Text :> QueryParam "search" Text :> QueryParam "maintainers" Text :> QueryParam "category" Text :> QueryParam "inrepo" Text :> QueryParam "outdated" Bool :> QueryParam "notinrepo" Text :> QueryParam "minspread" Integer :> QueryParam "maxspread" Integer :> Get '[JSON] Metapackages
data Package = Package
{ repo :: Text,
name :: Maybe Text,
version :: Text,
origversion :: Maybe Text,
status :: Maybe Text,
summary :: Maybe Text,
categories :: Maybe (Vector Text),
licenses :: Maybe (Vector Text),
www :: Maybe (Vector Text),
downloads :: Maybe (Vector Text)
}
deriving (Eq, Show, Generic, FromJSON)
api :: Proxy API
api = Proxy
metapackage :: Text -> ClientM (Vector Package)
metapackages ::
Maybe Text ->
Maybe Text ->
Maybe Text ->
Maybe Text ->
Maybe Bool ->
Maybe Text ->
Maybe Integer ->
Maybe Integer ->
ClientM Metapackages
metapackages' ::
Text ->
Maybe Text ->
Maybe Text ->
Maybe Text ->
Maybe Text ->
Maybe Bool ->
Maybe Text ->
Maybe Integer ->
Maybe Integer ->
ClientM Metapackages
metapackage :<|> metapackages :<|> metapackages' = client api
-- type PagingResult = PagingResult (Vector Metapackage, ClientM PagingResult)
-- metapackages :: Text -> ClientM PagingResult
-- metapackages n = do
-- m <- ms n
-- return (lastMetapackageName m, sortedMetapackages m)
lastMetapackageName :: Metapackages -> Maybe Text
lastMetapackageName = keys >>> sort >>> Prelude.reverse >>> headMay
sortedMetapackages :: Metapackages -> Vector Metapackage
sortedMetapackages = elems >>> sortBy compareMetapackage >>> V.fromList
nixRepo :: Text
nixRepo = "nix_unstable"
nixOutdated :: ClientM Metapackages
nixOutdated =
metapackages
Nothing
Nothing
Nothing
(Just nixRepo)
(Just True)
Nothing
Nothing
Nothing
nextNixOutdated :: Text -> ClientM Metapackages
nextNixOutdated n =
metapackages'
n
Nothing
Nothing
Nothing
(Just nixRepo)
(Just True)
Nothing
Nothing
Nothing
outdatedForRepo :: Text -> Vector Package -> Maybe Package
outdatedForRepo r =
V.find (\p -> (status p) == Just "outdated" && (repo p) == r)
newest :: Vector Package -> Maybe Package
newest = V.find (\p -> (status p) == Just "newest")
dropMaybes :: [(Maybe Package, Maybe Package)] -> [(Package, Package)]
dropMaybes = Data.List.foldl' twoJusts []
where
twoJusts a (Just o, Just n) = (o, n) : a
twoJusts a _ = a
getUpdateInfo :: ClientM (Maybe Text, Bool, Vector (Package, Package))
getUpdateInfo = do
outdated <- nixOutdated
let ms = elems outdated
let nixPackages = fmap (outdatedForRepo nixRepo) ms
let newestPackages = fmap newest ms
let nixNew = dropMaybes (zip nixPackages newestPackages)
let mLastName = lastMetapackageName outdated
liftIO $ hPutStrLn stderr $ show mLastName
liftIO $ hPutStrLn stderr $ show (length ms)
return (mLastName, length ms /= 1, V.fromList nixNew)
-- let sorted = sortBy (\(p1,_) (p2,_) -> compare (name p1) (name p2)) nixNew
getNextUpdateInfo ::
Text -> ClientM (Maybe Text, Bool, Vector (Package, Package))
getNextUpdateInfo n = do
outdated <- nextNixOutdated n
let ms = elems outdated
let nixPackages = fmap (outdatedForRepo nixRepo) ms
let newestPackages = fmap newest ms
let nixNew = dropMaybes (zip nixPackages newestPackages)
let mLastName = lastMetapackageName outdated
liftIO $ hPutStrLn stderr $ show mLastName
liftIO $ hPutStrLn stderr $ show (length ms)
return (mLastName, length ms /= 1, V.fromList nixNew)
repologyUrl :: Text -> Text
repologyUrl pname = "https://repology.org/metapackage/" <> pname <> "/versions"
-- let sorted = sortBy (\(p1,_) (p2,_) -> compare (name p1) (name p2)) nixNew
updateInfo :: (Package, Package) -> Maybe Text
updateInfo (outdated, newestP) = do
pname <- name outdated
pure $ T.unwords [pname, version outdated, version newestP, repologyUrl pname]
justs :: Vector (Maybe a) -> Vector a
justs = V.concatMap (maybeToList >>> V.fromList)
moreNixUpdateInfo ::
(Maybe Text, Vector (Package, Package)) ->
ClientM (Vector (Package, Package))
moreNixUpdateInfo (Nothing, acc) = do
(mLastName, moreWork, newNix) <- getUpdateInfo
liftIO $
V.sequence_ $
fmap Data.Text.IO.putStrLn $
justs $
fmap updateInfo newNix
if moreWork
then moreNixUpdateInfo (mLastName, newNix V.++ acc)
else return acc
moreNixUpdateInfo (Just pname, acc) = do
(mLastName, moreWork, newNix) <- getNextUpdateInfo pname
liftIO $
V.sequence_ $
fmap Data.Text.IO.putStrLn $
justs $
fmap updateInfo newNix
if moreWork
then moreNixUpdateInfo (mLastName, newNix V.++ acc)
else return acc
allNixUpdateInfo :: ClientM (Vector (Package, Package))
allNixUpdateInfo = moreNixUpdateInfo (Nothing, V.empty)
fetch :: IO ()
fetch = do
hSetBuffering stdout LineBuffering
hSetBuffering stderr LineBuffering
liftIO $ hPutStrLn stderr "starting"
manager' <- newTlsManager
e <- runClientM allNixUpdateInfo (mkClientEnv manager' baseUrl)
case e of
Left ce -> liftIO $ hPutStrLn stderr $ show ce
Right _ -> liftIO $ hPutStrLn stderr $ "done"
return ()