packages feed

bumper-0.5: src/Package.hs

-- * Contains helper functions to load and manipulate .cabal files
{-# LANGUAGE
    TypeOperators
  , TemplateHaskell
  , TupleSections
  #-}
module Package where

import Prelude hiding (readFile)

import Control.Applicative
import Control.Monad
import Data.Label
import Data.List
import Data.List.Split
import Data.Maybe
import Data.Version
import Distribution.Package hiding (Package)
import Distribution.PackageDescription
import Distribution.PackageDescription.Configuration
import Distribution.PackageDescription.Parse
import Distribution.Text
import Distribution.Verbosity
import System.IO.Strict
import System.Process
import Text.Regex
import Version
import qualified Data.Map as M

data Package = Package
    { _name         :: PackageName
    , _path         :: String
    , _version      :: Version
    , _dependencies :: [Dependency]
    } deriving (Show, Eq)

$(mkLabels [''Package])

type Packages = [Package]

-- | Helper functions

lookupPackage :: PackageName -> Packages -> Maybe Package
lookupPackage s = find ((== s) . _name)

lookupPackages ::  [PackageName] -> Packages -> Packages
lookupPackages ns ps = catMaybes . map (flip lookupPackage ps) $ ns

hasPackage :: PackageName -> Packages -> Bool
hasPackage n = isJust . lookupPackage n

removePackage :: PackageName -> Packages -> Packages
removePackage s = filter ((/= s). _name)

removeAll :: [PackageName] -> Packages -> Packages
removeAll = flip $ foldr removePackage

-- | Loading packages
packages :: IO Packages
packages =
  do (_, hOut, _, _) <- runInteractiveCommand "find . -name *.cabal -type f"
     paths <- lines <$> hGetContents hOut
     forM paths $ \p ->
        do pkg <- flattenPackageDescription `liftM` readPackageDescription normal p
           return $ Package
                      { _name = pkgName $ package pkg
                      , _path = p
                      , _version = pkgVersion $ package pkg
                      , _dependencies = buildDepends pkg
                      }

getBaseVersions :: String -> Packages -> IO Packages
getBaseVersions ind ps =
 do (_, hOut, _, _) <- runInteractiveCommand $ "tar -tf " ++ ind
    gps <- lines <$> hGetContents hOut
    let vs = catMaybes $ map (parseVer . splitOn "/") gps
        parseVer (n:v:_) = fmap (PackageName n, ) $ simpleParse v
        parseVer _       = Nothing
        globver = M.fromListWith (\a b -> if a > b then a else b) vs
        updVer p = modify version (maybe id (\v -> if get version p < v then const v else id) $ M.lookup (_name p) globver) p
    return $ map updVer ps

-- | Manipulating package contents
whiteReg :: String
whiteReg = "[ \n\t]*"

modifyVersion :: Version -> String -> String
modifyVersion v s = subRegex (mkRegexWithOpts regex False False) s result
  where regex = "(version" ++ whiteReg ++ ":" ++ whiteReg ++ ") ([0-9.a-zA-Z]+)"
        result = "\\1 " ++ display v

modifyDependency :: Dependency -> String -> String
modifyDependency (Dependency nm range) s = subRegex (mkRegexWithOpts regex False False) s result
  where regex = "(build-depends" ++ whiteReg ++ ":" ++ "[^:]*"
              ++ "[ ,\n\t]" ++ display nm ++ whiteReg ++ ")[, ]([" ++ rangeChar ++ " \t\n]*[" ++ rangeChar ++ "])"
        rangeChar = "0-9.*&|()<>="
        result = "\\1 " ++ printRange range

-- | Data structure containing package modifications
type PackageChanges = (Maybe Version, [Dependency])

-- | Writing to packages
modifyPackage :: PackageChanges -> String -> String
modifyPackage (mv, deps) = flip (foldr modifyDependency) deps
                         . maybe id modifyVersion mv

updatePackage :: Package -> PackageChanges -> IO ()
updatePackage p ch = readFile (get path p) >>= writeFile (get path p) . modifyPackage ch