packages feed

thank-you-stars-1.0.0: src/Utils/ThankYouStars/Package.hs

module Utils.ThankYouStars.Package (
      dependentRepos
    , getCabalFiles
    , readCabalFile
    , readHackageDB
    ) where

import Utils.ThankYouStars.GitHub

import           Data.List
    ( isInfixOf
    , isPrefixOf
    )
import           Data.List.Split                               ( splitOneOf )
import qualified Data.Map                                      as M
import           Data.Maybe
import qualified Data.Set                                      as S
import           Distribution.Hackage.DB
    ( HackageDB
    , cabalFile
    , hackageTarball
    , readTarball
    )
import           Distribution.Package
import           Distribution.PackageDescription
import           Distribution.PackageDescription.Configuration
    ( flattenPackageDescription
    )
import           Distribution.PackageDescription.Parsec
    ( readGenericPackageDescription
    )
import           Distribution.Verbosity                        ( normal )
import           System.Directory
    ( getCurrentDirectory
    , getPermissions
    , listDirectory
    , searchable
    )
import           System.FilePath
    ( combine
    , takeExtension
    )

getCabalFiles :: IO (S.Set FilePath)
getCabalFiles = getCurrentDirectory >>= searchCabalFiles

searchCabalFiles :: FilePath -> IO (S.Set FilePath)
searchCabalFiles fp = do
    p <- getPermissions fp
    if searchable p
        then do
            children <- map (combine fp) . filter visible <$> listDirectory fp
            S.unions <$> mapM searchCabalFiles children
        else do
            if takeExtension fp == ".cabal"
                then return $ S.singleton fp
                else return $ S.empty

visible :: FilePath -> Bool
visible fp = not $ "." `isPrefixOf` fp

readCabalFile :: FilePath -> IO GenericPackageDescription
readCabalFile = readGenericPackageDescription normal

dependentRepos :: HackageDB -> GenericPackageDescription -> S.Set GitHubRepo
dependentRepos db desc = S.map fromJust $ S.filter isJust mRepos
    where
        excepts = [mkPackageName "base", packageName desc]
        pkgs    = foldr S.delete (allDependencies desc) excepts
        mRepos  = S.map (flip lookupRepo $ db) pkgs

allDependencies :: GenericPackageDescription -> S.Set PackageName
allDependencies = S.fromList . map toPackageName . allBuildDepends . flattenPackageDescription
    where
        toPackageName (Dependency name _ _) = name

lookupRepo :: PackageName -> HackageDB -> Maybe GitHubRepo
lookupRepo pkg db = listToMaybe . catMaybes . map parseRepo $ repos
    where
        repos   = fromMaybe [] $ toRepos <$> M.lookup pkg db
        toRepos = sourceRepos . flattenPackageDescription . cabalFile . snd . M.findMax

parseRepo :: SourceRepo -> Maybe GitHubRepo
parseRepo src = case (repoType src, repoLocation src) of
    (Just Git, Just loc) -> parseLocation loc
    _                    -> Nothing

-- TODO: Too naive parsing
parseLocation :: String -> Maybe GitHubRepo
parseLocation loc
    | isGitHub && length ps > 5 =
        Just $ GitHubRepo { owner = ps !! 4, repo = ps !! 5 }
    | otherwise     = Nothing
    where
        isGitHub = isInfixOf "github.com" loc
        ps       = splitOneOf "/." loc

readHackageDB :: IO HackageDB
readHackageDB = hackageTarball >>= readTarball Nothing