{-# LANGUAGE PatternSynonyms #-}

module GHC.Types.SourceFile
   ( HscSource(HsBootFile, HsigFile, ..)
   , HsBootOrSig(..)
   , hscSourceToIsBoot
   , isHsBootOrSig
   , isHsBootFile, isHsigFile
   , hscSourceString
   )
where

import GHC.Prelude
import GHC.Utils.Binary
import GHC.Unit.Types

{- Note [HscSource types]
~~~~~~~~~~~~~~~~~~~~~~~~~
There are three types of source file for Haskell code:

     * HsSrcFile is an ordinary hs file which contains code,

     * HsBootFile is an hs-boot file, which is used to break
       recursive module imports (there will always be an
       HsSrcFile associated with it), and

     * HsigFile is an hsig file, which contains only type
       signatures and is used to specify signatures for
       modules.

Syntactically, hs-boot files and hsig files are quite similar: they
only include type signatures and must be associated with an
actual HsSrcFile.  isHsBootOrSig allows us to abstract over code
which is indifferent to which.  However, there are some important
differences, mostly owing to the fact that hsigs are proper
modules (you `import Sig` directly) whereas HsBootFiles are
temporary placeholders (you `import {-# SOURCE #-} Mod).
When we finish compiling the true implementation of an hs-boot,
we replace the HomeModInfo with the real HsSrcFile.  An HsigFile, on the
other hand, is never replaced (in particular, we *cannot* use the
HomeModInfo of the original HsSrcFile backing the signature, since it
will export too many symbols.)

Additionally, while HsSrcFile is the only Haskell file
which has *code*, we do generate .o files for HsigFile, because
this is how the recompilation checker figures out if a file
needs to be recompiled.  These are fake object files which
should NOT be linked against.
-}

data HsBootOrSig
  = HsBoot -- ^ .hs-boot file
  | Hsig   -- ^ .hsig file
   deriving (Eq, Ord, Show)

data HscSource
   -- | .hs file
   = HsSrcFile
   -- | .hs-boot or .hsig file
   | HsBootOrSig !HsBootOrSig
   deriving (Eq, Ord, Show)

{-# COMPLETE HsSrcFile, HsBootFile, HsigFile #-}
pattern HsBootFile, HsigFile :: HscSource
pattern HsBootFile = HsBootOrSig HsBoot
pattern HsigFile   = HsBootOrSig Hsig

-- | Tests if an 'HscSource' is a boot file, primarily for constructing elements
-- of 'BuildModule'. We conflate signatures and modules because they are bound
-- in the same namespace; only boot interfaces can be disambiguated with
-- `import {-# SOURCE #-}`.
hscSourceToIsBoot :: HscSource -> IsBootInterface
hscSourceToIsBoot HsBootFile = IsBoot
hscSourceToIsBoot _ = NotBoot

instance Binary HscSource where
    put_ bh HsSrcFile = putByte bh 0
    put_ bh HsBootFile = putByte bh 1
    put_ bh HsigFile = putByte bh 2
    get bh = do
        h <- getByte bh
        case h of
            0 -> return HsSrcFile
            1 -> return HsBootFile
            _ -> return HsigFile

hscSourceString :: HscSource -> String
hscSourceString HsSrcFile  = ""
hscSourceString HsBootFile = "[boot]"
hscSourceString HsigFile   = "[sig]"

-- See Note [HscSource types]
isHsBootOrSig :: HscSource -> Bool
isHsBootOrSig (HsBootOrSig _) = True
isHsBootOrSig HsSrcFile       = False

isHsBootFile :: HscSource -> Bool
isHsBootFile HsBootFile = True
isHsBootFile _          = False

isHsigFile :: HscSource -> Bool
isHsigFile HsigFile = True
isHsigFile _        = False
