{-# OPTIONS_GHC -fno-warn-orphans #-}

{-# LANGUAGE CPP            #-}
{-# LANGUAGE DeriveAnyClass #-}

{- |
Copyright: (c) 2020-2022 Kowainik
SPDX-License-Identifier: MPL-2.0
Maintainer: Kowainik <xrom.xkov@gmail.com>

Data types and functions to work with different types of 'Extension's.

@extensions@ library supports the following types of extensions:

 +-------------------------------------------+----------------------------+
 | @Haskell2010@ Default Enabled Extensions  | 'On' of 'OnOffExtension's  |
 +-------------------------------------------+----------------------------+
 | @Haskell2010@ Default Disabled Extensions | 'Off' of 'OnOffExtension's |
 +-------------------------------------------+----------------------------+
 | @SafeHaskell@ Extensions                  | 'SafeHaskellExtension's    |
 +-------------------------------------------+----------------------------+

-}

module Extensions.Types
    ( Extensions (..)
    , ParsedExtensions (..)
    , CabalAndModuleExtensions (..)
    , ExtensionsResult

      -- * Errors
    , ExtensionsError (..)
    , CabalException (..)
    , ModuleParseError (..)

      -- ** Defaults / empty data types
    , emptyExtensions
    , emptyParsedExtensions

      -- * Enabled/Disabled Haskell2010 Extensions
    , OnOffExtension (..)
    , showOnOffExtension
    , readOnOffExtension
    , readExtension
    , mergeExtensions
    , mergeAnyExtensions
    , default2010Extensions

      -- * Safe Haskell Extensions
    , SafeHaskellExtension (..)
    ) where

import Control.Applicative ((<|>))
import Control.Exception (Exception)
import Data.Foldable (foldl')
import Data.List.NonEmpty (NonEmpty (..))
import Data.Set (Set)
import Data.Text (Text)
import GHC.LanguageExtensions.Type (Extension (..))
import Text.Read (readMaybe)

import qualified Data.Set as Set
import qualified Data.Text as Text
import qualified Text.Parsec as Parsec


{- | Main returned data type that includes merged 'OnOffExtension's and possibly
one 'SafeHaskellExtension'.
-}
data Extensions = Extensions
    { Extensions -> Set OnOffExtension
extensionsAll  :: !(Set OnOffExtension)
    , Extensions -> Maybe SafeHaskellExtension
extensionsSafe :: !(Maybe SafeHaskellExtension)
    } deriving stock (Int -> Extensions -> ShowS
[Extensions] -> ShowS
Extensions -> String
(Int -> Extensions -> ShowS)
-> (Extensions -> String)
-> ([Extensions] -> ShowS)
-> Show Extensions
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Extensions -> ShowS
showsPrec :: Int -> Extensions -> ShowS
$cshow :: Extensions -> String
show :: Extensions -> String
$cshowList :: [Extensions] -> ShowS
showList :: [Extensions] -> ShowS
Show, Extensions -> Extensions -> Bool
(Extensions -> Extensions -> Bool)
-> (Extensions -> Extensions -> Bool) -> Eq Extensions
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Extensions -> Extensions -> Bool
== :: Extensions -> Extensions -> Bool
$c/= :: Extensions -> Extensions -> Bool
/= :: Extensions -> Extensions -> Bool
Eq)

{- | Extensions that are collected in the result of parsing @.cabal@ file or
Haskell module (both 'OnOffExtension' and possibly one 'SafeHaskellExtension').

'OnOffExtension's are not necessary unique. They reflect exactly the extensions
found during parsing.
-}
data ParsedExtensions = ParsedExtensions
    { ParsedExtensions -> [OnOffExtension]
parsedExtensionsAll  :: ![OnOffExtension]
    , ParsedExtensions -> Maybe SafeHaskellExtension
parsedExtensionsSafe :: !(Maybe SafeHaskellExtension)
    } deriving stock (Int -> ParsedExtensions -> ShowS
[ParsedExtensions] -> ShowS
ParsedExtensions -> String
(Int -> ParsedExtensions -> ShowS)
-> (ParsedExtensions -> String)
-> ([ParsedExtensions] -> ShowS)
-> Show ParsedExtensions
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ParsedExtensions -> ShowS
showsPrec :: Int -> ParsedExtensions -> ShowS
$cshow :: ParsedExtensions -> String
show :: ParsedExtensions -> String
$cshowList :: [ParsedExtensions] -> ShowS
showList :: [ParsedExtensions] -> ShowS
Show, ParsedExtensions -> ParsedExtensions -> Bool
(ParsedExtensions -> ParsedExtensions -> Bool)
-> (ParsedExtensions -> ParsedExtensions -> Bool)
-> Eq ParsedExtensions
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ParsedExtensions -> ParsedExtensions -> Bool
== :: ParsedExtensions -> ParsedExtensions -> Bool
$c/= :: ParsedExtensions -> ParsedExtensions -> Bool
/= :: ParsedExtensions -> ParsedExtensions -> Bool
Eq)

-- | Stores extensions from @.cabal@ file and module separately.
data CabalAndModuleExtensions = CabalAndModuleExtensions
    { CabalAndModuleExtensions -> ParsedExtensions
cabalExtensions  :: !ParsedExtensions
    , CabalAndModuleExtensions -> ParsedExtensions
moduleExtensions :: !ParsedExtensions
    } deriving stock (Int -> CabalAndModuleExtensions -> ShowS
[CabalAndModuleExtensions] -> ShowS
CabalAndModuleExtensions -> String
(Int -> CabalAndModuleExtensions -> ShowS)
-> (CabalAndModuleExtensions -> String)
-> ([CabalAndModuleExtensions] -> ShowS)
-> Show CabalAndModuleExtensions
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CabalAndModuleExtensions -> ShowS
showsPrec :: Int -> CabalAndModuleExtensions -> ShowS
$cshow :: CabalAndModuleExtensions -> String
show :: CabalAndModuleExtensions -> String
$cshowList :: [CabalAndModuleExtensions] -> ShowS
showList :: [CabalAndModuleExtensions] -> ShowS
Show, CabalAndModuleExtensions -> CabalAndModuleExtensions -> Bool
(CabalAndModuleExtensions -> CabalAndModuleExtensions -> Bool)
-> (CabalAndModuleExtensions -> CabalAndModuleExtensions -> Bool)
-> Eq CabalAndModuleExtensions
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CabalAndModuleExtensions -> CabalAndModuleExtensions -> Bool
== :: CabalAndModuleExtensions -> CabalAndModuleExtensions -> Bool
$c/= :: CabalAndModuleExtensions -> CabalAndModuleExtensions -> Bool
/= :: CabalAndModuleExtensions -> CabalAndModuleExtensions -> Bool
Eq)

-- | Type alias for the result of extensions analysis.
type ExtensionsResult = Either ExtensionsError Extensions

-- | Represents possible errors during the work of extensions analyser.
data ExtensionsError
    -- | Parse error during module extensions parsing.
    = ModuleParseError FilePath ModuleParseError
    -- | Error during @.cabal@ file reading/parsing.
    | CabalError CabalException
    -- | File is in cabal file, but the source file is not provided where requested.
    | SourceNotFound FilePath
    -- | Source file is provided, but module is not in cabal file.
    | NotCabalModule FilePath
    -- | Conflicting 'SafeHaskellExtension's in one scope.
    | SafeHaskellConflict (NonEmpty SafeHaskellExtension)
    deriving stock (Int -> ExtensionsError -> ShowS
[ExtensionsError] -> ShowS
ExtensionsError -> String
(Int -> ExtensionsError -> ShowS)
-> (ExtensionsError -> String)
-> ([ExtensionsError] -> ShowS)
-> Show ExtensionsError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ExtensionsError -> ShowS
showsPrec :: Int -> ExtensionsError -> ShowS
$cshow :: ExtensionsError -> String
show :: ExtensionsError -> String
$cshowList :: [ExtensionsError] -> ShowS
showList :: [ExtensionsError] -> ShowS
Show, ExtensionsError -> ExtensionsError -> Bool
(ExtensionsError -> ExtensionsError -> Bool)
-> (ExtensionsError -> ExtensionsError -> Bool)
-> Eq ExtensionsError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ExtensionsError -> ExtensionsError -> Bool
== :: ExtensionsError -> ExtensionsError -> Bool
$c/= :: ExtensionsError -> ExtensionsError -> Bool
/= :: ExtensionsError -> ExtensionsError -> Bool
Eq)

{- | Exception that gets thrown when working with @.cabal@ files.
-}
data CabalException
    -- | The @.cabal@ file is not found.
    = CabalFileNotFound FilePath
    -- | Parsing errors in the @.cabal@ file.
    | CabalParseError Text
    -- | Conflicting 'SafeHaskellExtension's in one scope.
    | CabalSafeExtensionsConflict (NonEmpty SafeHaskellExtension)
    deriving stock (Int -> CabalException -> ShowS
[CabalException] -> ShowS
CabalException -> String
(Int -> CabalException -> ShowS)
-> (CabalException -> String)
-> ([CabalException] -> ShowS)
-> Show CabalException
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CabalException -> ShowS
showsPrec :: Int -> CabalException -> ShowS
$cshow :: CabalException -> String
show :: CabalException -> String
$cshowList :: [CabalException] -> ShowS
showList :: [CabalException] -> ShowS
Show, CabalException -> CabalException -> Bool
(CabalException -> CabalException -> Bool)
-> (CabalException -> CabalException -> Bool) -> Eq CabalException
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CabalException -> CabalException -> Bool
== :: CabalException -> CabalException -> Bool
$c/= :: CabalException -> CabalException -> Bool
/= :: CabalException -> CabalException -> Bool
Eq)
    deriving anyclass (Show CabalException
Typeable CabalException
(Typeable CabalException, Show CabalException) =>
(CabalException -> SomeException)
-> (SomeException -> Maybe CabalException)
-> (CabalException -> String)
-> Exception CabalException
SomeException -> Maybe CabalException
CabalException -> String
CabalException -> SomeException
forall e.
(Typeable e, Show e) =>
(e -> SomeException)
-> (SomeException -> Maybe e) -> (e -> String) -> Exception e
$ctoException :: CabalException -> SomeException
toException :: CabalException -> SomeException
$cfromException :: SomeException -> Maybe CabalException
fromException :: SomeException -> Maybe CabalException
$cdisplayException :: CabalException -> String
displayException :: CabalException -> String
Exception)

-- | Error while parsing Haskell source file.
data ModuleParseError
    -- | File parsing error.
    = ParsecError Parsec.ParseError
    -- | Uknown extensions were used in the module.
    | UnknownExtensions (NonEmpty String)
    -- | Conflicting 'SafeHaskellExtension's in one scope.
    | ModuleSafeHaskellConflict (NonEmpty SafeHaskellExtension)
    -- | Module file not found.
    | FileNotFound FilePath
    deriving stock (Int -> ModuleParseError -> ShowS
[ModuleParseError] -> ShowS
ModuleParseError -> String
(Int -> ModuleParseError -> ShowS)
-> (ModuleParseError -> String)
-> ([ModuleParseError] -> ShowS)
-> Show ModuleParseError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ModuleParseError -> ShowS
showsPrec :: Int -> ModuleParseError -> ShowS
$cshow :: ModuleParseError -> String
show :: ModuleParseError -> String
$cshowList :: [ModuleParseError] -> ShowS
showList :: [ModuleParseError] -> ShowS
Show, ModuleParseError -> ModuleParseError -> Bool
(ModuleParseError -> ModuleParseError -> Bool)
-> (ModuleParseError -> ModuleParseError -> Bool)
-> Eq ModuleParseError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ModuleParseError -> ModuleParseError -> Bool
== :: ModuleParseError -> ModuleParseError -> Bool
$c/= :: ModuleParseError -> ModuleParseError -> Bool
/= :: ModuleParseError -> ModuleParseError -> Bool
Eq)


-- | Empty 'Extensions' with no specified 'SafeHaskellExtension'.
emptyExtensions :: Extensions
emptyExtensions :: Extensions
emptyExtensions = Extensions
    { extensionsAll :: Set OnOffExtension
extensionsAll = Set OnOffExtension
forall a. Monoid a => a
mempty
    , extensionsSafe :: Maybe SafeHaskellExtension
extensionsSafe = Maybe SafeHaskellExtension
forall a. Maybe a
Nothing
    }

-- | Empty 'ParsedExtensions' with no specified 'SafeHaskellExtension'.
emptyParsedExtensions :: ParsedExtensions
emptyParsedExtensions :: ParsedExtensions
emptyParsedExtensions = ParsedExtensions
    { parsedExtensionsAll :: [OnOffExtension]
parsedExtensionsAll = []
    , parsedExtensionsSafe :: Maybe SafeHaskellExtension
parsedExtensionsSafe = Maybe SafeHaskellExtension
forall a. Maybe a
Nothing
    }

{- | Language extensions that are used by Safe Haskell to indicate safety of the
code.

To find out more, checkout the official documentation on @SafeHaskell@:

 * [Safe Language Pragmas](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/safe_haskell.html#safe-haskell)
-}
data SafeHaskellExtension
    = Unsafe
    | Trustworthy
    | Safe
    deriving stock (Int -> SafeHaskellExtension -> ShowS
[SafeHaskellExtension] -> ShowS
SafeHaskellExtension -> String
(Int -> SafeHaskellExtension -> ShowS)
-> (SafeHaskellExtension -> String)
-> ([SafeHaskellExtension] -> ShowS)
-> Show SafeHaskellExtension
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SafeHaskellExtension -> ShowS
showsPrec :: Int -> SafeHaskellExtension -> ShowS
$cshow :: SafeHaskellExtension -> String
show :: SafeHaskellExtension -> String
$cshowList :: [SafeHaskellExtension] -> ShowS
showList :: [SafeHaskellExtension] -> ShowS
Show, ReadPrec [SafeHaskellExtension]
ReadPrec SafeHaskellExtension
Int -> ReadS SafeHaskellExtension
ReadS [SafeHaskellExtension]
(Int -> ReadS SafeHaskellExtension)
-> ReadS [SafeHaskellExtension]
-> ReadPrec SafeHaskellExtension
-> ReadPrec [SafeHaskellExtension]
-> Read SafeHaskellExtension
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: Int -> ReadS SafeHaskellExtension
readsPrec :: Int -> ReadS SafeHaskellExtension
$creadList :: ReadS [SafeHaskellExtension]
readList :: ReadS [SafeHaskellExtension]
$creadPrec :: ReadPrec SafeHaskellExtension
readPrec :: ReadPrec SafeHaskellExtension
$creadListPrec :: ReadPrec [SafeHaskellExtension]
readListPrec :: ReadPrec [SafeHaskellExtension]
Read, SafeHaskellExtension -> SafeHaskellExtension -> Bool
(SafeHaskellExtension -> SafeHaskellExtension -> Bool)
-> (SafeHaskellExtension -> SafeHaskellExtension -> Bool)
-> Eq SafeHaskellExtension
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SafeHaskellExtension -> SafeHaskellExtension -> Bool
== :: SafeHaskellExtension -> SafeHaskellExtension -> Bool
$c/= :: SafeHaskellExtension -> SafeHaskellExtension -> Bool
/= :: SafeHaskellExtension -> SafeHaskellExtension -> Bool
Eq, Eq SafeHaskellExtension
Eq SafeHaskellExtension =>
(SafeHaskellExtension -> SafeHaskellExtension -> Ordering)
-> (SafeHaskellExtension -> SafeHaskellExtension -> Bool)
-> (SafeHaskellExtension -> SafeHaskellExtension -> Bool)
-> (SafeHaskellExtension -> SafeHaskellExtension -> Bool)
-> (SafeHaskellExtension -> SafeHaskellExtension -> Bool)
-> (SafeHaskellExtension
    -> SafeHaskellExtension -> SafeHaskellExtension)
-> (SafeHaskellExtension
    -> SafeHaskellExtension -> SafeHaskellExtension)
-> Ord SafeHaskellExtension
SafeHaskellExtension -> SafeHaskellExtension -> Bool
SafeHaskellExtension -> SafeHaskellExtension -> Ordering
SafeHaskellExtension
-> SafeHaskellExtension -> SafeHaskellExtension
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: SafeHaskellExtension -> SafeHaskellExtension -> Ordering
compare :: SafeHaskellExtension -> SafeHaskellExtension -> Ordering
$c< :: SafeHaskellExtension -> SafeHaskellExtension -> Bool
< :: SafeHaskellExtension -> SafeHaskellExtension -> Bool
$c<= :: SafeHaskellExtension -> SafeHaskellExtension -> Bool
<= :: SafeHaskellExtension -> SafeHaskellExtension -> Bool
$c> :: SafeHaskellExtension -> SafeHaskellExtension -> Bool
> :: SafeHaskellExtension -> SafeHaskellExtension -> Bool
$c>= :: SafeHaskellExtension -> SafeHaskellExtension -> Bool
>= :: SafeHaskellExtension -> SafeHaskellExtension -> Bool
$cmax :: SafeHaskellExtension
-> SafeHaskellExtension -> SafeHaskellExtension
max :: SafeHaskellExtension
-> SafeHaskellExtension -> SafeHaskellExtension
$cmin :: SafeHaskellExtension
-> SafeHaskellExtension -> SafeHaskellExtension
min :: SafeHaskellExtension
-> SafeHaskellExtension -> SafeHaskellExtension
Ord, Int -> SafeHaskellExtension
SafeHaskellExtension -> Int
SafeHaskellExtension -> [SafeHaskellExtension]
SafeHaskellExtension -> SafeHaskellExtension
SafeHaskellExtension
-> SafeHaskellExtension -> [SafeHaskellExtension]
SafeHaskellExtension
-> SafeHaskellExtension
-> SafeHaskellExtension
-> [SafeHaskellExtension]
(SafeHaskellExtension -> SafeHaskellExtension)
-> (SafeHaskellExtension -> SafeHaskellExtension)
-> (Int -> SafeHaskellExtension)
-> (SafeHaskellExtension -> Int)
-> (SafeHaskellExtension -> [SafeHaskellExtension])
-> (SafeHaskellExtension
    -> SafeHaskellExtension -> [SafeHaskellExtension])
-> (SafeHaskellExtension
    -> SafeHaskellExtension -> [SafeHaskellExtension])
-> (SafeHaskellExtension
    -> SafeHaskellExtension
    -> SafeHaskellExtension
    -> [SafeHaskellExtension])
-> Enum SafeHaskellExtension
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: SafeHaskellExtension -> SafeHaskellExtension
succ :: SafeHaskellExtension -> SafeHaskellExtension
$cpred :: SafeHaskellExtension -> SafeHaskellExtension
pred :: SafeHaskellExtension -> SafeHaskellExtension
$ctoEnum :: Int -> SafeHaskellExtension
toEnum :: Int -> SafeHaskellExtension
$cfromEnum :: SafeHaskellExtension -> Int
fromEnum :: SafeHaskellExtension -> Int
$cenumFrom :: SafeHaskellExtension -> [SafeHaskellExtension]
enumFrom :: SafeHaskellExtension -> [SafeHaskellExtension]
$cenumFromThen :: SafeHaskellExtension
-> SafeHaskellExtension -> [SafeHaskellExtension]
enumFromThen :: SafeHaskellExtension
-> SafeHaskellExtension -> [SafeHaskellExtension]
$cenumFromTo :: SafeHaskellExtension
-> SafeHaskellExtension -> [SafeHaskellExtension]
enumFromTo :: SafeHaskellExtension
-> SafeHaskellExtension -> [SafeHaskellExtension]
$cenumFromThenTo :: SafeHaskellExtension
-> SafeHaskellExtension
-> SafeHaskellExtension
-> [SafeHaskellExtension]
enumFromThenTo :: SafeHaskellExtension
-> SafeHaskellExtension
-> SafeHaskellExtension
-> [SafeHaskellExtension]
Enum, SafeHaskellExtension
SafeHaskellExtension
-> SafeHaskellExtension -> Bounded SafeHaskellExtension
forall a. a -> a -> Bounded a
$cminBound :: SafeHaskellExtension
minBound :: SafeHaskellExtension
$cmaxBound :: SafeHaskellExtension
maxBound :: SafeHaskellExtension
Bounded)

-- | Represents enabled/disabled extensions.
data OnOffExtension
    = On Extension
    | Off Extension
    deriving stock (Int -> OnOffExtension -> ShowS
[OnOffExtension] -> ShowS
OnOffExtension -> String
(Int -> OnOffExtension -> ShowS)
-> (OnOffExtension -> String)
-> ([OnOffExtension] -> ShowS)
-> Show OnOffExtension
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> OnOffExtension -> ShowS
showsPrec :: Int -> OnOffExtension -> ShowS
$cshow :: OnOffExtension -> String
show :: OnOffExtension -> String
$cshowList :: [OnOffExtension] -> ShowS
showList :: [OnOffExtension] -> ShowS
Show, OnOffExtension -> OnOffExtension -> Bool
(OnOffExtension -> OnOffExtension -> Bool)
-> (OnOffExtension -> OnOffExtension -> Bool) -> Eq OnOffExtension
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: OnOffExtension -> OnOffExtension -> Bool
== :: OnOffExtension -> OnOffExtension -> Bool
$c/= :: OnOffExtension -> OnOffExtension -> Bool
/= :: OnOffExtension -> OnOffExtension -> Bool
Eq, Eq OnOffExtension
Eq OnOffExtension =>
(OnOffExtension -> OnOffExtension -> Ordering)
-> (OnOffExtension -> OnOffExtension -> Bool)
-> (OnOffExtension -> OnOffExtension -> Bool)
-> (OnOffExtension -> OnOffExtension -> Bool)
-> (OnOffExtension -> OnOffExtension -> Bool)
-> (OnOffExtension -> OnOffExtension -> OnOffExtension)
-> (OnOffExtension -> OnOffExtension -> OnOffExtension)
-> Ord OnOffExtension
OnOffExtension -> OnOffExtension -> Bool
OnOffExtension -> OnOffExtension -> Ordering
OnOffExtension -> OnOffExtension -> OnOffExtension
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: OnOffExtension -> OnOffExtension -> Ordering
compare :: OnOffExtension -> OnOffExtension -> Ordering
$c< :: OnOffExtension -> OnOffExtension -> Bool
< :: OnOffExtension -> OnOffExtension -> Bool
$c<= :: OnOffExtension -> OnOffExtension -> Bool
<= :: OnOffExtension -> OnOffExtension -> Bool
$c> :: OnOffExtension -> OnOffExtension -> Bool
> :: OnOffExtension -> OnOffExtension -> Bool
$c>= :: OnOffExtension -> OnOffExtension -> Bool
>= :: OnOffExtension -> OnOffExtension -> Bool
$cmax :: OnOffExtension -> OnOffExtension -> OnOffExtension
max :: OnOffExtension -> OnOffExtension -> OnOffExtension
$cmin :: OnOffExtension -> OnOffExtension -> OnOffExtension
min :: OnOffExtension -> OnOffExtension -> OnOffExtension
Ord)

-- | Display 'OnOffExtension' as GHC recognizes it.
showOnOffExtension :: OnOffExtension -> Text
showOnOffExtension :: OnOffExtension -> Text
showOnOffExtension = \case
    On Extension
ext  -> Extension -> Text
showExtension Extension
ext
    Off Extension
ext -> Text
"No" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Extension -> Text
showExtension Extension
ext
  where
    showExtension :: Extension -> Text
    showExtension :: Extension -> Text
showExtension = \case
        Extension
Cpp        -> Text
"CPP"
#if !MIN_VERSION_ghc_boot_th(9,4,1)
        RecordPuns -> "NamedFieldPuns"
#endif
        Extension
ext        -> String -> Text
Text.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Extension -> String
forall a. Show a => a -> String
show Extension
ext

{- | Parse 'OnOffExtension' from a string that specifies extension.
-}
readOnOffExtension :: String -> Maybe OnOffExtension
readOnOffExtension :: String -> Maybe OnOffExtension
readOnOffExtension String
s =
    (Extension -> OnOffExtension
On (Extension -> OnOffExtension)
-> Maybe Extension -> Maybe OnOffExtension
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> Maybe Extension
readExtension String
s) Maybe OnOffExtension
-> Maybe OnOffExtension -> Maybe OnOffExtension
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Extension -> OnOffExtension
Off (Extension -> OnOffExtension)
-> Maybe Extension -> Maybe OnOffExtension
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Extension
readOffExtension)
  where
    readOffExtension :: Maybe Extension
    readOffExtension :: Maybe Extension
readOffExtension = do
        (String
"No", String
ext) <- (String, String) -> Maybe (String, String)
forall a. a -> Maybe a
Just ((String, String) -> Maybe (String, String))
-> (String, String) -> Maybe (String, String)
forall a b. (a -> b) -> a -> b
$ Int -> String -> (String, String)
forall a. Int -> [a] -> ([a], [a])
splitAt Int
2 String
s
        String -> Maybe Extension
readExtension String
ext

{- | Parse 'Extension' from a string. 'Read' instance for 'Extension'
doesn't always work since some extensions are named differently.
-}
readExtension :: String -> Maybe Extension
readExtension :: String -> Maybe Extension
readExtension = \case
    String
"GeneralisedNewtypeDeriving" -> Extension -> Maybe Extension
forall a. a -> Maybe a
Just Extension
GeneralizedNewtypeDeriving
#if !MIN_VERSION_ghc_boot_th(9,4,1)
    "NamedFieldPuns"             -> Just RecordPuns
    "RecordPuns"                 -> Nothing
#endif
    String
"Rank2Types"                 -> Extension -> Maybe Extension
forall a. a -> Maybe a
Just Extension
RankNTypes
    String
"CPP"                        -> Extension -> Maybe Extension
forall a. a -> Maybe a
Just Extension
Cpp
    String
"Cpp"                        -> Maybe Extension
forall a. Maybe a
Nothing
    String
s                            -> String -> Maybe Extension
forall a. Read a => String -> Maybe a
readMaybe String
s

{- | Take accumulated 'OnOffExtension's, and merge them into one 'Set',
excluding enabling of 'default2010Extensions'.

If the default extension is enabled manually it still won't count as it doesn't
affect real behaviour. However, disabling of default extension will be included
in the list.

So, basically, this set will only have 'On' extensions that are not defaults,
and 'Off' extensions of defaults.

'foldl\'' is used in order to process them in the right order: first all cabal
extensions and then extensions from the module in the order of appearance.
-}
mergeExtensions :: [OnOffExtension] -> Set OnOffExtension
mergeExtensions :: [OnOffExtension] -> Set OnOffExtension
mergeExtensions = (Set OnOffExtension -> OnOffExtension -> Set OnOffExtension)
-> Set OnOffExtension -> [OnOffExtension] -> Set OnOffExtension
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Set OnOffExtension -> OnOffExtension -> Set OnOffExtension
handleExt Set OnOffExtension
forall a. Set a
Set.empty
  where
    handleExt :: Set OnOffExtension -> OnOffExtension -> Set OnOffExtension
    handleExt :: Set OnOffExtension -> OnOffExtension -> Set OnOffExtension
handleExt Set OnOffExtension
exts (On Extension
e)
        | Extension
e Extension -> [Extension] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Extension]
default2010Extensions = OnOffExtension -> Set OnOffExtension -> Set OnOffExtension
forall a. Ord a => a -> Set a -> Set a
Set.delete (Extension -> OnOffExtension
Off Extension
e) Set OnOffExtension
exts
        | Bool
otherwise                      = OnOffExtension -> Set OnOffExtension -> Set OnOffExtension
forall a. Ord a => a -> Set a -> Set a
Set.insert (Extension -> OnOffExtension
On Extension
e) Set OnOffExtension
exts
    handleExt Set OnOffExtension
exts (Off Extension
e)
        | Extension
e Extension -> [Extension] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Extension]
default2010Extensions = OnOffExtension -> Set OnOffExtension -> Set OnOffExtension
forall a. Ord a => a -> Set a -> Set a
Set.insert (Extension -> OnOffExtension
Off Extension
e) Set OnOffExtension
exts
        | Bool
otherwise                      = OnOffExtension -> Set OnOffExtension -> Set OnOffExtension
forall a. Ord a => a -> Set a -> Set a
Set.delete (Extension -> OnOffExtension
On Extension
e) Set OnOffExtension
exts

{- | Similar to 'mergeExtensions', but also merge 'SafeHaskellExtension's.
In case of conflicting 'SafeHaskellExtension' returns 'Left' with the pair of
conflicting extension constructors under 'SafeHaskellConflict' error.
-}
mergeAnyExtensions
    :: ParsedExtensions  -- ^ Cabal parsed extensions.
    -> ParsedExtensions  -- ^ Module parsed extensions.
    -> ExtensionsResult
mergeAnyExtensions :: ParsedExtensions -> ParsedExtensions -> ExtensionsResult
mergeAnyExtensions (ParsedExtensions [OnOffExtension]
exts1 Maybe SafeHaskellExtension
safe1) (ParsedExtensions [OnOffExtension]
exts2 Maybe SafeHaskellExtension
safe2) = case (Maybe SafeHaskellExtension
safe1, Maybe SafeHaskellExtension
safe2) of
    (Maybe SafeHaskellExtension
Nothing, Maybe SafeHaskellExtension
safe) -> Extensions -> ExtensionsResult
forall a b. b -> Either a b
Right (Extensions -> ExtensionsResult) -> Extensions -> ExtensionsResult
forall a b. (a -> b) -> a -> b
$ Extensions
        { extensionsAll :: Set OnOffExtension
extensionsAll = Set OnOffExtension
mergedExts
        , extensionsSafe :: Maybe SafeHaskellExtension
extensionsSafe = Maybe SafeHaskellExtension
safe
        }
    (Maybe SafeHaskellExtension
safe, Maybe SafeHaskellExtension
Nothing) -> Extensions -> ExtensionsResult
forall a b. b -> Either a b
Right (Extensions -> ExtensionsResult) -> Extensions -> ExtensionsResult
forall a b. (a -> b) -> a -> b
$ Extensions
        { extensionsAll :: Set OnOffExtension
extensionsAll = Set OnOffExtension
mergedExts
        , extensionsSafe :: Maybe SafeHaskellExtension
extensionsSafe = Maybe SafeHaskellExtension
safe
        }
    (Just SafeHaskellExtension
s1, Just SafeHaskellExtension
s2) ->
        if Maybe SafeHaskellExtension
safe1 Maybe SafeHaskellExtension -> Maybe SafeHaskellExtension -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe SafeHaskellExtension
safe2
        then Extensions -> ExtensionsResult
forall a b. b -> Either a b
Right (Extensions -> ExtensionsResult) -> Extensions -> ExtensionsResult
forall a b. (a -> b) -> a -> b
$ Extensions
            { extensionsAll :: Set OnOffExtension
extensionsAll = Set OnOffExtension
mergedExts
            , extensionsSafe :: Maybe SafeHaskellExtension
extensionsSafe = Maybe SafeHaskellExtension
safe1
            }
        else ExtensionsError -> ExtensionsResult
forall a b. a -> Either a b
Left (ExtensionsError -> ExtensionsResult)
-> ExtensionsError -> ExtensionsResult
forall a b. (a -> b) -> a -> b
$ NonEmpty SafeHaskellExtension -> ExtensionsError
SafeHaskellConflict (NonEmpty SafeHaskellExtension -> ExtensionsError)
-> NonEmpty SafeHaskellExtension -> ExtensionsError
forall a b. (a -> b) -> a -> b
$ SafeHaskellExtension
s1 SafeHaskellExtension
-> [SafeHaskellExtension] -> NonEmpty SafeHaskellExtension
forall a. a -> [a] -> NonEmpty a
:| [SafeHaskellExtension
s2]
  where
    mergedExts :: Set OnOffExtension
    mergedExts :: Set OnOffExtension
mergedExts = [OnOffExtension] -> Set OnOffExtension
mergeExtensions ([OnOffExtension]
exts1 [OnOffExtension] -> [OnOffExtension] -> [OnOffExtension]
forall a. Semigroup a => a -> a -> a
<> [OnOffExtension]
exts2)

-- | Default enabled extensions for @Haskell2010@
default2010Extensions :: [Extension]
default2010Extensions :: [Extension]
default2010Extensions =
    [ Extension
ImplicitPrelude
    , Extension
StarIsType
    , Extension
MonomorphismRestriction
    , Extension
DatatypeContexts
    , Extension
TraditionalRecordSyntax
    , Extension
EmptyDataDecls
    , Extension
ForeignFunctionInterface
    , Extension
PatternGuards
    , Extension
DoAndIfThenElse
    , Extension
RelaxedPolyRec
#if __GLASGOW_HASKELL__ >= 810
    , Extension
CUSKs
#endif
    ]

deriving stock instance Read Extension
#if __GLASGOW_HASKELL__ < 900
deriving stock instance Ord Extension
#endif