content: {-# LANGUAGE CPP                 #-}
  {-# LANGUAGE NamedFieldPuns      #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  module Language.Haskell.Homplexity.CabalFiles
      ( CabalFile
      , CabalPackage(..)
      , warnings
      , parseCabalFile
      , languageExtensions
      ) where
  
  
  import Data.Generics.Uniplate.Data as U
  import Data.String (fromString)
  import Language.Haskell.Extension as Cabal
  import Language.Haskell.Exts.Extension as HSE
  import Language.Haskell.Exts.SrcLoc
  import qualified Data.ByteString as BS
  
  import Language.Haskell.Homplexity.Message
  
  #if MIN_VERSION_Cabal(3,0,0)
  import Distribution.PackageDescription.Parsec
  import Distribution.Parsec.Warning
  import Distribution.Types.GenericPackageDescription
  #elif MIN_VERSION_Cabal(2,0,0)
  import Distribution.PackageDescription.Parsec
  import Distribution.Types.GenericPackageDescription
  import Distribution.Parsec.Common
  #else
  import Distribution.PackageDescription(GenericPackageDescription(..))
  import Distribution.PackageDescription.Parse(parsePackageDescription)
  import Distribution.ParseUtils
  
  parseGenericPackageDescription = parsePackageDescription . BS.unpack
  #endif
  
  
  
  -- | Project file cabal file related info
  --
  data CabalFile = CabalFile
      { warnings    :: [PWarning]
      , description :: GenericPackageDescription
      }
      deriving (Show)
  
  
  parseCabalFile :: FilePath -> IO (Either Log CabalFile)
  parseCabalFile cabalFilePath = do
      cabalFile <- BS.readFile cabalFilePath
      case runParseResult $ parseGenericPackageDescription cabalFile of
          (_, Left err) ->
              return $ Left $ critical (SrcLoc cabalFilePath 0 0) (show err)
          (warns, Right desc) ->
              return $ Right $ CabalFile warns desc
  
  
  cabalExtensionToHseExtension :: Cabal.Extension -> HSE.Extension
  cabalExtensionToHseExtension (Cabal.UnknownExtension ex) = HSE.UnknownExtension ex
  cabalExtensionToHseExtension (Cabal.EnableExtension ex)  = HSE.EnableExtension (cabalKnownExtensionToHseKnownExtension ex)
  cabalExtensionToHseExtension (Cabal.DisableExtension ex) = HSE.DisableExtension (cabalKnownExtensionToHseKnownExtension ex)
  
  
  cabalKnownExtensionToHseKnownExtension :: Cabal.KnownExtension -> HSE.KnownExtension
  cabalKnownExtensionToHseKnownExtension Cabal.OverlappingInstances = HSE.OverlappingInstances
  -- cabalKnownExtensionToHseKnownExtension Cabal.AllowAmbiguousTypes = HSE.AllowAmbiguousTypes
  -- cabalKnownExtensionToHseKnownExtension Cabal.ApplicativeDo = HSE.ApplicativeDo
  cabalKnownExtensionToHseKnownExtension Cabal.Arrows = HSE.Arrows
  -- cabalKnownExtensionToHseKnownExtension Cabal.AutoDeriveTypeable = HSE.AutoDeriveTypeable
  cabalKnownExtensionToHseKnownExtension Cabal.BangPatterns = HSE.BangPatterns
  cabalKnownExtensionToHseKnownExtension Cabal.BinaryLiterals = HSE.BinaryLiterals
  -- cabalKnownExtensionToHseKnownExtension Cabal.BlockArguments = HSE.BlockArguments
  cabalKnownExtensionToHseKnownExtension Cabal.CApiFFI = HSE.CApiFFI
  cabalKnownExtensionToHseKnownExtension Cabal.CPP = HSE.CPP
  cabalKnownExtensionToHseKnownExtension Cabal.ConstrainedClassMethods = HSE.ConstrainedClassMethods
  cabalKnownExtensionToHseKnownExtension Cabal.ConstraintKinds = HSE.ConstraintKinds
  cabalKnownExtensionToHseKnownExtension Cabal.DataKinds = HSE.DataKinds
  cabalKnownExtensionToHseKnownExtension Cabal.DatatypeContexts = HSE.DatatypeContexts
  cabalKnownExtensionToHseKnownExtension Cabal.DefaultSignatures = HSE.DefaultSignatures
  cabalKnownExtensionToHseKnownExtension Cabal.DeriveDataTypeable = HSE.DeriveDataTypeable
  cabalKnownExtensionToHseKnownExtension Cabal.DeriveFoldable = HSE.DeriveFoldable
  cabalKnownExtensionToHseKnownExtension Cabal.DeriveFunctor = HSE.DeriveFunctor
  cabalKnownExtensionToHseKnownExtension Cabal.DeriveGeneric = HSE.DeriveGeneric
  -- cabalKnownExtensionToHseKnownExtension Cabal.DeriveLift = HSE.DeriveLift
  cabalKnownExtensionToHseKnownExtension Cabal.DeriveTraversable = HSE.DeriveTraversable
  cabalKnownExtensionToHseKnownExtension Cabal.DisambiguateRecordFields = HSE.DisambiguateRecordFields
  cabalKnownExtensionToHseKnownExtension Cabal.DoAndIfThenElse = HSE.DoAndIfThenElse
  cabalKnownExtensionToHseKnownExtension Cabal.DoRec = HSE.DoRec
  -- cabalKnownExtensionToHseKnownExtension Cabal.DuplicateRecordFields = HSE.DuplicateRecordFields
  cabalKnownExtensionToHseKnownExtension Cabal.EmptyDataDecls = HSE.EmptyDataDecls
  cabalKnownExtensionToHseKnownExtension Cabal.ExistentialQuantification = HSE.ExistentialQuantification
  cabalKnownExtensionToHseKnownExtension Cabal.ExplicitForAll = HSE.ExplicitForAll
  cabalKnownExtensionToHseKnownExtension Cabal.ExplicitNamespaces = HSE.ExplicitNamespaces
  cabalKnownExtensionToHseKnownExtension Cabal.ExtendedDefaultRules = HSE.ExtendedDefaultRules
  cabalKnownExtensionToHseKnownExtension Cabal.ExtensibleRecords = HSE.ExtensibleRecords
  cabalKnownExtensionToHseKnownExtension Cabal.FlexibleContexts = HSE.FlexibleContexts
  cabalKnownExtensionToHseKnownExtension Cabal.FlexibleInstances = HSE.FlexibleInstances
  cabalKnownExtensionToHseKnownExtension Cabal.ForeignFunctionInterface = HSE.ForeignFunctionInterface
  cabalKnownExtensionToHseKnownExtension Cabal.FunctionalDependencies = HSE.FunctionalDependencies
  -- cabalKnownExtensionToHseKnownExtension Cabal.GADTSyntax = HSE.GADTSyntax
  cabalKnownExtensionToHseKnownExtension Cabal.GADTs = HSE.GADTs
  cabalKnownExtensionToHseKnownExtension Cabal.GHCForeignImportPrim = HSE.GHCForeignImportPrim
  cabalKnownExtensionToHseKnownExtension Cabal.GeneralizedNewtypeDeriving = HSE.GeneralizedNewtypeDeriving
  cabalKnownExtensionToHseKnownExtension Cabal.Generics = HSE.Generics
  cabalKnownExtensionToHseKnownExtension Cabal.HereDocuments = HSE.HereDocuments
  -- cabalKnownExtensionToHseKnownExtension Cabal.HexFloatLiterals = HSE.HexFloatLiterals
  cabalKnownExtensionToHseKnownExtension Cabal.ImplicitParams = HSE.ImplicitParams
  cabalKnownExtensionToHseKnownExtension Cabal.ImplicitPrelude = HSE.ImplicitPrelude
  cabalKnownExtensionToHseKnownExtension Cabal.ImpredicativeTypes = HSE.ImpredicativeTypes
  cabalKnownExtensionToHseKnownExtension Cabal.IncoherentInstances = HSE.IncoherentInstances
  cabalKnownExtensionToHseKnownExtension Cabal.InstanceSigs = HSE.InstanceSigs
  cabalKnownExtensionToHseKnownExtension Cabal.InterruptibleFFI = HSE.InterruptibleFFI
  cabalKnownExtensionToHseKnownExtension Cabal.KindSignatures = HSE.KindSignatures
  cabalKnownExtensionToHseKnownExtension Cabal.LambdaCase = HSE.LambdaCase
  cabalKnownExtensionToHseKnownExtension Cabal.LiberalTypeSynonyms = HSE.LiberalTypeSynonyms
  cabalKnownExtensionToHseKnownExtension Cabal.MagicHash = HSE.MagicHash
  -- cabalKnownExtensionToHseKnownExtension Cabal.MonadComprehensions = HSE.MonadComprehensions
  -- cabalKnownExtensionToHseKnownExtension Cabal.MonadFailDesugaring = HSE.MonadFailDesugaring
  cabalKnownExtensionToHseKnownExtension Cabal.MonoLocalBinds = HSE.MonoLocalBinds
  cabalKnownExtensionToHseKnownExtension Cabal.MonoPatBinds = HSE.MonoPatBinds
  cabalKnownExtensionToHseKnownExtension Cabal.MonomorphismRestriction = HSE.MonomorphismRestriction
  cabalKnownExtensionToHseKnownExtension Cabal.MultiParamTypeClasses = HSE.MultiParamTypeClasses
  cabalKnownExtensionToHseKnownExtension Cabal.MultiWayIf = HSE.MultiWayIf
  cabalKnownExtensionToHseKnownExtension Cabal.NPlusKPatterns = HSE.NPlusKPatterns
  cabalKnownExtensionToHseKnownExtension Cabal.NamedFieldPuns = HSE.NamedFieldPuns
  cabalKnownExtensionToHseKnownExtension Cabal.NamedWildCards = HSE.NamedWildCards
  -- cabalKnownExtensionToHseKnownExtension Cabal.NegativeLiterals = HSE.NegativeLiterals
  cabalKnownExtensionToHseKnownExtension Cabal.NewQualifiedOperators = HSE.NewQualifiedOperators
  cabalKnownExtensionToHseKnownExtension Cabal.NondecreasingIndentation = HSE.NondecreasingIndentation
  -- cabalKnownExtensionToHseKnownExtension Cabal.NullaryTypeClasses = HSE.NullaryTypeClasses
  -- cabalKnownExtensionToHseKnownExtension Cabal.NumDecimals = HSE.NumDecimals
  -- cabalKnownExtensionToHseKnownExtension Cabal.NumericUnderscores = HSE.NumericUnderscores
  -- cabalKnownExtensionToHseKnownExtension Cabal.OverlappingInstances = HSE.OverlappingInstances
  cabalKnownExtensionToHseKnownExtension Cabal.OverloadedLabels = HSE.OverloadedLabels
  -- cabalKnownExtensionToHseKnownExtension Cabal.OverloadedLists = HSE.OverloadedLists
  cabalKnownExtensionToHseKnownExtension Cabal.OverloadedStrings = HSE.OverloadedStrings
  cabalKnownExtensionToHseKnownExtension Cabal.PackageImports = HSE.PackageImports
  cabalKnownExtensionToHseKnownExtension Cabal.ParallelArrays = HSE.ParallelArrays
  cabalKnownExtensionToHseKnownExtension Cabal.ParallelListComp = HSE.ParallelListComp
  cabalKnownExtensionToHseKnownExtension Cabal.PartialTypeSignatures = HSE.PartialTypeSignatures
  cabalKnownExtensionToHseKnownExtension Cabal.PatternGuards = HSE.PatternGuards
  cabalKnownExtensionToHseKnownExtension Cabal.PatternSignatures = HSE.PatternSignatures
  cabalKnownExtensionToHseKnownExtension Cabal.PatternSynonyms = HSE.PatternSynonyms
  cabalKnownExtensionToHseKnownExtension Cabal.PolyKinds = HSE.PolyKinds
  cabalKnownExtensionToHseKnownExtension Cabal.PolymorphicComponents = HSE.PolymorphicComponents
  cabalKnownExtensionToHseKnownExtension Cabal.PostfixOperators = HSE.PostfixOperators
  -- cabalKnownExtensionToHseKnownExtension Cabal.QuantifiedConstraints = HSE.QuantifiedConstraints
  cabalKnownExtensionToHseKnownExtension Cabal.QuasiQuotes = HSE.QuasiQuotes
  cabalKnownExtensionToHseKnownExtension Cabal.Rank2Types = HSE.Rank2Types
  cabalKnownExtensionToHseKnownExtension Cabal.RankNTypes = HSE.RankNTypes
  cabalKnownExtensionToHseKnownExtension Cabal.RebindableSyntax = HSE.RebindableSyntax
  cabalKnownExtensionToHseKnownExtension Cabal.RecordPuns = HSE.RecordPuns
  cabalKnownExtensionToHseKnownExtension Cabal.RecordWildCards = HSE.RecordWildCards
  cabalKnownExtensionToHseKnownExtension Cabal.RecursiveDo = HSE.RecursiveDo
  cabalKnownExtensionToHseKnownExtension Cabal.RegularPatterns = HSE.RegularPatterns
  cabalKnownExtensionToHseKnownExtension Cabal.RelaxedPolyRec = HSE.RelaxedPolyRec
  cabalKnownExtensionToHseKnownExtension Cabal.RestrictedTypeSynonyms = HSE.RestrictedTypeSynonyms
  cabalKnownExtensionToHseKnownExtension Cabal.RoleAnnotations = HSE.RoleAnnotations
  cabalKnownExtensionToHseKnownExtension Cabal.Safe = HSE.Safe
  cabalKnownExtensionToHseKnownExtension Cabal.SafeImports = HSE.SafeImports
  cabalKnownExtensionToHseKnownExtension Cabal.ScopedTypeVariables = HSE.ScopedTypeVariables
  cabalKnownExtensionToHseKnownExtension Cabal.StandaloneDeriving = HSE.StandaloneDeriving
  -- cabalKnownExtensionToHseKnownExtension Cabal.StarIsType = HSE.StarIsType
  -- cabalKnownExtensionToHseKnownExtension Cabal.StaticPointers = HSE.StaticPointers
  -- cabalKnownExtensionToHseKnownExtension Cabal.Strict = HSE.Strict
  -- cabalKnownExtensionToHseKnownExtension Cabal.StrictData = HSE.StrictData
  cabalKnownExtensionToHseKnownExtension Cabal.TemplateHaskell = HSE.TemplateHaskell
  -- cabalKnownExtensionToHseKnownExtension Cabal.TemplateHaskellQuotes = HSE.TemplateHaskellQuotes
  -- cabalKnownExtensionToHseKnownExtension Cabal.TraditionalRecordSyntax = HSE.TraditionalRecordSyntax
  cabalKnownExtensionToHseKnownExtension Cabal.TransformListComp = HSE.TransformListComp
  cabalKnownExtensionToHseKnownExtension Cabal.Trustworthy = HSE.Trustworthy
  cabalKnownExtensionToHseKnownExtension Cabal.TupleSections = HSE.TupleSections
  cabalKnownExtensionToHseKnownExtension Cabal.TypeApplications = HSE.TypeApplications
  cabalKnownExtensionToHseKnownExtension Cabal.TypeFamilies = HSE.TypeFamilies
  -- cabalKnownExtensionToHseKnownExtension Cabal.TypeInType = HSE.TypeInType
  cabalKnownExtensionToHseKnownExtension Cabal.TypeOperators = HSE.TypeOperators
  cabalKnownExtensionToHseKnownExtension Cabal.TypeSynonymInstances = HSE.TypeSynonymInstances
  cabalKnownExtensionToHseKnownExtension Cabal.UnboxedTuples = HSE.UnboxedTuples
  cabalKnownExtensionToHseKnownExtension Cabal.UndecidableInstances = HSE.UndecidableInstances
  -- cabalKnownExtensionToHseKnownExtension Cabal.UndecidableSuperClasses = HSE.UndecidableSuperClasses
  cabalKnownExtensionToHseKnownExtension Cabal.UnicodeSyntax = HSE.UnicodeSyntax
  cabalKnownExtensionToHseKnownExtension Cabal.UnliftedFFITypes = HSE.UnliftedFFITypes
  -- cabalKnownExtensionToHseKnownExtension Cabal.Unsafe = HSE.Unsafe
  cabalKnownExtensionToHseKnownExtension Cabal.ViewPatterns = HSE.ViewPatterns
  cabalKnownExtensionToHseKnownExtension Cabal.XmlSyntax = HSE.XmlSyntax
  cabalKnownExtensionToHseKnownExtension Cabal.DeriveAnyClass = HSE.DeriveAnyClass
  cabalKnownExtensionToHseKnownExtension Cabal.DerivingStrategies = HSE.DerivingStrategies
  cabalKnownExtensionToHseKnownExtension Cabal.EmptyCase = HSE.EmptyCase
  cabalKnownExtensionToHseKnownExtension Cabal.JavaScriptFFI = HSE.JavaScriptFFI
  cabalKnownExtensionToHseKnownExtension Cabal.TypeFamilyDependencies = HSE.TypeFamilyDependencies
  cabalKnownExtensionToHseKnownExtension Cabal.UnboxedSums = HSE.UnboxedSums
  
  cabalKnownExtensionToHseKnownExtension ex = error $ "Extension '" ++ show ex ++ "' unsupported"
  
  
  -- | Cabal package identification
  --
  data CabalPackage = Library | Package String
  
  
  languageExtensions :: CabalPackage -> CabalFile -> [HSE.Extension]
  languageExtensions moduleName CabalFile{description} = map cabalExtensionToHseExtension $ languageExtensions' moduleName
    where
      languageExtensions' Library = [e | (e :: Cabal.Extension) <- U.universeBi (condLibrary description)]
      languageExtensions' (Package pname) =
          let pname' = fromString pname
              filt = filter (\(name,_) -> name == pname')
          in [e | (e :: Cabal.Extension) <- U.universeBi (filt $ condForeignLibs description)] ++
             [e | (e :: Cabal.Extension) <- U.universeBi (filt $ condExecutables description)] ++
             [e | (e :: Cabal.Extension) <- U.universeBi (filt $ condTestSuites description) ] ++
             [e | (e :: Cabal.Extension) <- U.universeBi (filt $ condBenchmarks description) ]
  
  