{- - Copyright 2011 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 PkgDB where import Util.Misc import Control.Exception as CE import Data.List import Data.Maybe import Data.Maybe import Distribution.PackageDescription import Distribution.Text import Distribution.Version import System.IO.Error import Text.JSON import qualified Distribution.Package as P import qualified Distribution.Version as V type CblPkg = (String, (V.Version, [P.Dependency], Int)) type CblDB = [CblPkg] pkgName :: CblPkg -> String pkgName (n, _) = n pkgVersion :: CblPkg -> V.Version pkgVersion (_, (v, _, _)) = v pkgDeps :: CblPkg -> [P.Dependency] pkgDeps (_, (_, ds, _)) = ds pkgRelease :: CblPkg -> Int pkgRelease (_, (_, _, i)) = i createCblPkg :: PackageDescription -> CblPkg createCblPkg pd = (name, (version, deps, 1)) where name = (\ (P.PackageName n) -> n) (P.pkgName $ package pd) version = P.pkgVersion $ package pd deps = buildDepends pd getDependencyOn :: String -> CblPkg -> Maybe P.Dependency getDependencyOn n p = find (\ d -> depName d == n) (pkgDeps p) isBasePkg (_, (_, ds, _)) = null ds emptyPkgDB :: CblDB emptyPkgDB = [] addPkg :: CblDB -> String -> V.Version -> [P.Dependency] -> Int -> CblDB addPkg db n v ds r = nubBy cmp newdb where cmp (n1, _) (n2, _) = n1 == n2 newdb = (n, (v, ds, r)) : db addPkg2 db (n, (v, ds, r)) = addPkg db n v ds r addBasePkg db n v = addPkg db n v [] 0 delPkg :: CblDB -> String -> CblDB delPkg db n = filter (\ p -> n /= pkgName p) db lookupPkg :: CblDB -> String -> Maybe CblPkg lookupPkg db n = maybe Nothing (\ s -> Just (n, s)) (lookup n db) lookupDependencies :: CblDB -> String -> Maybe [String] lookupDependencies db n = case lookupPkg db n of Nothing -> Nothing Just p -> let ds = pkgDeps p in Just $ map depName ds lookupDependants :: CblDB -> String -> [String] lookupDependants db n = map pkgName $ filter (\ p -> doesDependOn p n) db where doesDependOn p n = n `elem` (map depName $ pkgDeps p) checkDependants db n v = let d1 = catMaybes $ map (lookupPkg db) (lookupDependants db n) d2 = map (\ p -> (pkgName p, getDependencyOn n p)) d1 fails = filter (not . withinRange v . depVersionRange . fromJust . snd) d2 in fails transitiveDependants db pkgs = keepLast $ concat $ map transUsersOfOne pkgs where transUsersOfOne pkg = pkg : (keepLast $ concat $ map (transUsersOfOne) (lookupDependants db pkg)) keepLast = reverse . nub . reverse lookupRelease :: CblDB -> String -> Maybe Int lookupRelease db n = lookupPkg db n >>= return . pkgRelease bumpRelease db n = let bump (n', (v', d', r')) = (n', (v', d', r' + 1)) in maybe db (addPkg2 db . bump) (lookupPkg db n) readDb :: FilePath -> IO CblDB readDb fp = (flip CE.catch) (\ e -> if isDoesNotExistError e then return emptyPkgDB else throwIO e) $ do r <- readFile fp >>= return . sequence . map decode . lines case r of Ok a -> return a Error s -> fail s saveDb :: CblDB -> FilePath -> IO () saveDb db fp = writeFile fp s where s = unlines $ map (encode . showJSON) db instance JSON V.Version where showJSON v = makeObj [ ("Version", showJSON $ display v) ] readJSON (JSObject o) = let jAssoc = fromJSObject o resultToMaybe (Ok a) = Just a resultToMaybe _ = Nothing version = lookup "Version" jAssoc >>= resultToMaybe . readJSON >>= simpleParse in maybe (fail "Not a version object") return version readJSON _ = fail "Not a version object" instance JSON P.Dependency where showJSON d = makeObj [ ("Dependency", showJSON $ display d) ] readJSON (JSObject o) = let jAssoc = fromJSObject o resultToMaybe (Ok a) = Just a resultToMaybe _ = Nothing dependency = lookup "Dependency" jAssoc >>= resultToMaybe . readJSON >>= simpleParse in maybe (fail "Not a dependency object") return dependency readJSON _ = fail "Not a version object"