content: {-# LANGUAGE NoImplicitPrelude #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  {-|
  Module      : Stack.Types.NamedComponent
  Description : Module exporting the 'NamedComponent' type and related functions.
  License     : BSD-3-Clause
  
  Module exporting the 'NamedComponent' type and related functions.
  -}
  
  module Stack.Types.NamedComponent
    ( NamedComponent (..)
    , componentCachePath
    , renderComponent
    , renderComponentTo
    , renderPkgComponents
    , renderPkgComponent
    , exeComponents
    , testComponents
    , benchComponents
    , subLibComponents
    , isCLib
    , isCSubLib
    , isCExe
    , isCTest
    , isCBench
    , isPotentialDependency
    , splitComponents
    ) where
  
  import qualified Data.Set as Set
  import qualified Data.Text as T
  import           Stack.Prelude
  import           Stack.Types.ComponentUtils
                     ( StackUnqualCompName, unqualCompToString, unqualCompToText
                     )
  
  -- | Type representing components of a fully-resolved Cabal package.
  data NamedComponent
    = CLib
      -- The \'main\' unnamed library component.
    | CSubLib !StackUnqualCompName
      -- A named \'subsidiary\' or \'ancillary\` library component (sub-library).
    | CFlib !StackUnqualCompName
      -- A foreign library.
    | CExe !StackUnqualCompName
      -- A named executable component.
    | CTest !StackUnqualCompName
      -- A named test-suite component.
    | CBench !StackUnqualCompName
      -- A named benchmark component.
    deriving (Eq, Ord, Show)
  
  -- | Render a component to anything with an "IsString" instance. For 'Text'
  -- prefer 'renderComponent'.
  renderComponentTo :: IsString a => NamedComponent -> a
  renderComponentTo = fromString . T.unpack . renderComponent
  
  renderComponent :: NamedComponent -> Text
  renderComponent CLib = "lib"
  renderComponent (CSubLib x) = "sub-lib:" <> unqualCompToText x
  renderComponent (CFlib x) = "flib:" <> unqualCompToText x
  renderComponent (CExe x) = "exe:" <> unqualCompToText x
  renderComponent (CTest x) = "test:" <> unqualCompToText x
  renderComponent (CBench x) = "bench:" <> unqualCompToText x
  
  componentCachePath :: NamedComponent -> String
  componentCachePath CLib = "lib"
  componentCachePath (CSubLib x) = "sub-lib-" <> unqualCompToString x
  componentCachePath (CFlib x) = "flib-" <> unqualCompToString x
  componentCachePath (CExe x) = "exe-" <> unqualCompToString x
  componentCachePath (CTest x) = "test-" <> unqualCompToString x
  componentCachePath (CBench x) = "bench-" <> unqualCompToString x
  
  renderPkgComponents :: [(PackageName, NamedComponent)] -> Text
  renderPkgComponents = T.intercalate " " . map renderPkgComponent
  
  renderPkgComponent :: (PackageName, NamedComponent) -> Text
  renderPkgComponent (pkg, comp) =
    fromPackageName pkg <> ":" <> renderComponent comp
  
  exeComponents :: Set NamedComponent -> Set StackUnqualCompName
  exeComponents = Set.fromList . mapMaybe mExeName . Set.toList
   where
    mExeName (CExe name) = Just name
    mExeName _ = Nothing
  
  testComponents :: Set NamedComponent -> Set StackUnqualCompName
  testComponents = Set.fromList . mapMaybe mTestName . Set.toList
   where
    mTestName (CTest name) = Just name
    mTestName _ = Nothing
  
  benchComponents :: Set NamedComponent -> Set StackUnqualCompName
  benchComponents = Set.fromList . mapMaybe mBenchName . Set.toList
   where
    mBenchName (CBench name) = Just name
    mBenchName _ = Nothing
  
  subLibComponents :: Set NamedComponent -> Set StackUnqualCompName
  subLibComponents = Set.fromList . mapMaybe mSubLibName . Set.toList
   where
    mSubLibName (CSubLib name) = Just name
    mSubLibName _ = Nothing
  
  isCLib :: NamedComponent -> Bool
  isCLib CLib{} = True
  isCLib _ = False
  
  isCSubLib :: NamedComponent -> Bool
  isCSubLib CSubLib{} = True
  isCSubLib _ = False
  
  isCExe :: NamedComponent -> Bool
  isCExe CExe{} = True
  isCExe _ = False
  
  isCTest :: NamedComponent -> Bool
  isCTest CTest{} = True
  isCTest _ = False
  
  isCBench :: NamedComponent -> Bool
  isCBench CBench{} = True
  isCBench _ = False
  
  isPotentialDependency :: NamedComponent -> Bool
  isPotentialDependency v = isCLib v || isCSubLib v || isCExe v
  
  -- | A function to split the given list of components into sets of the names of
  -- the named components by the type of component (sub-libraries, executables,
  -- test-suites, benchmarks), ignoring any \'main\' unnamed library component or
  -- foreign library component. This function should be used very sparingly; more
  -- often than not, you can keep/parse the components split from the start.
  splitComponents ::
       [NamedComponent]
    -> ( Set StackUnqualCompName
         -- ^ Sub-libraries.
       , Set StackUnqualCompName
         -- ^ Executables.
       , Set StackUnqualCompName
         -- ^ Test-suites.
       , Set StackUnqualCompName
         -- ^ Benchmarks.
       )
  splitComponents =
    go id id id id
   where
    run c = Set.fromList $ c []
    go s e t b [] = (run s, run e, run t, run b)
    go s e t b (CLib : xs) = go s e t b xs
    go s e t b (CSubLib x : xs) = go (s . (x:)) e t b xs
    -- Ignore foreign libraries, for now.
    go s e t b (CFlib _ : xs) = go s e t b xs
    go s e t b (CExe x : xs) = go s (e . (x:)) t b xs
    go s e t b (CTest x : xs) = go s e (t . (x:)) b xs
    go s e t b (CBench x : xs) = go s e t (b . (x:)) xs
  