-----------------------------------------------------------------------------
-- |
-- Module      :  Distribution.Simple.PackageDescription
-- Copyright   :  Isaac Jones 2003-2005
-- License     :  BSD3
--
-- Maintainer  :  cabal-devel@haskell.org
-- Portability :  portable
--
-- This defines parsers for the @.cabal@ format

module Distribution.Simple.PackageDescription (
    -- * Read and Parse files
    readGenericPackageDescription,
    readHookedBuildInfo,

    -- * Utility Parsing function
    parseString,
    ) where

import Prelude ()
import Distribution.Compat.Prelude

import Distribution.Fields.ParseResult
import Distribution.PackageDescription
import Distribution.PackageDescription.Parsec
    ( parseGenericPackageDescription, parseHookedBuildInfo )
import Distribution.Parsec.Error ( showPError )
import Distribution.Parsec.Warning
    ( PWarning(..), PWarnType(PWTExperimental), showPWarning )
import Distribution.Simple.Utils ( equating, die', warn )
import Distribution.Verbosity ( normal, Verbosity )

import Data.List ( groupBy )
import Text.Printf ( printf )
import qualified Data.ByteString as BS
import System.Directory (doesFileExist)

readGenericPackageDescription :: Verbosity -> FilePath -> IO GenericPackageDescription
readGenericPackageDescription :: Verbosity -> String -> IO GenericPackageDescription
readGenericPackageDescription = forall a.
(ByteString -> ParseResult a) -> Verbosity -> String -> IO a
readAndParseFile ByteString -> ParseResult GenericPackageDescription
parseGenericPackageDescription

readHookedBuildInfo :: Verbosity -> FilePath -> IO HookedBuildInfo
readHookedBuildInfo :: Verbosity -> String -> IO HookedBuildInfo
readHookedBuildInfo = forall a.
(ByteString -> ParseResult a) -> Verbosity -> String -> IO a
readAndParseFile ByteString -> ParseResult HookedBuildInfo
parseHookedBuildInfo

-- | Helper combinator to do parsing plumbing for files.
--
-- Given a parser and a filename, return the parse of the file,
-- after checking if the file exists.
--
-- Argument order is chosen to encourage partial application.
readAndParseFile
    :: (BS.ByteString -> ParseResult a)  -- ^ File contents to final value parser
    -> Verbosity                         -- ^ Verbosity level
    -> FilePath                          -- ^ File to read
    -> IO a
readAndParseFile :: forall a.
(ByteString -> ParseResult a) -> Verbosity -> String -> IO a
readAndParseFile ByteString -> ParseResult a
parser Verbosity
verbosity String
fpath = do
    Bool
exists <- String -> IO Bool
doesFileExist String
fpath
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
exists forall a b. (a -> b) -> a -> b
$
      forall a. Verbosity -> String -> IO a
die' Verbosity
verbosity forall a b. (a -> b) -> a -> b
$
        String
"Error Parsing: file \"" forall a. [a] -> [a] -> [a]
++ String
fpath forall a. [a] -> [a] -> [a]
++ String
"\" doesn't exist. Cannot continue."
    ByteString
bs <- String -> IO ByteString
BS.readFile String
fpath
    forall a.
(ByteString -> ParseResult a)
-> Verbosity -> String -> ByteString -> IO a
parseString ByteString -> ParseResult a
parser Verbosity
verbosity String
fpath ByteString
bs

parseString
    :: (BS.ByteString -> ParseResult a)  -- ^ File contents to final value parser
    -> Verbosity                         -- ^ Verbosity level
    -> String                            -- ^ File name
    -> BS.ByteString
    -> IO a
parseString :: forall a.
(ByteString -> ParseResult a)
-> Verbosity -> String -> ByteString -> IO a
parseString ByteString -> ParseResult a
parser Verbosity
verbosity String
name ByteString
bs = do
    let ([PWarning]
warnings, Either (Maybe Version, NonEmpty PError) a
result) = forall a.
ParseResult a
-> ([PWarning], Either (Maybe Version, NonEmpty PError) a)
runParseResult (ByteString -> ParseResult a
parser ByteString
bs)
    forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ (Verbosity -> String -> IO ()
warn Verbosity
verbosity forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> PWarning -> String
showPWarning String
name) (Verbosity -> [PWarning] -> [PWarning]
flattenDups Verbosity
verbosity [PWarning]
warnings)
    case Either (Maybe Version, NonEmpty PError) a
result of
        Right a
x -> forall (m :: * -> *) a. Monad m => a -> m a
return a
x
        Left (Maybe Version
_, NonEmpty PError
errors) -> do
            forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ (Verbosity -> String -> IO ()
warn Verbosity
verbosity forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> PError -> String
showPError String
name) NonEmpty PError
errors
            forall a. Verbosity -> String -> IO a
die' Verbosity
verbosity forall a b. (a -> b) -> a -> b
$ String
"Failed parsing \"" forall a. [a] -> [a] -> [a]
++ String
name forall a. [a] -> [a] -> [a]
++ String
"\"."

-- | Collapse duplicate experimental feature warnings into single warning, with
-- a count of further sites
flattenDups :: Verbosity -> [PWarning] -> [PWarning]
flattenDups :: Verbosity -> [PWarning] -> [PWarning]
flattenDups Verbosity
verbosity [PWarning]
ws
    | Verbosity
verbosity forall a. Ord a => a -> a -> Bool
<= Verbosity
normal = [PWarning]
rest forall a. [a] -> [a] -> [a]
++ [PWarning]
experimentals
    | Bool
otherwise = [PWarning]
ws -- show all instances
    where
        ([PWarning]
exps, [PWarning]
rest) = forall a. (a -> Bool) -> [a] -> ([a], [a])
partition (\(PWarning PWarnType
w Position
_ String
_) -> PWarnType
w forall a. Eq a => a -> a -> Bool
== PWarnType
PWTExperimental) [PWarning]
ws
        experimentals :: [PWarning]
experimentals =
             forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap [PWarning] -> [PWarning]
flatCount
           forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> a -> Bool) -> [a] -> [[a]]
groupBy (forall a b. Eq a => (b -> a) -> b -> b -> Bool
equating PWarning -> String
warningStr)
           forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing PWarning -> String
warningStr)
           forall a b. (a -> b) -> a -> b
$ [PWarning]
exps

        warningStr :: PWarning -> String
warningStr (PWarning PWarnType
_ Position
_ String
w) = String
w

        -- flatten if we have 3 or more examples
        flatCount :: [PWarning] -> [PWarning]
        flatCount :: [PWarning] -> [PWarning]
flatCount w :: [PWarning]
w@[] = [PWarning]
w
        flatCount w :: [PWarning]
w@[PWarning
_] = [PWarning]
w
        flatCount w :: [PWarning]
w@[PWarning
_,PWarning
_] = [PWarning]
w
        flatCount (PWarning PWarnType
t Position
pos String
w:[PWarning]
xs) =
            [PWarnType -> Position -> String -> PWarning
PWarning PWarnType
t Position
pos
                (String
w forall a. Semigroup a => a -> a -> a
<> forall r. PrintfType r => String -> r
printf String
" (and %d more occurrences)" (forall (t :: * -> *) a. Foldable t => t a -> Int
length [PWarning]
xs))
            ]