{-# OPTIONS_GHC -fno-warn-orphans #-} {-# Language CPP #-} {-# Language StandaloneDeriving #-} {-# Language DeriveGeneric #-} {-# Language DeriveDataTypeable #-} {-# Language DeriveFunctor #-} module Parse ( parsePackageDescription , readPackageDescription , displayError , printWarnings , Result(..) , result ) where import Control.DeepSeq import Data.Data import Data.Maybe import Distribution.PackageDescription.Parse (parseGenericPackageDescription) import Distribution.ParseUtils import Distribution.Simple.Utils import Distribution.Verbosity import GHC.Generics import Prelude.Compat import System.Environment import System.Exit import System.IO #if MIN_VERSION_base(4,9,0) import Data.Functor.Classes #endif -- | Like Cabal's @ParseResult@, but treats warnings as a separate failure -- case. data Result a = Error (Maybe LineNo) String -- ^ Parse error on the given line. | Warn [PWarning] -- ^ Warnings emitted during parse. | Success a -- ^ The input is a compliant package description. deriving (Show, Eq, Functor, Generic, Typeable, Data) #if MIN_VERSION_base(4,6,0) deriving instance Generic1 Result #endif #if MIN_VERSION_base(4,9,0) instance Show1 Result where liftShowsPrec sp _ p (Success n) = showsUnaryWith sp "Success" p n liftShowsPrec _ _ p (Warn pws) = showsUnaryWith showsPrec "Warn" p pws liftShowsPrec _ _ p (Error ml s) = showsBinaryWith showsPrec showsPrec "Error" p ml s instance Eq1 Result where liftEq eq (Success a) (Success b) = eq a b liftEq _ (Error ml s) (Error ml2 s2) = (ml,s) == (ml2,s2) liftEq _ (Warn pws) (Warn pws2) = pws == pws2 liftEq _ _ _ = False #endif -- | Case analysis for 'Result'. result :: (Maybe LineNo -> String -> b) -> ([PWarning] -> b) -> (a -> b) -> Result a -> b result e w s p = case p of Error l m -> e l m Warn ws -> w ws Success r -> s r instance NFData a => NFData (Result a) deriving instance Generic PWarning deriving instance Data PWarning deriving instance Typeable PWarning instance NFData PWarning -- | This function is similar to Cabal's own file parser, except that it -- treats warnings as a separate failure case. There are a wide range of -- different behaviors accepted by different Cabal parser versions. Parse -- warnings generally indicate a version-related inconsistency, so we play -- it safe here. parsePackageDescription input = case parseGenericPackageDescription input of ParseFailed e -> uncurry Error $ locatedErrorMsg e ParseOk warnings x | null warnings -> Success x | otherwise -> Warn $ reverse warnings -- | Shorthand to combine 'parsePackageDescription' and one of 'printWarnings' or -- 'displayError'. The given 'FilePath' is used only for error messages and -- is not read from. readPackageDescription fpath = result (displayError fpath) (printWarnings fpath) return . parsePackageDescription -- | Print some warnings to 'stderr' and exit. printWarnings :: Maybe FilePath -> [PWarning] -> IO a printWarnings fpath ps = mapM_ (warn normal . showPWarning (fromMaybe "" fpath)) ps >> exitFailure -- | Print a parse error to 'stderr', annotated with filepath and line -- number (if available), then exit. displayError :: Maybe FilePath -> Maybe LineNo -> String -> IO a displayError fpath line' message = do prog <- getProgName hPutStrLn stderr $ prog ++ ": " ++ fromMaybe "" fpath ++ (case line' of Just lineno -> ":" ++ show lineno Nothing -> "") ++ ": " ++ message exitFailure