{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Distribution.Types.UnqualComponentName
  ( UnqualComponentName
  , unUnqualComponentName
  , unUnqualComponentNameST
  , mkUnqualComponentName
  , packageNameToUnqualComponentName
  , unqualComponentNameToPackageName
  , combineNames
  ) where

import Distribution.Compat.Prelude
import Distribution.Utils.ShortText

import Distribution.Parsec
import Distribution.Pretty
import Distribution.Types.PackageName

-- | An unqualified component name, for any kind of component.
--
-- This is distinguished from a 'ComponentName' and 'ComponentId'. The former
-- also states which of a library, executable, etc the name refers too. The
-- later uniquely identifiers a component and its closure.
--
-- @since 2.0.0.2
newtype UnqualComponentName = UnqualComponentName ShortText
  deriving
    ( Generic
    , Read
    , Show
    , Eq
    , Ord
    , Typeable
    , Data
    , Semigroup
    , Monoid -- TODO: bad enabler of bad monoids
    )

-- | Convert 'UnqualComponentName' to 'String'
--
-- @since 2.0.0.2
unUnqualComponentName :: UnqualComponentName -> String
unUnqualComponentName (UnqualComponentName s) = fromShortText s

-- | @since 3.4.0.0
unUnqualComponentNameST :: UnqualComponentName -> ShortText
unUnqualComponentNameST (UnqualComponentName s) = s

-- | Construct a 'UnqualComponentName' from a 'String'
--
-- 'mkUnqualComponentName' is the inverse to 'unUnqualComponentName'
--
-- Note: No validations are performed to ensure that the resulting
-- 'UnqualComponentName' is valid
--
-- @since 2.0.0.2
mkUnqualComponentName :: String -> UnqualComponentName
mkUnqualComponentName = UnqualComponentName . toShortText

-- | 'mkUnqualComponentName'
--
-- @since 2.0.0.2
instance IsString UnqualComponentName where
  fromString = mkUnqualComponentName

instance Binary UnqualComponentName
instance Structured UnqualComponentName

instance Pretty UnqualComponentName where
  pretty = showToken . unUnqualComponentName

instance Parsec UnqualComponentName where
  parsec = mkUnqualComponentName <$> parsecUnqualComponentName

instance NFData UnqualComponentName where
  rnf (UnqualComponentName pkg) = rnf pkg

-- TODO avoid String round trip with these PackageName <->
-- UnqualComponentName converters.

-- | Converts a package name to an unqualified component name
--
-- Useful in legacy situations where a package name may refer to an internal
-- component, if one is defined with that name.
--
-- 2018-12-21: These "legacy" situations are not legacy.
-- We can @build-depends@ on the internal library. However
-- Now dependency contains @Set LibraryName@, and we should use that.
--
-- @since 2.0.0.2
packageNameToUnqualComponentName :: PackageName -> UnqualComponentName
packageNameToUnqualComponentName = UnqualComponentName . unPackageNameST

-- | Converts an unqualified component name to a package name
--
-- `packageNameToUnqualComponentName` is the inverse of
-- `unqualComponentNameToPackageName`.
--
-- Useful in legacy situations where a package name may refer to an internal
-- component, if one is defined with that name.
--
-- @since 2.0.0.2
unqualComponentNameToPackageName :: UnqualComponentName -> PackageName
unqualComponentNameToPackageName = mkPackageNameST . unUnqualComponentNameST

-- | Combine names in targets if one name is empty or both names are equal
-- (partial function).
-- Useful in 'Semigroup' and similar instances.
combineNames
  :: (Monoid b, Eq b, Show b)
  => a
  -> a
  -> (a -> b)
  -> String
  -> b
combineNames a b tacc tt
  -- One empty or the same.
  | nb == mempty
      || na == nb =
      na
  | na == mempty =
      nb
  -- Both non-empty, different.
  | otherwise =
      error $
        "Ambiguous values for "
          ++ tt
          ++ " field: '"
          ++ show na
          ++ "' and '"
          ++ show nb
          ++ "'"
  where
    (na, nb) = (tacc a, tacc b)
{-# INLINEABLE combineNames #-}
