-----------------------------------------------------------------------------
-- |
-- 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 -> FilePath -> IO GenericPackageDescription
readGenericPackageDescription = (ByteString -> ParseResult GenericPackageDescription)
-> Verbosity -> FilePath -> IO GenericPackageDescription
forall a.
(ByteString -> ParseResult a) -> Verbosity -> FilePath -> IO a
readAndParseFile ByteString -> ParseResult GenericPackageDescription
parseGenericPackageDescription

readHookedBuildInfo :: Verbosity -> FilePath -> IO HookedBuildInfo
readHookedBuildInfo :: Verbosity -> FilePath -> IO HookedBuildInfo
readHookedBuildInfo = (ByteString -> ParseResult HookedBuildInfo)
-> Verbosity -> FilePath -> IO HookedBuildInfo
forall a.
(ByteString -> ParseResult a) -> Verbosity -> FilePath -> 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 :: (ByteString -> ParseResult a) -> Verbosity -> FilePath -> IO a
readAndParseFile ByteString -> ParseResult a
parser Verbosity
verbosity FilePath
fpath = do
    Bool
exists <- FilePath -> IO Bool
doesFileExist FilePath
fpath
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
exists (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
      Verbosity -> FilePath -> IO ()
forall a. Verbosity -> FilePath -> IO a
die' Verbosity
verbosity (FilePath -> IO ()) -> FilePath -> IO ()
forall a b. (a -> b) -> a -> b
$
        FilePath
"Error Parsing: file \"" FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
fpath FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
"\" doesn't exist. Cannot continue."
    ByteString
bs <- FilePath -> IO ByteString
BS.readFile FilePath
fpath
    (ByteString -> ParseResult a)
-> Verbosity -> FilePath -> ByteString -> IO a
forall a.
(ByteString -> ParseResult a)
-> Verbosity -> FilePath -> ByteString -> IO a
parseString ByteString -> ParseResult a
parser Verbosity
verbosity FilePath
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 :: (ByteString -> ParseResult a)
-> Verbosity -> FilePath -> ByteString -> IO a
parseString ByteString -> ParseResult a
parser Verbosity
verbosity FilePath
name ByteString
bs = do
    let ([PWarning]
warnings, Either (Maybe Version, NonEmpty PError) a
result) = ParseResult a
-> ([PWarning], Either (Maybe Version, NonEmpty PError) a)
forall a.
ParseResult a
-> ([PWarning], Either (Maybe Version, NonEmpty PError) a)
runParseResult (ByteString -> ParseResult a
parser ByteString
bs)
    (PWarning -> IO ()) -> [PWarning] -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ (Verbosity -> FilePath -> IO ()
warn Verbosity
verbosity (FilePath -> IO ()) -> (PWarning -> FilePath) -> PWarning -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> PWarning -> FilePath
showPWarning FilePath
name) (Verbosity -> [PWarning] -> [PWarning]
flattenDups Verbosity
verbosity [PWarning]
warnings)
    case Either (Maybe Version, NonEmpty PError) a
result of
        Right a
x -> a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
x
        Left (Maybe Version
_, NonEmpty PError
errors) -> do
            (PError -> IO ()) -> NonEmpty PError -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ (Verbosity -> FilePath -> IO ()
warn Verbosity
verbosity (FilePath -> IO ()) -> (PError -> FilePath) -> PError -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> PError -> FilePath
showPError FilePath
name) NonEmpty PError
errors
            Verbosity -> FilePath -> IO a
forall a. Verbosity -> FilePath -> IO a
die' Verbosity
verbosity (FilePath -> IO a) -> FilePath -> IO a
forall a b. (a -> b) -> a -> b
$ FilePath
"Failed parsing \"" FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
name FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
"\"."

-- | 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 Verbosity -> Verbosity -> Bool
forall a. Ord a => a -> a -> Bool
<= Verbosity
normal = [PWarning]
rest [PWarning] -> [PWarning] -> [PWarning]
forall a. [a] -> [a] -> [a]
++ [PWarning]
experimentals
    | Bool
otherwise = [PWarning]
ws -- show all instances
    where
        ([PWarning]
exps, [PWarning]
rest) = (PWarning -> Bool) -> [PWarning] -> ([PWarning], [PWarning])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition (\(PWarning PWarnType
w Position
_ FilePath
_) -> PWarnType
w PWarnType -> PWarnType -> Bool
forall a. Eq a => a -> a -> Bool
== PWarnType
PWTExperimental) [PWarning]
ws
        experimentals :: [PWarning]
experimentals =
             ([PWarning] -> [PWarning]) -> [[PWarning]] -> [PWarning]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap [PWarning] -> [PWarning]
flatCount
           ([[PWarning]] -> [PWarning])
-> ([PWarning] -> [[PWarning]]) -> [PWarning] -> [PWarning]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (PWarning -> PWarning -> Bool) -> [PWarning] -> [[PWarning]]
forall a. (a -> a -> Bool) -> [a] -> [[a]]
groupBy ((PWarning -> FilePath) -> PWarning -> PWarning -> Bool
forall a b. Eq a => (b -> a) -> b -> b -> Bool
equating PWarning -> FilePath
warningStr)
           ([PWarning] -> [[PWarning]])
-> ([PWarning] -> [PWarning]) -> [PWarning] -> [[PWarning]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (PWarning -> PWarning -> Ordering) -> [PWarning] -> [PWarning]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy ((PWarning -> FilePath) -> PWarning -> PWarning -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing PWarning -> FilePath
warningStr)
           ([PWarning] -> [PWarning]) -> [PWarning] -> [PWarning]
forall a b. (a -> b) -> a -> b
$ [PWarning]
exps

        warningStr :: PWarning -> FilePath
warningStr (PWarning PWarnType
_ Position
_ FilePath
w) = FilePath
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 FilePath
w:[PWarning]
xs) =
            [PWarnType -> Position -> FilePath -> PWarning
PWarning PWarnType
t Position
pos
                (FilePath
w FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath -> Int -> FilePath
forall r. PrintfType r => FilePath -> r
printf FilePath
" (and %d more occurrences)" ([PWarning] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [PWarning]
xs))
            ]