module CabalBounds.Update
( update
) where
import qualified Distribution.PackageDescription as C
import qualified Distribution.Package as C
import qualified Distribution.Version as C
import qualified Distribution.Simple.LocalBuildInfo as C
import qualified Distribution.Simple.PackageIndex as C
import qualified Distribution.InstalledPackageInfo as C
import Control.Lens
import CabalBounds.Bound (Bound(..))
import CabalBounds.Targets (Targets(..), dependenciesOf)
import CabalBounds.Dependencies (Dependencies, filterDependencies)
import CabalBounds.Lenses
import Data.List (sort, foldl', find)
type InstalledPackages = [(C.PackageName, C.Version)]
update :: Bound -> Targets -> Dependencies -> C.GenericPackageDescription -> C.LocalBuildInfo -> C.GenericPackageDescription
update bound AllTargets deps pkgDescrp buildInfo =
pkgDescrp & dependenciesOfLib . filterDeps %~ updateDep
& dependenciesOfAllExes . filterDeps %~ updateDep
& dependenciesOfAllTests . filterDeps %~ updateDep
& dependenciesOfAllBenchms . filterDeps %~ updateDep
where
filterDeps = filterDependencies deps
updateDep = updateDependency bound (installedPackages buildInfo)
update bound (Targets targets) deps pkgDescrp buildInfo =
foldl' updateTarget pkgDescrp targets
where
updateTarget pkgDescrp target =
pkgDescrp & (dependenciesOf target) . filterDeps %~ updateDep
filterDeps = filterDependencies deps
updateDep = updateDependency bound (installedPackages buildInfo)
updateDependency :: Bound -> InstalledPackages -> C.Dependency -> C.Dependency
updateDependency LowerBound instPkgs dep@(C.Dependency pkgName _)
| Just (_, version) <- find ((== pkgName) . fst) instPkgs
, Just intervals <- versionIntervals version Nothing
= C.Dependency pkgName (C.fromVersionIntervals intervals)
| otherwise
= dep
updateDependency UpperBound instPkgs dep@(C.Dependency pkgName versionRange)
| not . C.isAnyVersion $ versionRange
, Just (_, upperVersion) <- find ((== pkgName) . fst) instPkgs
, (C.LowerBound lowerVersion _, _):_ <- C.asVersionIntervals versionRange
, Just intervals <- versionIntervals lowerVersion (Just $ nextMajorVersion upperVersion)
= C.Dependency pkgName (C.fromVersionIntervals intervals)
| otherwise
= updateDependency BothBounds instPkgs dep
updateDependency BothBounds instPkgs dep@(C.Dependency pkgName _)
| Just (_, version) <- find ((== pkgName) . fst) instPkgs
, Just intervals <- versionIntervals version (Just $ nextMajorVersion version)
= C.Dependency pkgName (C.fromVersionIntervals intervals)
| otherwise
= dep
versionIntervals :: C.Version -> Maybe C.Version -> Maybe C.VersionIntervals
versionIntervals lowerVersion Nothing =
C.mkVersionIntervals [(C.LowerBound lowerVersion C.InclusiveBound, C.NoUpperBound)]
versionIntervals lowerVersion (Just upperVersion) =
C.mkVersionIntervals [(C.LowerBound lowerVersion C.InclusiveBound, C.UpperBound upperVersion C.ExclusiveBound)]
nextMajorVersion :: C.Version -> C.Version
nextMajorVersion version
| (v1:v2:_) <- C.versionBranch version
= C.Version {C.versionBranch = [v1, (v2 + 1)], C.versionTags = []}
| (v1:_) <- C.versionBranch version
= C.Version {C.versionBranch = [v1, 1], C.versionTags = []}
| otherwise
= version
installedPackages :: C.LocalBuildInfo -> InstalledPackages
installedPackages = map (& _2 %~ newestVersion)
. filter ((not . null) . snd)
. C.allPackagesByName . C.installedPkgs
where
newestVersion :: [C.InstalledPackageInfo] -> C.Version
newestVersion = last . sort . map (C.pkgVersion . C.sourcePackageId)