packages feed

cblrepo-0.18.0: src/Add.hs

{-
 - Copyright 2011-2014 Per Magnus Therning
 -
 - Licensed under the Apache License, Version 2.0 (the "License");
 - you may not use this file except in compliance with the License.
 - You may obtain a copy of the License at
 -
 -     http://www.apache.org/licenses/LICENSE-2.0
 -
 - Unless required by applicable law or agreed to in writing, software
 - distributed under the License is distributed on an "AS IS" BASIS,
 - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 - See the License for the specific language governing permissions and
 - limitations under the License.
 -}

module Add where

-- {{{1 imports
-- {{{2 local
import PkgDB
import qualified Util.Cabal as Cbl
import Util.Misc
import Util.Dist
import Util.Cfg

-- {{{2 system
import Control.Monad.Reader
import Data.List
import Data.Maybe
import Distribution.PackageDescription
import Distribution.Version
import qualified Distribution.Package as P
import Control.Arrow
import Data.Monoid
import Data.Either
import System.Unix.Directory

-- {{{1 types
data PkgType
    = GhcType String Version
    | DistroType String Version Int Int
    | RepoType GenericPackageDescription
    deriving (Eq, Show)

-- {{{1 add
add :: Command ()
add = do
    dbFn <- asks $ dbFile . fst
    db <- liftIO $ readDb dbFn
    dr <- asks $ dryRun . fst
    ghcVersion <- asks $ ghcVer . optsCmd . fst
    filePkgs <- asks $ cmdAddFileCbls . optsCmd . fst
    idxPkgs <- asks $ cmdAddCbls . optsCmd . fst
    --
    ghcPkgs <- asks  $ map (uncurry GhcType) . cmdAddGhcPkgs . optsCmd . fst
    distroPkgs <- asks $ map (\ (n, v, x, r) -> DistroType n v x r) . cmdAddDistroPkgs . optsCmd . fst
    genFilePkgs <- mapM (runCabalParseWithTempDir . fmap snd . Cbl.readFromFile . fst) filePkgs
    genIdxPkgs <- mapM ((runCabalParseWithTempDir . fmap snd . Cbl.readFromIdx) . (\ (a, b, _) -> (a, b))) idxPkgs
    genPkgs <- liftM (map RepoType) $ exitOnAnyLefts (genFilePkgs ++ genIdxPkgs)
    --
    let pkgs = ghcPkgs ++ distroPkgs ++ genPkgs
        pkgNames = map getName pkgs
        tmpDb = foldl delPkg db pkgNames
        oldFlags = map (maybe ([], []) (pkgName &&& pkgFlags) . lookupPkg db . getName) pkgs
        fileFlags = map (\ (pkg, (_, fa)) -> (pkgNameStr pkg, fa))
            (zip (rights genFilePkgs) filePkgs)
        idxFlags = map (\ (a, _, b) -> (a, b)) idxPkgs
        flags = fileFlags `combineFlags` idxFlags `combineFlags` oldFlags
    case addPkgs ghcVersion tmpDb flags pkgs of
        Left (unsatisfiables, breaksOthers) -> liftIO (mapM_ printUnSat unsatisfiables >> mapM_ printBrksOth breaksOthers)
        Right newDb -> liftIO $ unless dr $ saveDb newDb dbFn

runCabalParseWithTempDir :: Cbl.CabalParse a -> Command (Either String a)
runCabalParseWithTempDir f = do
    aD <- asks $ appDir . fst
    pD <- asks $ patchDir . optsCmd . fst
    cfg <- asks snd
    liftIO $ withTemporaryDirectory "/tmp/cblrepo." $ \ destDir -> do
        let cpe = Cbl.CabalParseEnv aD pD destDir (getIndexFileName cfg)
        Cbl.runCabalParse cpe f

getName (GhcType n _) = n
getName (DistroType n _ _ _) = n
getName (RepoType gpd) = pkgNameStr $ packageDescription gpd

-- {{{1 addPkgs
addPkgs :: Version -> CblDB -> [(String, FlagAssignment)] -> [PkgType] -> Either ([(String, [P.Dependency])], [((String, Version), [(String, Maybe P.Dependency)])]) CblDB
addPkgs ghcVer db flags pkgs = let
        (succs, fails) = partition (canBeAdded ghcVer db flags) pkgs
        newDb = foldl addPkg2 db (map (pkgTypeToCblPkg ghcVer db flags) succs)
        unsatisfieds = mapMaybe (finalizeToUnsatisfiableDeps ghcVer db flags) fails
        breaksOthers = mapMaybe (findBreaking db) fails
    in case (succs, fails) of
        (_, []) -> Right newDb
        ([], _) -> Left (unsatisfieds, breaksOthers)
        (_, _) -> addPkgs ghcVer newDb flags fails

canBeAdded :: Version -> CblDB -> [(String, FlagAssignment)] -> PkgType -> Bool
canBeAdded _ db _ (GhcType n v) = null $ checkDependants db n v
canBeAdded _ db _ (DistroType n v _ _) = null $ checkDependants db n v
canBeAdded ghcVer db flags pkg@(RepoType gpd) =  finable && depsOK
    where
        fa = fromMaybe [] $ lookup (getName pkg) flags
        finable = either (const False) (const True) (finalizePkg ghcVer db fa gpd)
        n = pkgNameStr (packageDescription gpd)
        v = P.pkgVersion $ package $ packageDescription gpd
        depsOK = null $ checkDependants db n v

pkgTypeToCblPkg _ _ _ (GhcType n v) = createGhcPkg n v
pkgTypeToCblPkg _ _ _ (DistroType n v x r) = createDistroPkg n v x r
pkgTypeToCblPkg ghcVer db flags pkg@(RepoType gpd) =
    let fa = fromMaybe [] $ lookup (getName pkg) flags
    in fromJust $ case finalizePkg ghcVer db fa gpd of
       Right (pd, fa) -> Just $ createCblPkg pd fa
       Left _ -> Nothing

finalizeToUnsatisfiableDeps ghcVer db flags pkg@(RepoType gpd) =
    let fa = fromMaybe [] $ lookup (getName pkg) flags
    in case finalizePkg ghcVer db fa gpd of
       Left ds -> Just (pkgNameStr (packageDescription gpd), ds)
       _ -> Nothing

finalizeToUnsatisfiableDeps _ _ _ _ = Nothing

findBreaking db (GhcType n v) = let
        d = checkDependants db n v
    in if null d
        then Nothing
        else Just ((n, v), d)
findBreaking db (DistroType n v _ _) = let
        d = checkDependants db n v
    in if null d
        then Nothing
        else Just ((n, v), d)
findBreaking db (RepoType gpd) = let
        n = pkgNameStr (packageDescription gpd)
        v = P.pkgVersion $ package $ packageDescription gpd
        d = checkDependants db n v
    in if null d
        then Nothing
        else Just ((n, v), d)

combineFlags a b = zip keys $ mapMaybe (uncurry mappend . (\ k -> (lookup k a, lookup k b))) keys
    where
        keys = nub $ map fst (a ++ b)