{-# LANGUAGE DeriveGeneric #-}

module Distribution.Types.Component
  ( Component (..)
  , foldComponent
  , componentBuildInfo
  , componentBuildable
  , componentName
  , partitionComponents
  ) where

import Distribution.Compat.Prelude
import Prelude ()

import Distribution.Types.Benchmark
import Distribution.Types.Executable
import Distribution.Types.ForeignLib
import Distribution.Types.Library
import Distribution.Types.TestSuite

import Distribution.Types.BuildInfo
import Distribution.Types.ComponentName

import qualified Distribution.Types.BuildInfo.Lens as L

data Component
  = CLib Library
  | CFLib ForeignLib
  | CExe Executable
  | CTest TestSuite
  | CBench Benchmark
  deriving (Generic, Show, Eq, Read)

instance Binary Component
instance Structured Component

instance Semigroup Component where
  CLib l <> CLib l' = CLib (l <> l')
  CFLib l <> CFLib l' = CFLib (l <> l')
  CExe e <> CExe e' = CExe (e <> e')
  CTest t <> CTest t' = CTest (t <> t')
  CBench b <> CBench b' = CBench (b <> b')
  _ <> _ = error "Cannot merge Component"

instance L.HasBuildInfo Component where
  buildInfo f (CLib l) = CLib <$> L.buildInfo f l
  buildInfo f (CFLib l) = CFLib <$> L.buildInfo f l
  buildInfo f (CExe e) = CExe <$> L.buildInfo f e
  buildInfo f (CTest t) = CTest <$> L.buildInfo f t
  buildInfo f (CBench b) = CBench <$> L.buildInfo f b

foldComponent
  :: (Library -> a)
  -> (ForeignLib -> a)
  -> (Executable -> a)
  -> (TestSuite -> a)
  -> (Benchmark -> a)
  -> Component
  -> a
foldComponent f _ _ _ _ (CLib lib) = f lib
foldComponent _ f _ _ _ (CFLib flib) = f flib
foldComponent _ _ f _ _ (CExe exe) = f exe
foldComponent _ _ _ f _ (CTest tst) = f tst
foldComponent _ _ _ _ f (CBench bch) = f bch

componentBuildInfo :: Component -> BuildInfo
componentBuildInfo =
  foldComponent libBuildInfo foreignLibBuildInfo buildInfo testBuildInfo benchmarkBuildInfo

-- | Is a component buildable (i.e., not marked with @buildable: False@)?
-- See also this note in
-- "Distribution.Types.ComponentRequestedSpec#buildable_vs_enabled_components".
--
-- @since 2.0.0.2
componentBuildable :: Component -> Bool
componentBuildable = buildable . componentBuildInfo

componentName :: Component -> ComponentName
componentName =
  foldComponent
    (CLibName . libName)
    (CFLibName . foreignLibName)
    (CExeName . exeName)
    (CTestName . testName)
    (CBenchName . benchmarkName)

partitionComponents
  :: [Component]
  -> ([Library], [ForeignLib], [Executable], [TestSuite], [Benchmark])
partitionComponents = foldr (foldComponent fa fb fc fd fe) ([], [], [], [], [])
  where
    fa x ~(a, b, c, d, e) = (x : a, b, c, d, e)
    fb x ~(a, b, c, d, e) = (a, x : b, c, d, e)
    fc x ~(a, b, c, d, e) = (a, b, x : c, d, e)
    fd x ~(a, b, c, d, e) = (a, b, c, x : d, e)
    fe x ~(a, b, c, d, e) = (a, b, c, d, x : e)
