module Distribution.FieldGrammar.Newtypes (
alaList,
alaList',
CommaVCat (..),
CommaFSep (..),
VCat (..),
FSep (..),
NoCommaFSep (..),
Sep (..),
List,
alaSet,
alaSet',
Set',
alaNonEmpty,
alaNonEmpty',
NonEmpty',
SpecVersion (..),
TestedWith (..),
SpecLicense (..),
Token (..),
Token' (..),
MQuoted (..),
FilePathNT (..),
) where
import Distribution.Compat.Newtype
import Distribution.Compat.Prelude
import Prelude ()
import Distribution.CabalSpecVersion
import Distribution.Compiler (CompilerFlavor)
import Distribution.License (License)
import Distribution.Parsec
import Distribution.Pretty
import Distribution.Version
(LowerBound (..), Version, VersionInterval (..), VersionRange, VersionRangeF (..), anyVersion, asVersionIntervals, cataVersionRange, mkVersion,
version0, versionNumbers)
import Text.PrettyPrint (Doc, comma, fsep, punctuate, text, vcat)
import qualified Data.List.NonEmpty as NE
import qualified Data.Set as Set
import qualified Distribution.Compat.CharParsing as P
import qualified Distribution.SPDX as SPDX
data CommaVCat = CommaVCat
data CommaFSep = CommaFSep
data VCat = VCat
data FSep = FSep
data NoCommaFSep = NoCommaFSep
class Sep sep where
prettySep :: Proxy sep -> [Doc] -> Doc
parseSep :: CabalParsing m => Proxy sep -> m a -> m [a]
parseSepNE :: CabalParsing m => Proxy sep -> m a -> m (NonEmpty a)
instance Sep CommaVCat where
prettySep _ = vcat . punctuate comma
parseSep _ p = do
v <- askCabalSpecVersion
if v >= CabalSpecV2_2 then parsecLeadingCommaList p else parsecCommaList p
parseSepNE _ p = do
v <- askCabalSpecVersion
if v >= CabalSpecV2_2 then parsecLeadingCommaNonEmpty p else parsecCommaNonEmpty p
instance Sep CommaFSep where
prettySep _ = fsep . punctuate comma
parseSep _ p = do
v <- askCabalSpecVersion
if v >= CabalSpecV2_2 then parsecLeadingCommaList p else parsecCommaList p
parseSepNE _ p = do
v <- askCabalSpecVersion
if v >= CabalSpecV2_2 then parsecLeadingCommaNonEmpty p else parsecCommaNonEmpty p
instance Sep VCat where
prettySep _ = vcat
parseSep _ p = do
v <- askCabalSpecVersion
if v >= CabalSpecV3_0 then parsecLeadingOptCommaList p else parsecOptCommaList p
parseSepNE _ p = NE.some1 (p <* P.spaces)
instance Sep FSep where
prettySep _ = fsep
parseSep _ p = do
v <- askCabalSpecVersion
if v >= CabalSpecV3_0 then parsecLeadingOptCommaList p else parsecOptCommaList p
parseSepNE _ p = NE.some1 (p <* P.spaces)
instance Sep NoCommaFSep where
prettySep _ = fsep
parseSep _ p = many (p <* P.spaces)
parseSepNE _ p = NE.some1 (p <* P.spaces)
newtype List sep b a = List { _getList :: [a] }
alaList :: sep -> [a] -> List sep (Identity a) a
alaList _ = List
alaList' :: sep -> (a -> b) -> [a] -> List sep b a
alaList' _ _ = List
instance Newtype [a] (List sep wrapper a)
instance (Newtype a b, Sep sep, Parsec b) => Parsec (List sep b a) where
parsec = pack . map (unpack :: b -> a) <$> parseSep (Proxy :: Proxy sep) parsec
instance (Newtype a b, Sep sep, Pretty b) => Pretty (List sep b a) where
pretty = prettySep (Proxy :: Proxy sep) . map (pretty . (pack :: a -> b)) . unpack
newtype Set' sep b a = Set' { _getSet :: Set a }
alaSet :: sep -> Set a -> Set' sep (Identity a) a
alaSet _ = Set'
alaSet' :: sep -> (a -> b) -> Set a -> Set' sep b a
alaSet' _ _ = Set'
instance Newtype (Set a) (Set' sep wrapper a)
instance (Newtype a b, Ord a, Sep sep, Parsec b) => Parsec (Set' sep b a) where
parsec = pack . Set.fromList . map (unpack :: b -> a) <$> parseSep (Proxy :: Proxy sep) parsec
instance (Newtype a b, Sep sep, Pretty b) => Pretty (Set' sep b a) where
pretty = prettySep (Proxy :: Proxy sep) . map (pretty . (pack :: a -> b)) . Set.toList . unpack
newtype NonEmpty' sep b a = NonEmpty' { _getNonEmpty :: NonEmpty a }
alaNonEmpty :: sep -> NonEmpty a -> NonEmpty' sep (Identity a) a
alaNonEmpty _ = NonEmpty'
alaNonEmpty' :: sep -> (a -> b) -> NonEmpty a -> NonEmpty' sep b a
alaNonEmpty' _ _ = NonEmpty'
instance Newtype (NonEmpty a) (NonEmpty' sep wrapper a)
instance (Newtype a b, Sep sep, Parsec b) => Parsec (NonEmpty' sep b a) where
parsec = pack . fmap (unpack :: b -> a) <$> parseSepNE (Proxy :: Proxy sep) parsec
instance (Newtype a b, Sep sep, Pretty b) => Pretty (NonEmpty' sep b a) where
pretty = prettySep (Proxy :: Proxy sep) . map (pretty . (pack :: a -> b)) . NE.toList . unpack
newtype Token = Token { getToken :: String }
instance Newtype String Token
instance Parsec Token where
parsec = pack <$> parsecToken
instance Pretty Token where
pretty = showToken . unpack
newtype Token' = Token' { getToken' :: String }
instance Newtype String Token'
instance Parsec Token' where
parsec = pack <$> parsecToken'
instance Pretty Token' where
pretty = showToken . unpack
newtype MQuoted a = MQuoted { getMQuoted :: a }
instance Newtype a (MQuoted a)
instance Parsec a => Parsec (MQuoted a) where
parsec = pack <$> parsecMaybeQuoted parsec
instance Pretty a => Pretty (MQuoted a) where
pretty = pretty . unpack
newtype FilePathNT = FilePathNT { getFilePathNT :: String }
instance Newtype String FilePathNT
instance Parsec FilePathNT where
parsec = do
token <- parsecToken
if null token
then P.unexpected "empty FilePath"
else return (FilePathNT token)
instance Pretty FilePathNT where
pretty = showFilePath . unpack
newtype SpecVersion = SpecVersion { getSpecVersion :: CabalSpecVersion }
deriving (Eq, Show)
instance Newtype CabalSpecVersion SpecVersion
instance Parsec SpecVersion where
parsec = do
e <- parsecSpecVersion
let ver :: Version
ver = either id specVersionFromRange e
digits :: [Int]
digits = versionNumbers ver
case cabalSpecFromVersionDigits digits of
Nothing -> fail $ "Unknown cabal spec version specified: " ++ prettyShow ver
Just csv -> do
case e of
Left _v | csv < CabalSpecV1_12 -> parsecWarning PWTSpecVersion $ concat
[ "With 1.10 or earlier, the 'cabal-version' field must use "
, "range syntax rather than a simple version number. Use "
, "'cabal-version: >= " ++ prettyShow ver ++ "'."
]
Right _vr | csv >= CabalSpecV1_12 -> parsecWarning PWTSpecVersion $ concat
[ "Packages with 'cabal-version: 1.12' or later should specify a "
, "specific version of the Cabal spec of the form "
, "'cabal-version: x.y'. "
, "Use 'cabal-version: " ++ prettyShow ver ++ "'."
]
Right vr | csv < CabalSpecV1_12
, not (simpleSpecVersionRangeSyntax vr) -> parsecWarning PWTSpecVersion $ concat
[ "It is recommended that the 'cabal-version' field only specify a "
, "version range of the form '>= x.y' for older cabal versions. Use "
, "'cabal-version: >= " ++ prettyShow ver ++ "'. "
, "Tools based on Cabal 1.10 and later will ignore upper bounds."
]
_ -> pure ()
return (pack csv)
where
parsecSpecVersion = Left <$> parsec <|> Right <$> range
range = do
vr <- parsec
if specVersionFromRange vr >= mkVersion [2,1]
then fail "cabal-version higher than 2.2 cannot be specified as a range. See https://github.com/haskell/cabal/issues/4899"
else return vr
specVersionFromRange :: VersionRange -> Version
specVersionFromRange versionRange = case asVersionIntervals versionRange of
[] -> version0
VersionInterval (LowerBound version _) _ : _ -> version
simpleSpecVersionRangeSyntax = cataVersionRange alg where
alg (OrLaterVersionF _) = True
alg _ = False
instance Pretty SpecVersion where
pretty (SpecVersion csv)
| csv >= CabalSpecV1_12 = text (showCabalSpecVersion csv)
| otherwise = text ">=" <<>> text (showCabalSpecVersion csv)
newtype SpecLicense = SpecLicense { getSpecLicense :: Either SPDX.License License }
instance Newtype (Either SPDX.License License) SpecLicense
instance Parsec SpecLicense where
parsec = do
v <- askCabalSpecVersion
if v >= CabalSpecV2_2
then SpecLicense . Left <$> parsec
else SpecLicense . Right <$> parsec
instance Pretty SpecLicense where
pretty = either pretty pretty . unpack
newtype TestedWith = TestedWith { getTestedWith :: (CompilerFlavor, VersionRange) }
instance Newtype (CompilerFlavor, VersionRange) TestedWith
instance Parsec TestedWith where
parsec = pack <$> parsecTestedWith
instance Pretty TestedWith where
pretty x = case unpack x of
(compiler, vr) -> pretty compiler <+> pretty vr
parsecTestedWith :: CabalParsing m => m (CompilerFlavor, VersionRange)
parsecTestedWith = do
name <- lexemeParsec
ver <- parsec <|> pure anyVersion
return (name, ver)