Skip to content

Commit

Permalink
Add ActiveRepos data type
Browse files Browse the repository at this point in the history
  • Loading branch information
phadej committed Apr 22, 2020
1 parent b89a1c6 commit e49387a
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 4 deletions.
14 changes: 14 additions & 0 deletions Cabal/Distribution/Parsec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ module Distribution.Parsec (
parsecMaybeQuoted,
parsecCommaList,
parsecLeadingCommaList,
parsecLeadingCommaNonEmpty,
parsecOptCommaList,
parsecLeadingOptCommaList,
parsecStandard,
Expand Down Expand Up @@ -309,6 +310,19 @@ parsecLeadingCommaList p = do
lp = p <* P.spaces
comma = P.char ',' *> P.spaces P.<?> "comma"

-- |
--
-- @since 3.4.0.0
parsecLeadingCommaNonEmpty :: CabalParsing m => m a -> m (NonEmpty a)
parsecLeadingCommaNonEmpty p = do
c <- P.optional comma
case c of
Nothing -> P.sepEndByNonEmpty lp comma
Just _ -> P.sepByNonEmpty lp comma
where
lp = p <* P.spaces
comma = P.char ',' *> P.spaces P.<?> "comma"

parsecOptCommaList :: CabalParsing m => m a -> m [a]
parsecOptCommaList p = P.sepBy (p <* P.spaces) (P.optional comma)
where
Expand Down
3 changes: 3 additions & 0 deletions Cabal/Distribution/Utils/GrammarRegex.hs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ data GrammarRegex a
| RESpaces -- ^ zero-or-more spaces
| RESpaces1 -- ^ one-or-more spaces
| RECommaList (GrammarRegex a) -- ^ comma list (note, leading or trailing commas)
| RECommaNonEmpty (GrammarRegex a) -- ^ comma non-empty list (note, leading or trailing commas)
| REOptCommaList (GrammarRegex a) -- ^ opt comma list

| RETodo -- ^ unspecified
Expand Down Expand Up @@ -146,6 +147,8 @@ regexDoc = go 0 . vacuous where

go _ (RECommaList r) =
"\\mathrm{commalist}" <<>> go 4 r
go _ (RECommaNonEmpty r) =
"\\mathrm{commanonempty}" <<>> go 4 r
go _ (REOptCommaList r) =
"\\mathrm{optcommalist}" <<>> go 4 r

Expand Down
8 changes: 8 additions & 0 deletions Cabal/tests/UnitTests/Distribution/Described.hs
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,21 @@ convert = go id . vacuous where
go _ RESpaces1 = RE.ch_ ' ' RE.\/ " " RE.\/ "\n"

go f (RECommaList r) = go f (expandedCommaList r)
go f (RECommaNonEmpty r)= go f (expandedCommaNonEmpty r)
go f (REOptCommaList r) = go f (expandedOptCommaList r)

go _ RETodo = RE.Null

expandedCommaList :: GrammarRegex a -> GrammarRegex a
expandedCommaList = REUnion . expandedCommaList'

expandedCommaNonEmpty :: GrammarRegex a -> GrammarRegex a
expandedCommaNonEmpty r = REUnion
[ REMunch1 reSpacedComma r
, reComma <> RESpaces <> REMunch1 reSpacedComma r
, REMunch1 reSpacedComma r <> RESpaces <> reComma
]

expandedCommaList' :: GrammarRegex a -> [GrammarRegex a]
expandedCommaList' r =
[ REMunch reSpacedComma r
Expand Down
192 changes: 192 additions & 0 deletions cabal-install/Distribution/Client/IndexUtils/ActiveRepos.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Distribution.Client.IndexUtils.ActiveRepos (
ActiveRepos (..),
ActiveRepoEntry (..),
CombineStrategy (..),
organizeByRepos,
) where

import Distribution.Client.Compat.Prelude
import Distribution.Client.Types.RepoName (RepoName (..))
import Prelude ()

import Distribution.FieldGrammar.Described
import Distribution.Parsec (Parsec (..), parsecLeadingCommaList)
import Distribution.Pretty (Pretty (..), prettyShow)

import qualified Distribution.Compat.CharParsing as P
import qualified Text.PrettyPrint as Disp

-- $setup
-- >>> import Distribution.Parsec

-------------------------------------------------------------------------------
-- Types
-------------------------------------------------------------------------------

-- | Ordered list of active repositories.
newtype ActiveRepos = ActiveRepos [ActiveRepoEntry]
deriving (Eq, Show, Generic)

instance Binary ActiveRepos
instance Structured ActiveRepos
instance NFData ActiveRepos

instance Pretty ActiveRepos where
pretty (ActiveRepos [])
= Disp.text ":none"
pretty (ActiveRepos repos)
= Disp.hsep
$ Disp.punctuate Disp.comma
$ map pretty repos

-- | Note: empty string is not valid 'ActiveRepos'.
--
-- >>> simpleParsec "" :: Maybe ActiveRepos
-- Nothing
--
-- >>> simpleParsec ":none" :: Maybe ActiveRepos
-- Just (ActiveRepos [])
--
-- >>> simpleParsec ":rest" :: Maybe ActiveRepos
-- Just (ActiveRepos [ActiveRepoRest CombineStrategyMerge])
--
-- >>> simpleParsec "hackage.haskell.org, :rest, head.hackage:override" :: Maybe ActiveRepos
-- Just (ActiveRepos [ActiveRepo (RepoName "hackage.haskell.org") CombineStrategyMerge,ActiveRepoRest CombineStrategyMerge,ActiveRepo (RepoName "head.hackage") CombineStrategyOverride])
--
instance Parsec ActiveRepos where
parsec = ActiveRepos [] <$ P.try (P.string ":none")
<|> do
repos <- parsecLeadingCommaList parsec
return (ActiveRepos (toList repos))

instance Described ActiveRepos where
describe _ = REUnion
[ ":none"
, RECommaNonEmpty (describe (Proxy :: Proxy ActiveRepoEntry))
]

data ActiveRepoEntry
= ActiveRepoRest CombineStrategy -- ^ rest repositories, i.e. not explicitly listed as 'ActiveRepo'
| ActiveRepo RepoName CombineStrategy -- ^ explicit repository name
deriving (Eq, Show, Generic)

instance Binary ActiveRepoEntry
instance Structured ActiveRepoEntry
instance NFData ActiveRepoEntry

instance Pretty ActiveRepoEntry where
pretty (ActiveRepoRest s) =
Disp.text ":rest" <<>> Disp.colon <<>> pretty s
pretty (ActiveRepo r s) =
pretty r <<>> Disp.colon <<>> pretty s

instance Parsec ActiveRepoEntry where
parsec = leadColon <|> leadRepo where
leadColon = do
_ <- P.char ':'
token <- P.munch1 isAlpha
case token of
"rest" -> ActiveRepoRest <$> strategyP
"repo" -> P.char ':' *> leadRepo
_ -> P.unexpected $ "Unknown active repository entry type: " ++ token

leadRepo = do
r <- parsec
s <- strategyP
return (ActiveRepo r s)

strategyP = P.option CombineStrategyMerge (P.char ':' *> parsec)

instance Described ActiveRepoEntry where
describe _ = REUnion
[ ":rest" <> strategy
, REOpt ":repo:" <> describe (Proxy :: Proxy RepoName) <> strategy
]
where
strategy = REOpt $ ":" <> describe (Proxy :: Proxy CombineStrategy)

data CombineStrategy
= CombineStrategyMerge -- ^ merge existing versions
| CombineStrategyOverride -- ^ if later repository specifies a package,
-- all package versions are replaced
deriving (Eq, Show, Enum, Bounded, Generic)

instance Binary CombineStrategy
instance Structured CombineStrategy
instance NFData CombineStrategy

instance Pretty CombineStrategy where
pretty CombineStrategyMerge = Disp.text "merge"
pretty CombineStrategyOverride = Disp.text "override"

instance Parsec CombineStrategy where
parsec = P.choice
[ CombineStrategyMerge <$ P.string "merge"
, CombineStrategyOverride <$ P.string "override"
]

instance Described CombineStrategy where
describe _ = REUnion
[ "merge"
, "override"
]

-------------------------------------------------------------------------------
-- Organisation
-------------------------------------------------------------------------------

-- | Sort values 'RepoName' according to 'ActiveRepos' list.
--
-- >>> let repos = [RepoName "a", RepoName "b", RepoName "c"]
-- >>> organizeByRepos (ActiveRepos [ActiveRepoRest CombineStrategyMerge]) id repos
-- Right [(RepoName "a",CombineStrategyMerge),(RepoName "b",CombineStrategyMerge),(RepoName "c",CombineStrategyMerge)]
--
-- >>> organizeByRepos (ActiveRepos [ActiveRepo (RepoName "b") CombineStrategyOverride, ActiveRepoRest CombineStrategyMerge]) id repos
-- Right [(RepoName "b",CombineStrategyOverride),(RepoName "a",CombineStrategyMerge),(RepoName "c",CombineStrategyMerge)]
--
-- >>> organizeByRepos (ActiveRepos [ActiveRepoRest CombineStrategyMerge, ActiveRepo (RepoName "b") CombineStrategyOverride]) id repos
-- Right [(RepoName "a",CombineStrategyMerge),(RepoName "c",CombineStrategyMerge),(RepoName "b",CombineStrategyOverride)]
--
-- >>> organizeByRepos (ActiveRepos [ActiveRepoRest CombineStrategyMerge, ActiveRepo (RepoName "d") CombineStrategyOverride]) id repos
-- Left "no repository provided d"
--
-- Note: currently if 'ActiveRepoRest' is provided more than once,
-- rest-repositories will be multiple times in the output.
--
organizeByRepos
:: forall a. ActiveRepos
-> (a -> RepoName)
-> [a]
-> Either String [(a, CombineStrategy)]
organizeByRepos (ActiveRepos xs0) sel ys0 =
-- here we use lazyness to do only one traversal
let (rest, result) = case go rest xs0 ys0 of
Right (rest', result') -> (rest', Right result')
Left err -> ([], Left err)
in result
where
go :: [a] -> [ActiveRepoEntry] -> [a] -> Either String ([a], [(a, CombineStrategy)])
go _rest [] ys = Right (ys, [])
go rest (ActiveRepoRest s : xs) ys =
go rest xs ys <&> \(rest', result) ->
(rest', map (\x -> (x, s)) rest ++ result)
go rest (ActiveRepo r s : xs) ys = do
(z, zs) <- extract r ys
go rest xs zs <&> \(rest', result) ->
(rest', (z, s) : result)

extract :: RepoName -> [a] -> Either String (a, [a])
extract r = loop id where
loop _acc [] = Left $ "no repository provided " ++ prettyShow r
loop acc (x:xs)
| sel x == r = Right (x, acc xs)
| otherwise = loop (acc . (x :)) xs

(<&>)
:: Either err ([s], b)
-> (([s], b) -> ([s], c))
-> Either err ([s], c)
(<&>) = flip fmap
1 change: 1 addition & 0 deletions cabal-install/cabal-install.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ executable cabal
Distribution.Client.HashValue
Distribution.Client.HttpUtils
Distribution.Client.IndexUtils
Distribution.Client.IndexUtils.ActiveRepos
Distribution.Client.IndexUtils.IndexState
Distribution.Client.IndexUtils.Timestamp
Distribution.Client.Init
Expand Down
1 change: 1 addition & 0 deletions cabal-install/cabal-install.cabal.pp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
Distribution.Client.HashValue
Distribution.Client.HttpUtils
Distribution.Client.IndexUtils
Distribution.Client.IndexUtils.ActiveRepos
Distribution.Client.IndexUtils.IndexState
Distribution.Client.IndexUtils.Timestamp
Distribution.Client.Init
Expand Down
19 changes: 15 additions & 4 deletions cabal-install/tests/UnitTests/Distribution/Client/Described.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ import Test.QuickCheck (Arbitrary (..), Gen, Property, choose, counterexam
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.QuickCheck (testProperty)

import Distribution.FieldGrammar.Described (Described (..), GrammarRegex (..), reComma, reSpacedComma, reSpacedList)
import Distribution.FieldGrammar.Described
(Described (..), GrammarRegex (..), reComma, reSpacedComma, reSpacedList)
import Distribution.Parsec (eitherParsec)
import Distribution.Pretty (prettyShow)

import qualified Distribution.Utils.CharSet as CS

import Distribution.Client.IndexUtils.IndexState (RepoIndexState, TotalIndexState)
import Distribution.Client.IndexUtils.Timestamp (Timestamp)
import Distribution.Client.Types (RepoName)
import Distribution.Client.IndexUtils.ActiveRepos (ActiveRepos)
import Distribution.Client.IndexUtils.IndexState (RepoIndexState, TotalIndexState)
import Distribution.Client.IndexUtils.Timestamp (Timestamp)
import Distribution.Client.Types (RepoName)

import qualified RERE as RE
import qualified RERE.CharSet as RE
Expand All @@ -33,6 +35,7 @@ tests = testGroup "Described"
, testDescribed (Proxy :: Proxy RepoIndexState)
, testDescribed (Proxy :: Proxy TotalIndexState)
, testDescribed (Proxy :: Proxy RepoName)
, testDescribed (Proxy :: Proxy ActiveRepos)
]

-------------------------------------------------------------------------------
Expand Down Expand Up @@ -132,13 +135,21 @@ convert = go id . vacuous where
go _ RESpaces1 = RE.ch_ ' ' RE.\/ " " RE.\/ "\n"

go f (RECommaList r) = go f (expandedCommaList r)
go f (RECommaNonEmpty r)= go f (expandedCommaNonEmpty r)
go f (REOptCommaList r) = go f (expandedOptCommaList r)

go _ RETodo = RE.Null

expandedCommaList :: GrammarRegex a -> GrammarRegex a
expandedCommaList = REUnion . expandedCommaList'

expandedCommaNonEmpty :: GrammarRegex a -> GrammarRegex a
expandedCommaNonEmpty r = REUnion
[ REMunch1 reSpacedComma r
, reComma <> RESpaces <> REMunch1 reSpacedComma r
, REMunch1 reSpacedComma r <> RESpaces <> reComma
]

expandedCommaList' :: GrammarRegex a -> [GrammarRegex a]
expandedCommaList' r =
[ REMunch reSpacedComma r
Expand Down

0 comments on commit e49387a

Please sign in to comment.