content: {-# LANGUAGE DeriveDataTypeable #-}
  {-# LANGUAGE DeriveGeneric #-}
  
  -----------------------------------------------------------------------------
  -- |
  -- Module      :  Distribution.ModuleName
  -- Copyright   :  Duncan Coutts 2008
  -- License     :  BSD3
  --
  -- Maintainer  :  cabal-devel@haskell.org
  -- Portability :  portable
  --
  -- Data type for Haskell module names.
  
  module Distribution.ModuleName (
          ModuleName (..), -- TODO: move Parsec instance here, don't export constructor
          fromString,
          fromComponents,
          components,
          toFilePath,
          main,
          simple,
          -- * Internal
          validModuleComponent,
    ) where
  
  import Prelude ()
  import Distribution.Compat.Prelude
  
  import Distribution.Utils.ShortText
  import Distribution.Text
  import qualified Distribution.Compat.ReadP as Parse
  
  import qualified Text.PrettyPrint as Disp
  import System.FilePath ( pathSeparator )
  
  -- | A valid Haskell module name.
  --
  newtype ModuleName = ModuleName ShortTextLst
    deriving (Eq, Generic, Ord, Read, Show, Typeable, Data)
  
  instance Binary ModuleName
  
  instance NFData ModuleName where
      rnf (ModuleName ms) = rnf ms
  
  instance Text ModuleName where
    disp (ModuleName ms) =
      Disp.hcat (intersperse (Disp.char '.') (map Disp.text $ stlToStrings ms))
  
    parse = do
      ms <- Parse.sepBy1 component (Parse.char '.')
      return (ModuleName $ stlFromStrings ms)
  
      where
        component = do
          c  <- Parse.satisfy isUpper
          cs <- Parse.munch validModuleChar
          return (c:cs)
  
  validModuleChar :: Char -> Bool
  validModuleChar c = isAlphaNum c || c == '_' || c == '\''
  
  validModuleComponent :: String -> Bool
  validModuleComponent []     = False
  validModuleComponent (c:cs) = isUpper c
                             && all validModuleChar cs
  
  {-# DEPRECATED simple "use ModuleName.fromString instead" #-}
  simple :: String -> ModuleName
  simple str = ModuleName (stlFromStrings [str])
  
  -- | Construct a 'ModuleName' from a valid module name 'String'.
  --
  -- This is just a convenience function intended for valid module strings. It is
  -- an error if it is used with a string that is not a valid module name. If you
  -- are parsing user input then use 'Distribution.Text.simpleParse' instead.
  --
  instance IsString ModuleName where
      fromString string = fromComponents (split string)
        where
          split cs = case break (=='.') cs of
            (chunk,[])     -> chunk : []
            (chunk,_:rest) -> chunk : split rest
  
  -- | Construct a 'ModuleName' from valid module components, i.e. parts
  -- separated by dots.
  fromComponents :: [String] -> ModuleName
  fromComponents components'
      | null components'                     = error zeroComponents
      | all validModuleComponent components' = ModuleName (stlFromStrings components')
      | otherwise                            = error badName
    where
      zeroComponents = "ModuleName.fromComponents: zero components"
      badName        = "ModuleName.fromComponents: invalid components " ++ show components'
  
  -- | The module name @Main@.
  --
  main :: ModuleName
  main = ModuleName (stlFromStrings ["Main"])
  
  -- | The individual components of a hierarchical module name. For example
  --
  -- > components (fromString "A.B.C") = ["A", "B", "C"]
  --
  components :: ModuleName -> [String]
  components (ModuleName ms) = stlToStrings ms
  
  -- | Convert a module name to a file path, but without any file extension.
  -- For example:
  --
  -- > toFilePath (fromString "A.B.C") = "A/B/C"
  --
  toFilePath :: ModuleName -> FilePath
  toFilePath = intercalate [pathSeparator] . components
  
  ----------------------------------------------------------------------------
  -- internal helper
  
  -- | Strict/unpacked representation of @[ShortText]@
  data ShortTextLst = STLNil
                    | STLCons !ShortText !ShortTextLst
                    deriving (Eq, Generic, Ord, Typeable, Data)
  
  instance NFData ShortTextLst where
      rnf = flip seq ()
  
  instance Show ShortTextLst where
      showsPrec p = showsPrec p . stlToList
  
  
  instance Read ShortTextLst where
      readsPrec p = map (first stlFromList) . readsPrec p
  
  instance Binary ShortTextLst where
      put = put . stlToList
      get = stlFromList <$> get
  
  stlToList :: ShortTextLst -> [ShortText]
  stlToList STLNil = []
  stlToList (STLCons st next) = st : stlToList next
  
  stlToStrings :: ShortTextLst -> [String]
  stlToStrings = map fromShortText . stlToList
  
  stlFromList :: [ShortText] -> ShortTextLst
  stlFromList [] = STLNil
  stlFromList (x:xs) = STLCons x (stlFromList xs)
  
  stlFromStrings :: [String] -> ShortTextLst
  stlFromStrings = stlFromList . map toShortText
  