module Distribution.Types.DependencyMap (
    DependencyMap,
    toDepMap,
    fromDepMap,
    constrainBy,
) where

import Distribution.Compat.Prelude
import Prelude ()

import Distribution.Types.Dependency
import Distribution.Types.LibraryName
import Distribution.Types.PackageName
import Distribution.Types.PackageVersionConstraint
import Distribution.Version

import qualified Data.Map.Lazy as Map

-- | A map of dependencies.  Newtyped since the default monoid instance is not
--   appropriate.  The monoid instance uses 'intersectVersionRanges'.
newtype DependencyMap = DependencyMap { DependencyMap
-> Map PackageName (VersionRange, NonEmptySet LibraryName)
unDependencyMap :: Map PackageName (VersionRange, NonEmptySet LibraryName) }
  deriving (Int -> DependencyMap -> ShowS
[DependencyMap] -> ShowS
DependencyMap -> String
(Int -> DependencyMap -> ShowS)
-> (DependencyMap -> String)
-> ([DependencyMap] -> ShowS)
-> Show DependencyMap
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DependencyMap] -> ShowS
$cshowList :: [DependencyMap] -> ShowS
show :: DependencyMap -> String
$cshow :: DependencyMap -> String
showsPrec :: Int -> DependencyMap -> ShowS
$cshowsPrec :: Int -> DependencyMap -> ShowS
Show, ReadPrec [DependencyMap]
ReadPrec DependencyMap
Int -> ReadS DependencyMap
ReadS [DependencyMap]
(Int -> ReadS DependencyMap)
-> ReadS [DependencyMap]
-> ReadPrec DependencyMap
-> ReadPrec [DependencyMap]
-> Read DependencyMap
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [DependencyMap]
$creadListPrec :: ReadPrec [DependencyMap]
readPrec :: ReadPrec DependencyMap
$creadPrec :: ReadPrec DependencyMap
readList :: ReadS [DependencyMap]
$creadList :: ReadS [DependencyMap]
readsPrec :: Int -> ReadS DependencyMap
$creadsPrec :: Int -> ReadS DependencyMap
Read)

instance Monoid DependencyMap where
    mempty :: DependencyMap
mempty = Map PackageName (VersionRange, NonEmptySet LibraryName)
-> DependencyMap
DependencyMap Map PackageName (VersionRange, NonEmptySet LibraryName)
forall k a. Map k a
Map.empty
    mappend :: DependencyMap -> DependencyMap -> DependencyMap
mappend = DependencyMap -> DependencyMap -> DependencyMap
forall a. Semigroup a => a -> a -> a
(<>)

instance Semigroup DependencyMap where
    (DependencyMap Map PackageName (VersionRange, NonEmptySet LibraryName)
a) <> :: DependencyMap -> DependencyMap -> DependencyMap
<> (DependencyMap Map PackageName (VersionRange, NonEmptySet LibraryName)
b) =
        Map PackageName (VersionRange, NonEmptySet LibraryName)
-> DependencyMap
DependencyMap (((VersionRange, NonEmptySet LibraryName)
 -> (VersionRange, NonEmptySet LibraryName)
 -> (VersionRange, NonEmptySet LibraryName))
-> Map PackageName (VersionRange, NonEmptySet LibraryName)
-> Map PackageName (VersionRange, NonEmptySet LibraryName)
-> Map PackageName (VersionRange, NonEmptySet LibraryName)
forall k a. Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a
Map.unionWith (VersionRange, NonEmptySet LibraryName)
-> (VersionRange, NonEmptySet LibraryName)
-> (VersionRange, NonEmptySet LibraryName)
intersectVersionRangesAndJoinComponents Map PackageName (VersionRange, NonEmptySet LibraryName)
a Map PackageName (VersionRange, NonEmptySet LibraryName)
b)

intersectVersionRangesAndJoinComponents :: (VersionRange, NonEmptySet LibraryName)
                                        -> (VersionRange, NonEmptySet LibraryName)
                                        -> (VersionRange, NonEmptySet LibraryName)
intersectVersionRangesAndJoinComponents :: (VersionRange, NonEmptySet LibraryName)
-> (VersionRange, NonEmptySet LibraryName)
-> (VersionRange, NonEmptySet LibraryName)
intersectVersionRangesAndJoinComponents (VersionRange
va, NonEmptySet LibraryName
ca) (VersionRange
vb, NonEmptySet LibraryName
cb) =
  (VersionRange -> VersionRange -> VersionRange
intersectVersionRanges VersionRange
va VersionRange
vb, NonEmptySet LibraryName
ca NonEmptySet LibraryName
-> NonEmptySet LibraryName -> NonEmptySet LibraryName
forall a. Semigroup a => a -> a -> a
<> NonEmptySet LibraryName
cb)

toDepMap :: [Dependency] -> DependencyMap
toDepMap :: [Dependency] -> DependencyMap
toDepMap [Dependency]
ds =
  Map PackageName (VersionRange, NonEmptySet LibraryName)
-> DependencyMap
DependencyMap (Map PackageName (VersionRange, NonEmptySet LibraryName)
 -> DependencyMap)
-> Map PackageName (VersionRange, NonEmptySet LibraryName)
-> DependencyMap
forall a b. (a -> b) -> a -> b
$ ((VersionRange, NonEmptySet LibraryName)
 -> (VersionRange, NonEmptySet LibraryName)
 -> (VersionRange, NonEmptySet LibraryName))
-> [(PackageName, (VersionRange, NonEmptySet LibraryName))]
-> Map PackageName (VersionRange, NonEmptySet LibraryName)
forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith (VersionRange, NonEmptySet LibraryName)
-> (VersionRange, NonEmptySet LibraryName)
-> (VersionRange, NonEmptySet LibraryName)
intersectVersionRangesAndJoinComponents [ (PackageName
p,(VersionRange
vr,NonEmptySet LibraryName
cs)) | Dependency PackageName
p VersionRange
vr NonEmptySet LibraryName
cs <- [Dependency]
ds ]

fromDepMap :: DependencyMap -> [Dependency]
fromDepMap :: DependencyMap -> [Dependency]
fromDepMap DependencyMap
m = [ PackageName
-> VersionRange -> NonEmptySet LibraryName -> Dependency
Dependency PackageName
p VersionRange
vr NonEmptySet LibraryName
cs | (PackageName
p,(VersionRange
vr,NonEmptySet LibraryName
cs)) <- Map PackageName (VersionRange, NonEmptySet LibraryName)
-> [(PackageName, (VersionRange, NonEmptySet LibraryName))]
forall k a. Map k a -> [(k, a)]
Map.toList (DependencyMap
-> Map PackageName (VersionRange, NonEmptySet LibraryName)
unDependencyMap DependencyMap
m) ]

-- Apply extra constraints to a dependency map.
-- Combines dependencies where the result will only contain keys from the left
-- (first) map.  If a key also exists in the right map, both constraints will
-- be intersected.
constrainBy
    :: DependencyMap
    -> [PackageVersionConstraint]
    -> DependencyMap
constrainBy :: DependencyMap -> [PackageVersionConstraint] -> DependencyMap
constrainBy = (DependencyMap -> PackageVersionConstraint -> DependencyMap)
-> DependencyMap -> [PackageVersionConstraint] -> DependencyMap
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' DependencyMap -> PackageVersionConstraint -> DependencyMap
tightenConstraint where
    tightenConstraint :: DependencyMap -> PackageVersionConstraint -> DependencyMap
tightenConstraint (DependencyMap Map PackageName (VersionRange, NonEmptySet LibraryName)
l) (PackageVersionConstraint PackageName
pn VersionRange
vr) = Map PackageName (VersionRange, NonEmptySet LibraryName)
-> DependencyMap
DependencyMap (Map PackageName (VersionRange, NonEmptySet LibraryName)
 -> DependencyMap)
-> Map PackageName (VersionRange, NonEmptySet LibraryName)
-> DependencyMap
forall a b. (a -> b) -> a -> b
$
        case PackageName
-> Map PackageName (VersionRange, NonEmptySet LibraryName)
-> Maybe (VersionRange, NonEmptySet LibraryName)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup PackageName
pn Map PackageName (VersionRange, NonEmptySet LibraryName)
l of
            Maybe (VersionRange, NonEmptySet LibraryName)
Nothing        -> Map PackageName (VersionRange, NonEmptySet LibraryName)
l
            Just (VersionRange
vr', NonEmptySet LibraryName
cs) -> PackageName
-> (VersionRange, NonEmptySet LibraryName)
-> Map PackageName (VersionRange, NonEmptySet LibraryName)
-> Map PackageName (VersionRange, NonEmptySet LibraryName)
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert PackageName
pn (VersionRange -> VersionRange -> VersionRange
intersectVersionRanges VersionRange
vr' VersionRange
vr, NonEmptySet LibraryName
cs) Map PackageName (VersionRange, NonEmptySet LibraryName)
l