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

import Prelude ()
import Distribution.Compat.Prelude

import Distribution.Types.Dependency
import Distribution.Types.PackageName
import Distribution.Types.LibraryName
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, Set LibraryName)
unDependencyMap :: Map PackageName (VersionRange, Set 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, Set LibraryName) -> DependencyMap
DependencyMap Map PackageName (VersionRange, Set 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, Set LibraryName)
a) <> :: DependencyMap -> DependencyMap -> DependencyMap
<> (DependencyMap Map PackageName (VersionRange, Set LibraryName)
b) =
        Map PackageName (VersionRange, Set LibraryName) -> DependencyMap
DependencyMap (((VersionRange, Set LibraryName)
 -> (VersionRange, Set LibraryName)
 -> (VersionRange, Set LibraryName))
-> Map PackageName (VersionRange, Set LibraryName)
-> Map PackageName (VersionRange, Set LibraryName)
-> Map PackageName (VersionRange, Set LibraryName)
forall k a. Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a
Map.unionWith (VersionRange, Set LibraryName)
-> (VersionRange, Set LibraryName)
-> (VersionRange, Set LibraryName)
intersectVersionRangesAndJoinComponents Map PackageName (VersionRange, Set LibraryName)
a Map PackageName (VersionRange, Set LibraryName)
b)

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

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

fromDepMap :: DependencyMap -> [Dependency]
fromDepMap :: DependencyMap -> [Dependency]
fromDepMap DependencyMap
m = [ PackageName -> VersionRange -> Set LibraryName -> Dependency
Dependency PackageName
p VersionRange
vr Set LibraryName
cs | (PackageName
p,(VersionRange
vr,Set LibraryName
cs)) <- Map PackageName (VersionRange, Set LibraryName)
-> [(PackageName, (VersionRange, Set LibraryName))]
forall k a. Map k a -> [(k, a)]
Map.toList (DependencyMap -> Map PackageName (VersionRange, Set 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  -- ^ Input map
            -> DependencyMap  -- ^ Extra constraints
            -> DependencyMap
constrainBy :: DependencyMap -> DependencyMap -> DependencyMap
constrainBy DependencyMap
left DependencyMap
extra =
    Map PackageName (VersionRange, Set LibraryName) -> DependencyMap
DependencyMap (Map PackageName (VersionRange, Set LibraryName) -> DependencyMap)
-> Map PackageName (VersionRange, Set LibraryName) -> DependencyMap
forall a b. (a -> b) -> a -> b
$
      (PackageName
 -> (VersionRange, Set LibraryName)
 -> Map PackageName (VersionRange, Set LibraryName)
 -> Map PackageName (VersionRange, Set LibraryName))
-> Map PackageName (VersionRange, Set LibraryName)
-> Map PackageName (VersionRange, Set LibraryName)
-> Map PackageName (VersionRange, Set LibraryName)
forall k a b. (k -> a -> b -> b) -> b -> Map k a -> b
Map.foldrWithKey PackageName
-> (VersionRange, Set LibraryName)
-> Map PackageName (VersionRange, Set LibraryName)
-> Map PackageName (VersionRange, Set LibraryName)
forall k.
Ord k =>
k
-> (VersionRange, Set LibraryName)
-> Map k (VersionRange, Set LibraryName)
-> Map k (VersionRange, Set LibraryName)
tightenConstraint (DependencyMap -> Map PackageName (VersionRange, Set LibraryName)
unDependencyMap DependencyMap
left)
                                         (DependencyMap -> Map PackageName (VersionRange, Set LibraryName)
unDependencyMap DependencyMap
extra)
  where tightenConstraint :: k
-> (VersionRange, Set LibraryName)
-> Map k (VersionRange, Set LibraryName)
-> Map k (VersionRange, Set LibraryName)
tightenConstraint k
n (VersionRange, Set LibraryName)
c Map k (VersionRange, Set LibraryName)
l =
            case k
-> Map k (VersionRange, Set LibraryName)
-> Maybe (VersionRange, Set LibraryName)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup k
n Map k (VersionRange, Set LibraryName)
l of
              Maybe (VersionRange, Set LibraryName)
Nothing -> Map k (VersionRange, Set LibraryName)
l
              Just (VersionRange, Set LibraryName)
vrcs -> k
-> (VersionRange, Set LibraryName)
-> Map k (VersionRange, Set LibraryName)
-> Map k (VersionRange, Set LibraryName)
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert k
n ((VersionRange, Set LibraryName)
-> (VersionRange, Set LibraryName)
-> (VersionRange, Set LibraryName)
intersectVersionRangesAndJoinComponents (VersionRange, Set LibraryName)
vrcs (VersionRange, Set LibraryName)
c) Map k (VersionRange, Set LibraryName)
l