packages feed

gen-imports-0.1.0.0: src/Distribution/Hackage/Imports/Generate.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-}

module Distribution.Hackage.Imports.Generate where

import Data.ByteString (ByteString)

import Distribution.Hackage.DB (Hackage, Version, readHackage)
import Distribution.PackageDescription (
  GenericPackageDescription(packageDescription, condLibrary),
  PackageDescription(library),
  Library(exposedModules),
  CondTree(CondNode, condTreeData)
  )
import Distribution.ModuleName (ModuleName)
import qualified Distribution.ModuleName
import Distribution.Version (Version(versionBranch))
import Distribution.Text (disp)
import qualified Data.Map.Lazy
import Control.Arrow (second)
import Text.PrettyPrint (Doc, text, ($+$), render)
import GHC.Exts (sortWith, Down(Down))
import Data.Monoid ((<>), mempty)
import Data.Foldable (foldl')
import System.FilePath (FilePath, (</>))
import Data.String (fromString)
import Data.Char (toUpper)
import Data.Foldable (traverse_)
import Data.List (intercalate)
import Debug.Trace (traceShowId)

type PackageName = String

class ToString a where
  toString :: a -> String

instance ToString String where
  toString = id

stringToFilePath :: String -> FilePath
stringToFilePath = id

getExposedModules :: GenericPackageDescription -> [ModuleName]
getExposedModules gpd = let
  libs1 = case library (packageDescription gpd) of
    Nothing -> []
    Just l -> exposedModules l
  libs2 = case condLibrary gpd of
    Nothing -> []
    Just (CondNode{condTreeData=l}) -> exposedModules l
  in
    libs1 ++ libs2

getExposedModulesAllVersions :: Hackage -> PackageName -> [(Version, [ModuleName])]
getExposedModulesAllVersions hackage pn = case Data.Map.Lazy.lookup pn hackage of
  Nothing -> []
  Just gpdm -> (second getExposedModules) <$> Data.Map.Lazy.toList gpdm

genImportList :: (ModuleName -> Doc) -> ModuleName -> PackageName -> [(Version, [ModuleName])] -> Doc
genImportList f moduleName packageName l = languageCpp $+$ moduleHeader $+$ guard $+$ body $+$ endIf $+$ "#endif" where
  languageCpp = "{-# LANGUAGE CPP #-}"
  guard = "#ifdef MIN_VERSION_" <> packageNameDoc
  packageNameDoc = text (replace_dash <$> toString packageName)
  moduleHeader = "module " <> disp moduleName <> " where"
  endIf = case l of
    [] -> mempty
    _ -> "#endif"
  body = (foldl' ($+$) mempty $ zipWith g ("#if":(repeat "#elif")) (sortWith Down l))
  g macro (version, module_list) = preprocessorLine $+$ modules where
    preprocessorLine = macro <> " MIN_VERSION_" <> packageNameDoc <> "(" <> v1 <> "," <> v2 <> "," <> v3 <> ")"
    modules = foldl' ($+$) mempty (f <$> module_list)
    [v1, v2, v3] = (text . show) <$> (take 3 (versionBranch version ++ [0..]))


genImportListFromHackage :: Hackage -> ModuleName -> PackageName -> Doc
genImportListFromHackage hackage moduleName packageName = genImportList toImportLine moduleName packageName (getExposedModulesAllVersions hackage packageName) where

toImportLine :: ModuleName -> Doc
toImportLine mn = "import " <> disp mn <> " ()"

writeImportListToFile :: Hackage -> FilePath -> [String] -> PackageName  -> IO ()
writeImportListToFile hackage basePath baseModuleStrList packageName =
  writeFile moduleFileName $ render (genImportListFromHackage hackage moduleName packageName) where
    capitalisedPackageName = let (s:ss) = toString packageName in fromString (((toUpper s):(replace_dash <$> ss)))
    capitalisedPackageFileName = capitalisedPackageName <> ".hs"
    moduleFileName = (foldl' (</>) basePath (map stringToFilePath baseModuleStrList)) </> capitalisedPackageFileName
    moduleName = Distribution.ModuleName.fromString ((intercalate "." baseModuleStrList) <> "." <> capitalisedPackageName)

replace_dash :: Char -> Char
replace_dash c = case c of
  '-' -> '_'
  x -> x

writeImportListsToDir :: Foldable f => FilePath -> [String] -> f PackageName -> IO ()
writeImportListsToDir basePath baseModuleStrList packageNames = do
  hackage <- readHackage
  traverse_ (writeImportListToFile hackage basePath baseModuleStrList) packageNames

writeGHCPackageImportListsToDir :: FilePath -> [String] -> IO ()
writeGHCPackageImportListsToDir basePath baseModuleStrList = writeImportListsToDir basePath baseModuleStrList ghcPackages

ghcPackages :: [PackageName]
ghcPackages = [
  "array",
  "base",
  "binary",
  "bytestring",
  "Cabal",
  "containers",
  "deepseq",
  "directory",
  "filepath",
  "ghc",
  "ghc-boot",
  "ghc-compact",
  "ghc-prim",
  "haskell98",
  "haskell2010",
  "hoopl",
  "hpc",
  "old-locale",
  "old-time",
  "integer-gmp",
  "process",
  "template-haskell",
  "time",
  "unix",
  "Win32"
  ]

{-
getExposedModulesAllVersionsIO :: PackageName -> IO [(Version, [ModuleName])]
getExposedModulesAllVersionsIO pn = do
  hackage <- readHackage
  pure $ getExposedModulesAllVersions hackage pn
-}