cabal2nix-2.0: hackage2nix/HackageGit.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module HackageGit where
import Control.Monad
import Control.Lens hiding ( (<.>) )
import Data.Aeson
import Data.ByteString.Char8 ( ByteString )
import qualified Data.ByteString.Char8 as BS
import Data.ByteString.Lazy.Char8 ( fromStrict )
import Data.Digest.Pure.SHA ( sha256, showDigest )
import Data.Map as Map
import Data.Set as Set
import Data.String
import Data.String.UTF8 ( toString, fromRep )
import Distribution.Nixpkgs.Haskell.OrphanInstances ( )
import Distribution.Package
import Distribution.PackageDescription
import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
import Distribution.Text
import Distribution.Version
import System.Directory
import System.FilePath
type Hackage = Map PackageName (Set Version)
readHackage :: FilePath -> IO Hackage
readHackage path = getSubDirs path >>= foldM discoverPackageVersions mempty
where
discoverPackageVersions :: Hackage -> String -> IO Hackage
discoverPackageVersions db pkg = do
vs <- getSubDirs (path </> pkg)
return (Map.insert (PackageName pkg) (Set.fromList (Prelude.map fromString vs)) db)
getSubDirs :: FilePath -> IO [FilePath]
getSubDirs path = do
let isDirectory p = doesDirectoryExist (path </> p)
getDirectoryContents path >>= filterM isDirectory . Prelude.filter (\x -> head x /= '.')
decodeUTF8 :: ByteString -> String
decodeUTF8 = toString . fromRep
type SHA256Hash = String
readPackage :: FilePath -> PackageIdentifier -> IO (GenericPackageDescription, SHA256Hash)
readPackage dirPrefix (PackageIdentifier name version) = do
let cabalFile = dirPrefix </> unPackageName name </> display version </> unPackageName name <.> "cabal"
buf <- BS.readFile cabalFile
cabal <- case parsePackageDescription (decodeUTF8 buf) of
ParseOk _ a -> return a
ParseFailed err -> fail (cabalFile ++ ": " ++ show err)
return (cabal, mkSHA256 buf)
mkSHA256 :: ByteString -> SHA256Hash
mkSHA256 = showDigest . sha256 . fromStrict
declareLenses [d|
data Meta = Meta { hashes :: Map String String
, locations :: [String]
, pkgsize :: Int
}
deriving (Show)
|]
instance FromJSON Meta where
parseJSON (Object v) = Meta
<$> v .: "package-hashes"
<*> v .: "package-locations"
<*> v .: "package-size"
parseJSON o = fail ("invalid Cabal metadata: " ++ show o)
readPackageMeta :: FilePath -> PackageIdentifier -> IO Meta
readPackageMeta dirPrefix (PackageIdentifier name version) = do
let metaFile = dirPrefix </> unPackageName name </> display version </> unPackageName name <.> "json"
buf <- BS.readFile metaFile
case eitherDecodeStrict buf of
Left msg -> fail (metaFile ++ ": " ++ msg)
Right x -> return x