{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} module Distribution.Types.SourceRepo ( SourceRepo(..), RepoKind(..), RepoType(..), KnownRepoType (..), knownRepoTypes, emptySourceRepo, classifyRepoType, classifyRepoKind, ) where import Prelude () import Distribution.Compat.Prelude import Distribution.Utils.Generic (lowercase) import Distribution.Pretty import Distribution.Parsec import qualified Distribution.Compat.CharParsing as P import qualified Text.PrettyPrint as Disp import qualified Data.Map.Strict as M -- ------------------------------------------------------------ -- * Source repos -- ------------------------------------------------------------ -- | Information about the source revision control system for a package. -- -- When specifying a repo it is useful to know the meaning or intention of the -- information as doing so enables automation. There are two obvious common -- purposes: one is to find the repo for the latest development version, the -- other is to find the repo for this specific release. The 'ReopKind' -- specifies which one we mean (or another custom one). -- -- A package can specify one or the other kind or both. Most will specify just -- a head repo but some may want to specify a repo to reconstruct the sources -- for this package release. -- -- The required information is the 'RepoType' which tells us if it's using -- 'Darcs', 'Git' for example. The 'repoLocation' and other details are -- interpreted according to the repo type. -- data SourceRepo = SourceRepo { -- | The kind of repo. This field is required. repoKind :: RepoKind, -- | The type of the source repository system for this repo, eg 'Darcs' or -- 'Git'. This field is required. repoType :: Maybe RepoType, -- | The location of the repository. For most 'RepoType's this is a URL. -- This field is required. repoLocation :: Maybe String, -- | 'CVS' can put multiple \"modules\" on one server and requires a -- module name in addition to the location to identify a particular repo. -- Logically this is part of the location but unfortunately has to be -- specified separately. This field is required for the 'CVS' 'RepoType' and -- should not be given otherwise. repoModule :: Maybe String, -- | The name or identifier of the branch, if any. Many source control -- systems have the notion of multiple branches in a repo that exist in the -- same location. For example 'Git' and 'CVS' use this while systems like -- 'Darcs' use different locations for different branches. This field is -- optional but should be used if necessary to identify the sources, -- especially for the 'RepoThis' repo kind. repoBranch :: Maybe String, -- | The tag identify a particular state of the repository. This should be -- given for the 'RepoThis' repo kind and not for 'RepoHead' kind. -- repoTag :: Maybe String, -- | Some repositories contain multiple projects in different subdirectories -- This field specifies the subdirectory where this packages sources can be -- found, eg the subdirectory containing the @.cabal@ file. It is interpreted -- relative to the root of the repository. This field is optional. If not -- given the default is \".\" ie no subdirectory. repoSubdir :: Maybe FilePath } deriving (Eq, Ord, Generic, Read, Show, Typeable, Data) emptySourceRepo :: RepoKind -> SourceRepo emptySourceRepo kind = SourceRepo { repoKind = kind , repoType = Nothing , repoLocation = Nothing , repoModule = Nothing , repoBranch = Nothing , repoTag = Nothing , repoSubdir = Nothing } instance Binary SourceRepo instance Structured SourceRepo instance NFData SourceRepo where rnf = genericRnf -- | What this repo info is for, what it represents. -- data RepoKind = -- | The repository for the \"head\" or development version of the project. -- This repo is where we should track the latest development activity or -- the usual repo people should get to contribute patches. RepoHead -- | The repository containing the sources for this exact package version -- or release. For this kind of repo a tag should be given to give enough -- information to re-create the exact sources. | RepoThis | RepoKindUnknown String deriving (Eq, Generic, Ord, Read, Show, Typeable, Data) instance Binary RepoKind instance Structured RepoKind instance NFData RepoKind where rnf = genericRnf -- | An enumeration of common source control systems. The fields used in the -- 'SourceRepo' depend on the type of repo. The tools and methods used to -- obtain and track the repo depend on the repo type. -- data KnownRepoType = Darcs | Git | SVN | CVS | Mercurial | GnuArch | Bazaar | Monotone | Pijul -- ^ @since 3.4.0.0 deriving (Eq, Generic, Ord, Read, Show, Typeable, Data, Enum, Bounded) instance Binary KnownRepoType instance Structured KnownRepoType instance NFData KnownRepoType where rnf = genericRnf instance Parsec KnownRepoType where parsec = do str <- P.munch1 isIdent maybe (P.unexpected $ "Could not parse KnownRepoType from " ++ str) return (M.lookup str knownRepoTypeMap) instance Pretty KnownRepoType where pretty = Disp.text . lowercase . show data RepoType = KnownRepoType KnownRepoType | OtherRepoType String deriving (Eq, Generic, Ord, Read, Show, Typeable, Data) instance Binary RepoType instance Structured RepoType instance NFData RepoType where rnf = genericRnf knownRepoTypes :: [KnownRepoType] knownRepoTypes = [minBound .. maxBound] repoTypeAliases :: KnownRepoType -> [String] repoTypeAliases Bazaar = ["bzr"] repoTypeAliases Mercurial = ["hg"] repoTypeAliases GnuArch = ["arch"] repoTypeAliases _ = [] instance Pretty RepoKind where pretty RepoHead = Disp.text "head" pretty RepoThis = Disp.text "this" pretty (RepoKindUnknown other) = Disp.text other instance Parsec RepoKind where parsec = classifyRepoKind <$> P.munch1 isIdent classifyRepoKind :: String -> RepoKind classifyRepoKind name = case lowercase name of "head" -> RepoHead "this" -> RepoThis _ -> RepoKindUnknown name instance Parsec RepoType where parsec = classifyRepoType <$> P.munch1 isIdent instance Pretty RepoType where pretty (OtherRepoType other) = Disp.text other pretty (KnownRepoType t) = pretty t classifyRepoType :: String -> RepoType classifyRepoType s = maybe (OtherRepoType s) KnownRepoType (M.lookup (lowercase s) knownRepoTypeMap) knownRepoTypeMap :: Map String KnownRepoType knownRepoTypeMap = M.fromList [ (name, repoType') | repoType' <- knownRepoTypes , name <- prettyShow repoType' : repoTypeAliases repoType' ] isIdent :: Char -> Bool isIdent c = isAlphaNum c || c == '_' || c == '-'