{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ViewPatterns #-}
module Distribution.Solver.Types.ProjectConfigPath
(
ProjectConfigPath(..)
, projectConfigPathRoot
, nullProjectConfigPath
, consProjectConfigPath
, unconsProjectConfigPath
, docProjectConfigPath
, docProjectConfigPaths
, cyclicalImportMsg
, docProjectConfigPathFailReason
, isCyclicConfigPath
, isTopLevelConfigPath
, canonicalizeConfigPath
) where
import Distribution.Solver.Compat.Prelude hiding (toList, (<>))
import qualified Distribution.Solver.Compat.Prelude as P ((<>))
import Prelude (sequence)
import Data.Coerce (coerce)
import Data.List.NonEmpty ((<|))
import Network.URI (parseURI, parseAbsoluteURI)
import System.Directory
import System.FilePath
import qualified Data.List.NonEmpty as NE
import Distribution.Solver.Modular.Version (VR)
import Distribution.Pretty (prettyShow)
import Text.PrettyPrint
import Distribution.Simple.Utils (ordNub)
newtype ProjectConfigPath = ProjectConfigPath (NonEmpty FilePath)
deriving (ProjectConfigPath -> ProjectConfigPath -> Bool
(ProjectConfigPath -> ProjectConfigPath -> Bool)
-> (ProjectConfigPath -> ProjectConfigPath -> Bool)
-> Eq ProjectConfigPath
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ProjectConfigPath -> ProjectConfigPath -> Bool
== :: ProjectConfigPath -> ProjectConfigPath -> Bool
$c/= :: ProjectConfigPath -> ProjectConfigPath -> Bool
/= :: ProjectConfigPath -> ProjectConfigPath -> Bool
Eq, Int -> ProjectConfigPath -> ShowS
[ProjectConfigPath] -> ShowS
ProjectConfigPath -> FilePath
(Int -> ProjectConfigPath -> ShowS)
-> (ProjectConfigPath -> FilePath)
-> ([ProjectConfigPath] -> ShowS)
-> Show ProjectConfigPath
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ProjectConfigPath -> ShowS
showsPrec :: Int -> ProjectConfigPath -> ShowS
$cshow :: ProjectConfigPath -> FilePath
show :: ProjectConfigPath -> FilePath
$cshowList :: [ProjectConfigPath] -> ShowS
showList :: [ProjectConfigPath] -> ShowS
Show, (forall x. ProjectConfigPath -> Rep ProjectConfigPath x)
-> (forall x. Rep ProjectConfigPath x -> ProjectConfigPath)
-> Generic ProjectConfigPath
forall x. Rep ProjectConfigPath x -> ProjectConfigPath
forall x. ProjectConfigPath -> Rep ProjectConfigPath x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. ProjectConfigPath -> Rep ProjectConfigPath x
from :: forall x. ProjectConfigPath -> Rep ProjectConfigPath x
$cto :: forall x. Rep ProjectConfigPath x -> ProjectConfigPath
to :: forall x. Rep ProjectConfigPath x -> ProjectConfigPath
Generic)
instance Ord ProjectConfigPath where
compare :: ProjectConfigPath -> ProjectConfigPath -> Ordering
compare pa :: ProjectConfigPath
pa@(ProjectConfigPath (NonEmpty FilePath -> [FilePath]
forall a. NonEmpty a -> [a]
NE.toList -> [FilePath]
as)) pb :: ProjectConfigPath
pb@(ProjectConfigPath (NonEmpty FilePath -> [FilePath]
forall a. NonEmpty a -> [a]
NE.toList -> [FilePath]
bs)) =
case ([FilePath]
as, [FilePath]
bs) of
([FilePath
a], [FilePath
b]) -> FilePath -> FilePath -> Ordering
forall a. Ord a => a -> a -> Ordering
compare FilePath
a FilePath
b
([FilePath
_], [FilePath]
_) -> Ordering
LT
([FilePath]
_, [FilePath
_]) -> Ordering
GT
(FilePath
a:[FilePath]
_, FilePath
b:[FilePath]
_) -> case (FilePath -> Maybe URI
parseAbsoluteURI FilePath
a, FilePath -> Maybe URI
parseAbsoluteURI FilePath
b) of
(Just URI
ua, Just URI
ub) -> URI -> URI -> Ordering
forall a. Ord a => a -> a -> Ordering
compare URI
ua URI
ub Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
P.<> Maybe ProjectConfigPath -> Maybe ProjectConfigPath -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Maybe ProjectConfigPath
aImporters Maybe ProjectConfigPath
bImporters
(Just URI
_, Maybe URI
Nothing) -> Ordering
GT
(Maybe URI
Nothing, Just URI
_) -> Ordering
LT
(Maybe URI
Nothing, Maybe URI
Nothing) -> [FilePath] -> [FilePath] -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (FilePath -> [FilePath]
splitPath FilePath
a) (FilePath -> [FilePath]
splitPath FilePath
b) Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
P.<> Maybe ProjectConfigPath -> Maybe ProjectConfigPath -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Maybe ProjectConfigPath
aImporters Maybe ProjectConfigPath
bImporters
([FilePath], [FilePath])
_ ->
Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare ([FilePath] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [FilePath]
as) ([FilePath] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [FilePath]
bs)
Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
P.<> Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare ([[FilePath]] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [[FilePath]]
aPaths) ([[FilePath]] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [[FilePath]]
bPaths)
Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
P.<> [[FilePath]] -> [[FilePath]] -> Ordering
forall a. Ord a => a -> a -> Ordering
compare [[FilePath]]
aPaths [[FilePath]]
bPaths
where
aPaths :: [[FilePath]]
aPaths = FilePath -> [FilePath]
splitPath (FilePath -> [FilePath]) -> [FilePath] -> [[FilePath]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [FilePath]
as
bPaths :: [[FilePath]]
bPaths = FilePath -> [FilePath]
splitPath (FilePath -> [FilePath]) -> [FilePath] -> [[FilePath]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [FilePath]
bs
aImporters :: Maybe ProjectConfigPath
aImporters = (FilePath, Maybe ProjectConfigPath) -> Maybe ProjectConfigPath
forall a b. (a, b) -> b
snd ((FilePath, Maybe ProjectConfigPath) -> Maybe ProjectConfigPath)
-> (FilePath, Maybe ProjectConfigPath) -> Maybe ProjectConfigPath
forall a b. (a -> b) -> a -> b
$ ProjectConfigPath -> (FilePath, Maybe ProjectConfigPath)
unconsProjectConfigPath ProjectConfigPath
pa
bImporters :: Maybe ProjectConfigPath
bImporters = (FilePath, Maybe ProjectConfigPath) -> Maybe ProjectConfigPath
forall a b. (a, b) -> b
snd ((FilePath, Maybe ProjectConfigPath) -> Maybe ProjectConfigPath)
-> (FilePath, Maybe ProjectConfigPath) -> Maybe ProjectConfigPath
forall a b. (a -> b) -> a -> b
$ ProjectConfigPath -> (FilePath, Maybe ProjectConfigPath)
unconsProjectConfigPath ProjectConfigPath
pb
instance Binary ProjectConfigPath
instance Structured ProjectConfigPath
docProjectConfigPath :: ProjectConfigPath -> Doc
docProjectConfigPath :: ProjectConfigPath -> Doc
docProjectConfigPath (ProjectConfigPath (FilePath
p :| [])) = FilePath -> Doc
text FilePath
p
docProjectConfigPath (ProjectConfigPath (FilePath
p :| [FilePath]
ps)) = [Doc] -> Doc
vcat ([Doc] -> Doc) -> [Doc] -> Doc
forall a b. (a -> b) -> a -> b
$
FilePath -> Doc
text FilePath
p Doc -> [Doc] -> [Doc]
forall a. a -> [a] -> [a]
: [ FilePath -> Doc
text FilePath
" " Doc -> Doc -> Doc
<+> FilePath -> Doc
text FilePath
"imported by:" Doc -> Doc -> Doc
<+> FilePath -> Doc
text FilePath
l | FilePath
l <- [FilePath]
ps ]
docProjectConfigPaths :: [ProjectConfigPath] -> Doc
docProjectConfigPaths :: [ProjectConfigPath] -> Doc
docProjectConfigPaths [ProjectConfigPath]
ps = [Doc] -> Doc
vcat
[ FilePath -> Doc
text FilePath
"-" Doc -> Doc -> Doc
<+> FilePath -> Doc
text FilePath
p
| FilePath
p <- [FilePath] -> [FilePath]
forall a. Ord a => [a] -> [a]
ordNub [ FilePath
p | ProjectConfigPath (FilePath
p :| [FilePath]
_) <- [ProjectConfigPath]
ps ]
]
cyclicalImportMsg :: ProjectConfigPath -> Doc
cyclicalImportMsg :: ProjectConfigPath -> Doc
cyclicalImportMsg path :: ProjectConfigPath
path@(ProjectConfigPath (FilePath
duplicate :| [FilePath]
_)) =
[Doc] -> Doc
vcat
[ FilePath -> Doc
text FilePath
"cyclical import of" Doc -> Doc -> Doc
<+> FilePath -> Doc
text FilePath
duplicate Doc -> Doc -> Doc
<> Doc
semi
, Int -> Doc -> Doc
nest Int
2 (ProjectConfigPath -> Doc
docProjectConfigPath ProjectConfigPath
path)
]
docProjectConfigPathFailReason :: VR -> ProjectConfigPath -> Doc
docProjectConfigPathFailReason :: VR -> ProjectConfigPath -> Doc
docProjectConfigPathFailReason VR
vr ProjectConfigPath
pcp
| ProjectConfigPath (FilePath
p :| []) <- ProjectConfigPath
pcp =
FilePath -> Doc
constraint FilePath
p
| ProjectConfigPath (FilePath
p :| [FilePath]
ps) <- ProjectConfigPath
pcp = [Doc] -> Doc
vcat
[ FilePath -> Doc
constraint FilePath
p
, [Doc] -> Doc
cat [Int -> Doc -> Doc
nest Int
2 (Doc -> Doc) -> Doc -> Doc
forall a b. (a -> b) -> a -> b
$ FilePath -> Doc
text FilePath
"imported by:" Doc -> Doc -> Doc
<+> FilePath -> Doc
text FilePath
l | FilePath
l <- [FilePath]
ps ]
]
where
pathRequiresVersion :: FilePath -> Doc
pathRequiresVersion FilePath
p = FilePath -> Doc
text FilePath
p Doc -> Doc -> Doc
<+> FilePath -> Doc
text FilePath
"requires" Doc -> Doc -> Doc
<+> FilePath -> Doc
text (VR -> FilePath
forall a. Pretty a => a -> FilePath
prettyShow VR
vr)
constraint :: FilePath -> Doc
constraint FilePath
p = Doc -> Doc
parens (Doc -> Doc) -> Doc -> Doc
forall a b. (a -> b) -> a -> b
$ FilePath -> Doc
text FilePath
"constraint from" Doc -> Doc -> Doc
<+> FilePath -> Doc
pathRequiresVersion FilePath
p
projectConfigPathRoot :: ProjectConfigPath -> FilePath
projectConfigPathRoot :: ProjectConfigPath -> FilePath
projectConfigPathRoot (ProjectConfigPath NonEmpty FilePath
xs) = NonEmpty FilePath -> FilePath
forall a. NonEmpty a -> a
last NonEmpty FilePath
xs
nullProjectConfigPath :: ProjectConfigPath
nullProjectConfigPath :: ProjectConfigPath
nullProjectConfigPath = NonEmpty FilePath -> ProjectConfigPath
ProjectConfigPath (NonEmpty FilePath -> ProjectConfigPath)
-> NonEmpty FilePath -> ProjectConfigPath
forall a b. (a -> b) -> a -> b
$ FilePath
"unused" FilePath -> [FilePath] -> NonEmpty FilePath
forall a. a -> [a] -> NonEmpty a
:| []
isCyclicConfigPath :: ProjectConfigPath -> Bool
isCyclicConfigPath :: ProjectConfigPath -> Bool
isCyclicConfigPath (ProjectConfigPath NonEmpty FilePath
p) = NonEmpty FilePath -> Int
forall a. NonEmpty a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length NonEmpty FilePath
p Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= NonEmpty FilePath -> Int
forall a. NonEmpty a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (NonEmpty FilePath -> NonEmpty FilePath
forall a. Eq a => NonEmpty a -> NonEmpty a
NE.nub NonEmpty FilePath
p)
isTopLevelConfigPath :: ProjectConfigPath -> Bool
isTopLevelConfigPath :: ProjectConfigPath -> Bool
isTopLevelConfigPath (ProjectConfigPath NonEmpty FilePath
p) = NonEmpty FilePath -> Int
forall a. NonEmpty a -> Int
NE.length NonEmpty FilePath
p Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
1
consProjectConfigPath :: FilePath -> ProjectConfigPath -> ProjectConfigPath
consProjectConfigPath :: FilePath -> ProjectConfigPath -> ProjectConfigPath
consProjectConfigPath FilePath
p ProjectConfigPath
ps = NonEmpty FilePath -> ProjectConfigPath
ProjectConfigPath (FilePath
p FilePath -> NonEmpty FilePath -> NonEmpty FilePath
forall a. a -> NonEmpty a -> NonEmpty a
<| ProjectConfigPath -> NonEmpty FilePath
forall a b. Coercible a b => a -> b
coerce ProjectConfigPath
ps)
unconsProjectConfigPath :: ProjectConfigPath -> (FilePath, Maybe ProjectConfigPath)
unconsProjectConfigPath :: ProjectConfigPath -> (FilePath, Maybe ProjectConfigPath)
unconsProjectConfigPath ProjectConfigPath
ps = (NonEmpty FilePath -> ProjectConfigPath)
-> Maybe (NonEmpty FilePath) -> Maybe ProjectConfigPath
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap NonEmpty FilePath -> ProjectConfigPath
ProjectConfigPath (Maybe (NonEmpty FilePath) -> Maybe ProjectConfigPath)
-> (FilePath, Maybe (NonEmpty FilePath))
-> (FilePath, Maybe ProjectConfigPath)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty FilePath -> (FilePath, Maybe (NonEmpty FilePath))
forall a. NonEmpty a -> (a, Maybe (NonEmpty a))
NE.uncons (ProjectConfigPath -> NonEmpty FilePath
forall a b. Coercible a b => a -> b
coerce ProjectConfigPath
ps)
makeRelativeConfigPath :: FilePath -> ProjectConfigPath -> ProjectConfigPath
makeRelativeConfigPath :: FilePath -> ProjectConfigPath -> ProjectConfigPath
makeRelativeConfigPath FilePath
dir (ProjectConfigPath NonEmpty FilePath
p) =
NonEmpty FilePath -> ProjectConfigPath
ProjectConfigPath
(NonEmpty FilePath -> ProjectConfigPath)
-> NonEmpty FilePath -> ProjectConfigPath
forall a b. (a -> b) -> a -> b
$ (\FilePath
segment -> (if FilePath -> Bool
isURI FilePath
segment then FilePath
segment else FilePath -> ShowS
makeRelative FilePath
dir FilePath
segment))
ShowS -> NonEmpty FilePath -> NonEmpty FilePath
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty FilePath
p
canonicalizeConfigPath :: FilePath -> ProjectConfigPath -> IO ProjectConfigPath
canonicalizeConfigPath :: FilePath -> ProjectConfigPath -> IO ProjectConfigPath
canonicalizeConfigPath FilePath
d (ProjectConfigPath NonEmpty FilePath
p) = do
NonEmpty FilePath
xs <- NonEmpty (IO FilePath) -> IO (NonEmpty FilePath)
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
forall (m :: * -> *) a. Monad m => NonEmpty (m a) -> m (NonEmpty a)
sequence (NonEmpty (IO FilePath) -> IO (NonEmpty FilePath))
-> NonEmpty (IO FilePath) -> IO (NonEmpty FilePath)
forall a b. (a -> b) -> a -> b
$ (FilePath -> IO FilePath -> IO FilePath)
-> IO FilePath -> NonEmpty FilePath -> NonEmpty (IO FilePath)
forall (f :: * -> *) a b.
Foldable f =>
(a -> b -> b) -> b -> f a -> NonEmpty b
NE.scanr (\FilePath
importee -> (IO FilePath -> (FilePath -> IO FilePath) -> IO FilePath
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \FilePath
importer ->
if FilePath -> Bool
isURI FilePath
importee
then FilePath -> IO FilePath
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure FilePath
importee
else FilePath -> IO FilePath
canonicalizePath (FilePath -> IO FilePath) -> FilePath -> IO FilePath
forall a b. (a -> b) -> a -> b
$ FilePath
d FilePath -> ShowS
</> ShowS
takeDirectory FilePath
importer FilePath -> ShowS
</> FilePath
importee))
(FilePath -> IO FilePath
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure FilePath
".") NonEmpty FilePath
p
ProjectConfigPath -> IO ProjectConfigPath
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ProjectConfigPath -> IO ProjectConfigPath)
-> ([FilePath] -> ProjectConfigPath)
-> [FilePath]
-> IO ProjectConfigPath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> ProjectConfigPath -> ProjectConfigPath
makeRelativeConfigPath FilePath
d (ProjectConfigPath -> ProjectConfigPath)
-> ([FilePath] -> ProjectConfigPath)
-> [FilePath]
-> ProjectConfigPath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty FilePath -> ProjectConfigPath
ProjectConfigPath (NonEmpty FilePath -> ProjectConfigPath)
-> ([FilePath] -> NonEmpty FilePath)
-> [FilePath]
-> ProjectConfigPath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [FilePath] -> NonEmpty FilePath
forall a. HasCallStack => [a] -> NonEmpty a
NE.fromList ([FilePath] -> IO ProjectConfigPath)
-> [FilePath] -> IO ProjectConfigPath
forall a b. (a -> b) -> a -> b
$ NonEmpty FilePath -> [FilePath]
forall a. NonEmpty a -> [a]
NE.init NonEmpty FilePath
xs
isURI :: FilePath -> Bool
isURI :: FilePath -> Bool
isURI = Maybe URI -> Bool
forall a. Maybe a -> Bool
isJust (Maybe URI -> Bool) -> (FilePath -> Maybe URI) -> FilePath -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Maybe URI
parseURI