Created
April 17, 2016 15:54
-
-
Save tfausak/48a10d6a5724ddb0df3908e27e3a6d21 to your computer and use it in GitHub Desktop.
Statistics about major version numbers of Haskell packages on Hackage.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env stack | |
{- stack --install-ghc --resolver lts-5 runghc | |
--package containers | |
--package HTTP | |
--package network-uri | |
--package tar | |
--package text | |
--package zlib | |
-} | |
{-# LANGUAGE OverloadedStrings #-} | |
import qualified Codec.Archive.Tar as Tar | |
import qualified Codec.Compression.GZip as GZip | |
import qualified Control.Exception as Exception | |
import qualified Data.List as List | |
import qualified Data.Map as Map | |
import qualified Data.Text as Text | |
import qualified Data.Version as Version | |
import qualified Network.HTTP as HTTP | |
import qualified Network.URI as URI | |
import qualified Text.ParserCombinators.ReadP as ReadP | |
main :: IO () | |
main = do | |
-- Build a request to get the package index. | |
let method = HTTP.GET | |
let index = "http://hackage.haskell.org/packages/index.tar.gz" | |
let Just uri = URI.parseURI index | |
let request = HTTP.mkRequest method uri | |
-- Make the request, decompress it, and unpack it. | |
result <- HTTP.simpleHTTP request | |
body <- HTTP.getResponseBody result | |
let archive = GZip.decompress body | |
let entries = Tar.read archive | |
-- Parse a textual version number. | |
let parseVersion text = case reverse (ReadP.readP_to_S Version.parseVersion (Text.unpack text)) of | |
(version, "") : _ -> Just (version :: Version.Version) | |
_ -> Nothing | |
-- Convert the list of entries into a map of package names to versions. | |
let step entry x = case Tar.entryContent entry of | |
Tar.NormalFile _ _ -> let | |
path = Text.pack (Tar.entryPath entry) | |
in if Text.isSuffixOf ".cabal" path | |
then case Text.splitOn "/" path of | |
[package, v, _] -> case parseVersion v of | |
Just version -> Map.insertWith (++) package [version] x | |
_ -> x | |
_ -> x | |
else x | |
_ -> x | |
let initial = Map.empty | |
let handle formatError = Exception.throw formatError | |
let versions = Tar.foldEntries step initial handle entries | |
-- Print out the total number of packages. | |
putStrLn "Number of packages:" | |
print (Map.size versions) | |
putStrLn "" | |
-- Print out the major version numbers along with how often they show up. | |
let majors = Map.foldr | |
(\ e a -> Map.insertWith (+) (head (Version.versionBranch (last (List.sort e)))) (1 :: Int) a) | |
Map.empty | |
versions | |
putStrLn "Major version numbers:" | |
mapM_ (\ (k, v) -> putStrLn (show k ++ "\t" ++ show v)) (Map.toAscList majors) | |
putStrLn "" | |
-- Print out packages that have changed their major version number. | |
let changes = Map.foldrWithKey | |
(\ k e a -> let | |
x = List.nub (map head (map Version.versionBranch (List.sort e))) | |
in if length x == 1 then a else Map.insert k x a) | |
Map.empty | |
versions | |
putStrLn "Major version number changes:" | |
mapM_ (\ (k, v) -> putStrLn (Text.unpack k ++ "\t" ++ unwords (map show v))) (Map.toAscList changes) | |
putStrLn "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment