Distribution/Types/PackageId.hs

    {-# LANGUAGE DeriveDataTypeable         #-}
    {-# LANGUAGE DeriveGeneric              #-}
    module Distribution.Types.PackageId
      ( PackageIdentifier(..)
      , PackageId
      ) where

    import Distribution.Compat.Prelude
    import Prelude ()

    import Distribution.Parsec      (Parsec (..), simpleParsec)
    import Distribution.Pretty
    import Distribution.Types.PackageName
    import Distribution.Version           (Version, nullVersion)

    import qualified Distribution.Compat.CharParsing as P
    import qualified Text.PrettyPrint                as Disp

    -- | Type alias so we can use the shorter name PackageId.
    type PackageId = PackageIdentifier

    -- | The name and version of a package.
    data PackageIdentifier
        = PackageIdentifier {
            pkgName    :: PackageName, -- ^The name of this package, eg. foo
            pkgVersion :: Version -- ^the version of this package, eg 1.2
         }
         deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)

    instance Binary PackageIdentifier

    instance Pretty PackageIdentifier where
      pretty (PackageIdentifier n v)
        | v == nullVersion = pretty n -- if no version, don't show version.
        | otherwise        = pretty n <<>> Disp.char '-' <<>> pretty v

    -- |
    --
    -- >>> simpleParsec "foo-bar-0" :: Maybe PackageIdentifier
    -- Just (PackageIdentifier {pkgName = PackageName "foo-bar", pkgVersion = mkVersion [0]})
    --
    -- >>> simpleParsec "foo-bar" :: Maybe PackageIdentifier
    -- Just (PackageIdentifier {pkgName = PackageName "foo-bar", pkgVersion = mkVersion []})
    --
    -- /Note:/ Stricter than 'Text' instance
    --
    -- >>> simpleParsec "foo-bar-0-0" :: Maybe PackageIdentifier
    -- Nothing
    --
    -- >>> simpleParsec "foo-bar.0" :: Maybe PackageIdentifier
    -- Nothing
    --
    -- >>> simpleParsec "foo-bar.4-2" :: Maybe PackageIdentifier
    -- Nothing
    --
    -- >>> simpleParsec "1.2.3" :: Maybe PackageIdentifier
    -- Nothing
    --
    instance Parsec PackageIdentifier where
      parsec = do
          xs' <- P.sepBy1 component (P.char '-')
          (v, xs) <- case simpleParsec (last xs') of
              Nothing -> return (nullVersion, xs') -- all components are version
              Just v  -> return (v, init xs')
          if not (null xs) && all (\c ->  all (/= '.') c && not (all isDigit c)) xs
          then return $ PackageIdentifier (mkPackageName (intercalate  "-" xs)) v
          else fail "all digits or a dot in a portion of package name"
        where
          component = P.munch1 (\c ->  isAlphaNum c || c == '.')

    instance NFData PackageIdentifier where
        rnf (PackageIdentifier name version) = rnf name `seq` rnf version