{-# LANGUAGE CPP #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
module Hpack.Syntax.Dependencies (
  Dependencies(..)
, DependencyInfo(..)
, parseDependency
) where

import           Imports

import qualified Control.Monad.Fail as Fail
import qualified Data.Text as T
import qualified Distribution.Package as D
import qualified Distribution.Types.LibraryName as D
import           Distribution.Pretty (prettyShow)
import           Data.Map.Lazy (Map)
import qualified Data.Map.Lazy as Map
import           GHC.Exts

#if MIN_VERSION_Cabal(3,4,0)
import qualified Distribution.Compat.NonEmptySet as DependencySet
#else
import qualified Data.Set as DependencySet
#endif

import           Data.Aeson.Config.FromValue
import           Data.Aeson.Config.Types

import           Hpack.Syntax.DependencyVersion
import           Hpack.Syntax.ParseDependencies

newtype Dependencies = Dependencies {
  Dependencies -> Map String DependencyInfo
unDependencies :: Map String DependencyInfo
} deriving (Dependencies -> Dependencies -> Bool
(Dependencies -> Dependencies -> Bool)
-> (Dependencies -> Dependencies -> Bool) -> Eq Dependencies
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Dependencies -> Dependencies -> Bool
$c/= :: Dependencies -> Dependencies -> Bool
== :: Dependencies -> Dependencies -> Bool
$c== :: Dependencies -> Dependencies -> Bool
Eq, Int -> Dependencies -> ShowS
[Dependencies] -> ShowS
Dependencies -> String
(Int -> Dependencies -> ShowS)
-> (Dependencies -> String)
-> ([Dependencies] -> ShowS)
-> Show Dependencies
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Dependencies] -> ShowS
$cshowList :: [Dependencies] -> ShowS
show :: Dependencies -> String
$cshow :: Dependencies -> String
showsPrec :: Int -> Dependencies -> ShowS
$cshowsPrec :: Int -> Dependencies -> ShowS
Show, b -> Dependencies -> Dependencies
NonEmpty Dependencies -> Dependencies
Dependencies -> Dependencies -> Dependencies
(Dependencies -> Dependencies -> Dependencies)
-> (NonEmpty Dependencies -> Dependencies)
-> (forall b. Integral b => b -> Dependencies -> Dependencies)
-> Semigroup Dependencies
forall b. Integral b => b -> Dependencies -> Dependencies
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
stimes :: b -> Dependencies -> Dependencies
$cstimes :: forall b. Integral b => b -> Dependencies -> Dependencies
sconcat :: NonEmpty Dependencies -> Dependencies
$csconcat :: NonEmpty Dependencies -> Dependencies
<> :: Dependencies -> Dependencies -> Dependencies
$c<> :: Dependencies -> Dependencies -> Dependencies
Semigroup, Semigroup Dependencies
Dependencies
Semigroup Dependencies
-> Dependencies
-> (Dependencies -> Dependencies -> Dependencies)
-> ([Dependencies] -> Dependencies)
-> Monoid Dependencies
[Dependencies] -> Dependencies
Dependencies -> Dependencies -> Dependencies
forall a.
Semigroup a -> a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
mconcat :: [Dependencies] -> Dependencies
$cmconcat :: [Dependencies] -> Dependencies
mappend :: Dependencies -> Dependencies -> Dependencies
$cmappend :: Dependencies -> Dependencies -> Dependencies
mempty :: Dependencies
$cmempty :: Dependencies
$cp1Monoid :: Semigroup Dependencies
Monoid)

instance IsList Dependencies where
  type Item Dependencies = (String, DependencyInfo)
  fromList :: [Item Dependencies] -> Dependencies
fromList = Map String DependencyInfo -> Dependencies
Dependencies (Map String DependencyInfo -> Dependencies)
-> ([(String, DependencyInfo)] -> Map String DependencyInfo)
-> [(String, DependencyInfo)]
-> Dependencies
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(String, DependencyInfo)] -> Map String DependencyInfo
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList
  toList :: Dependencies -> [Item Dependencies]
toList = Map String DependencyInfo -> [(String, DependencyInfo)]
forall k a. Map k a -> [(k, a)]
Map.toList (Map String DependencyInfo -> [(String, DependencyInfo)])
-> (Dependencies -> Map String DependencyInfo)
-> Dependencies
-> [(String, DependencyInfo)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Dependencies -> Map String DependencyInfo
unDependencies

instance FromValue Dependencies where
  fromValue :: Value -> Parser Dependencies
fromValue = ([(String, DependencyInfo)] -> Dependencies)
-> Parser [(String, DependencyInfo)] -> Parser Dependencies
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Map String DependencyInfo -> Dependencies
Dependencies (Map String DependencyInfo -> Dependencies)
-> ([(String, DependencyInfo)] -> Map String DependencyInfo)
-> [(String, DependencyInfo)]
-> Dependencies
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(String, DependencyInfo)] -> Map String DependencyInfo
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList) (Parser [(String, DependencyInfo)] -> Parser Dependencies)
-> (Value -> Parser [(String, DependencyInfo)])
-> Value
-> Parser Dependencies
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parse String DependencyInfo
-> Value -> Parser [(String, DependencyInfo)]
forall k v. Parse k v -> Value -> Parser [(k, v)]
parseDependencies Parse String DependencyInfo
parse
    where
      parse :: Parse String DependencyInfo
      parse :: Parse String DependencyInfo
parse = Parse :: forall k v.
(Text -> Parser (k, v))
-> (Object -> Parser v)
-> (Value -> Parser v)
-> (Text -> k)
-> Parse k v
Parse {
        parseString :: Text -> Parser (String, DependencyInfo)
parseString = \ Text
input -> do
          (String
name, DependencyVersion
version) <- String -> Text -> Parser (String, DependencyVersion)
forall (m :: * -> *).
MonadFail m =>
String -> Text -> m (String, DependencyVersion)
parseDependency String
"dependency" Text
input
          (String, DependencyInfo) -> Parser (String, DependencyInfo)
forall (m :: * -> *) a. Monad m => a -> m a
return (String
name, [String] -> DependencyVersion -> DependencyInfo
DependencyInfo [] DependencyVersion
version)
      , parseListItem :: Object -> Parser DependencyInfo
parseListItem = Object -> Parser DependencyInfo
objectDependencyInfo
      , parseDictItem :: Value -> Parser DependencyInfo
parseDictItem = Value -> Parser DependencyInfo
dependencyInfo
      , parseName :: Text -> String
parseName = Text -> String
T.unpack
      }

data DependencyInfo = DependencyInfo {
  DependencyInfo -> [String]
dependencyInfoMixins :: [String]
, DependencyInfo -> DependencyVersion
dependencyInfoVersion :: DependencyVersion
} deriving (DependencyInfo -> DependencyInfo -> Bool
(DependencyInfo -> DependencyInfo -> Bool)
-> (DependencyInfo -> DependencyInfo -> Bool) -> Eq DependencyInfo
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: DependencyInfo -> DependencyInfo -> Bool
$c/= :: DependencyInfo -> DependencyInfo -> Bool
== :: DependencyInfo -> DependencyInfo -> Bool
$c== :: DependencyInfo -> DependencyInfo -> Bool
Eq, Eq DependencyInfo
Eq DependencyInfo
-> (DependencyInfo -> DependencyInfo -> Ordering)
-> (DependencyInfo -> DependencyInfo -> Bool)
-> (DependencyInfo -> DependencyInfo -> Bool)
-> (DependencyInfo -> DependencyInfo -> Bool)
-> (DependencyInfo -> DependencyInfo -> Bool)
-> (DependencyInfo -> DependencyInfo -> DependencyInfo)
-> (DependencyInfo -> DependencyInfo -> DependencyInfo)
-> Ord DependencyInfo
DependencyInfo -> DependencyInfo -> Bool
DependencyInfo -> DependencyInfo -> Ordering
DependencyInfo -> DependencyInfo -> DependencyInfo
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: DependencyInfo -> DependencyInfo -> DependencyInfo
$cmin :: DependencyInfo -> DependencyInfo -> DependencyInfo
max :: DependencyInfo -> DependencyInfo -> DependencyInfo
$cmax :: DependencyInfo -> DependencyInfo -> DependencyInfo
>= :: DependencyInfo -> DependencyInfo -> Bool
$c>= :: DependencyInfo -> DependencyInfo -> Bool
> :: DependencyInfo -> DependencyInfo -> Bool
$c> :: DependencyInfo -> DependencyInfo -> Bool
<= :: DependencyInfo -> DependencyInfo -> Bool
$c<= :: DependencyInfo -> DependencyInfo -> Bool
< :: DependencyInfo -> DependencyInfo -> Bool
$c< :: DependencyInfo -> DependencyInfo -> Bool
compare :: DependencyInfo -> DependencyInfo -> Ordering
$ccompare :: DependencyInfo -> DependencyInfo -> Ordering
$cp1Ord :: Eq DependencyInfo
Ord, Int -> DependencyInfo -> ShowS
[DependencyInfo] -> ShowS
DependencyInfo -> String
(Int -> DependencyInfo -> ShowS)
-> (DependencyInfo -> String)
-> ([DependencyInfo] -> ShowS)
-> Show DependencyInfo
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DependencyInfo] -> ShowS
$cshowList :: [DependencyInfo] -> ShowS
show :: DependencyInfo -> String
$cshow :: DependencyInfo -> String
showsPrec :: Int -> DependencyInfo -> ShowS
$cshowsPrec :: Int -> DependencyInfo -> ShowS
Show)

addMixins :: Object -> DependencyVersion -> Parser DependencyInfo
addMixins :: Object -> DependencyVersion -> Parser DependencyInfo
addMixins Object
o DependencyVersion
version = do
  Maybe (List String)
mixinsMay <- Object
o Object -> Key -> Parser (Maybe (List String))
forall a. FromValue a => Object -> Key -> Parser (Maybe a)
.:? Key
"mixin"
  DependencyInfo -> Parser DependencyInfo
forall (m :: * -> *) a. Monad m => a -> m a
return (DependencyInfo -> Parser DependencyInfo)
-> DependencyInfo -> Parser DependencyInfo
forall a b. (a -> b) -> a -> b
$ [String] -> DependencyVersion -> DependencyInfo
DependencyInfo (Maybe (List String) -> [String]
forall a. Maybe (List a) -> [a]
fromMaybeList Maybe (List String)
mixinsMay) DependencyVersion
version

objectDependencyInfo :: Object -> Parser DependencyInfo
objectDependencyInfo :: Object -> Parser DependencyInfo
objectDependencyInfo Object
o = Object -> Parser DependencyVersion
objectDependency Object
o Parser DependencyVersion
-> (DependencyVersion -> Parser DependencyInfo)
-> Parser DependencyInfo
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Object -> DependencyVersion -> Parser DependencyInfo
addMixins Object
o

dependencyInfo :: Value -> Parser DependencyInfo
dependencyInfo :: Value -> Parser DependencyInfo
dependencyInfo = (DependencyVersion -> DependencyInfo)
-> (Object -> DependencyVersion -> Parser DependencyInfo)
-> Value
-> Parser DependencyInfo
forall a.
(DependencyVersion -> a)
-> (Object -> DependencyVersion -> Parser a) -> Value -> Parser a
withDependencyVersion ([String] -> DependencyVersion -> DependencyInfo
DependencyInfo []) Object -> DependencyVersion -> Parser DependencyInfo
addMixins

parseDependency :: Fail.MonadFail m => String -> Text -> m (String, DependencyVersion)
parseDependency :: String -> Text -> m (String, DependencyVersion)
parseDependency String
subject = (Dependency -> (String, DependencyVersion))
-> m Dependency -> m (String, DependencyVersion)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Dependency -> (String, DependencyVersion)
fromCabal (m Dependency -> m (String, DependencyVersion))
-> (Text -> m Dependency) -> Text -> m (String, DependencyVersion)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String -> m Dependency
forall (m :: * -> *) a.
(MonadFail m, Parsec a) =>
String -> String -> m a
cabalParse String
subject (String -> m Dependency)
-> (Text -> String) -> Text -> m Dependency
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack
  where
    fromCabal :: D.Dependency -> (String, DependencyVersion)
    fromCabal :: Dependency -> (String, DependencyVersion)
fromCabal Dependency
d = (PackageName -> [LibraryName] -> String
toName (Dependency -> PackageName
D.depPkgName Dependency
d) (Set LibraryName -> [LibraryName]
forall a. Set a -> [a]
DependencySet.toList (Set LibraryName -> [LibraryName])
-> Set LibraryName -> [LibraryName]
forall a b. (a -> b) -> a -> b
$ Dependency -> Set LibraryName
D.depLibraries Dependency
d), Maybe SourceDependency -> VersionConstraint -> DependencyVersion
DependencyVersion Maybe SourceDependency
forall a. Maybe a
Nothing (VersionConstraint -> DependencyVersion)
-> (VersionRange -> VersionConstraint)
-> VersionRange
-> DependencyVersion
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VersionRange -> VersionConstraint
versionConstraintFromCabal (VersionRange -> DependencyVersion)
-> VersionRange -> DependencyVersion
forall a b. (a -> b) -> a -> b
$ Dependency -> VersionRange
D.depVerRange Dependency
d)

    toName :: D.PackageName -> [D.LibraryName] -> String
    toName :: PackageName -> [LibraryName] -> String
toName PackageName
package [LibraryName]
components = PackageName -> String
forall a. Pretty a => a -> String
prettyShow PackageName
package String -> ShowS
forall a. Semigroup a => a -> a -> a
<> case [LibraryName]
components of
      [LibraryName
D.LMainLibName] -> String
""
      [D.LSubLibName UnqualComponentName
lib] -> String
":" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> UnqualComponentName -> String
forall a. Pretty a => a -> String
prettyShow UnqualComponentName
lib
      [LibraryName]
xs -> String
":{" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> (String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"," ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ (UnqualComponentName -> String)
-> [UnqualComponentName] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map UnqualComponentName -> String
forall a. Pretty a => a -> String
prettyShow [UnqualComponentName
name | D.LSubLibName UnqualComponentName
name <- [LibraryName]
xs]) String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
"}"