{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ViewPatterns #-}
module Data.Git.Ref
(
Ref(..)
, RefFile(..)
, mkRef
, readRefFile
, readPackedRefsFile
) where
import Control.Applicative
import Control.Monad.State
import Data.Attoparsec.ByteString as A
import Data.Attoparsec.ByteString.Char8 (isSpace_w8)
import Data.ByteString (ByteString)
import Data.String
import System.Posix.FilePath (RawFilePath, (</>))
import Data.Git.Internal.FileUtil
import Data.Git.Hash
import Data.Git.Internal.Parsers
import Data.Git.Paths
import Data.Git.RefName
data Ref = HEAD
| Branch RefName
| TagRef RefName (Maybe Sha1)
| RemRef RemoteName RefName
| ExpRef RefName
deriving (Eq, Ord, Show)
instance IsString Ref where
fromString s = maybe (error $ "can't parse ref: " ++ s) id $ mkRef (fromString s)
instance InRepo Ref where
inRepo HEAD = "HEAD"
inRepo (Branch b) = "refs/heads" </> getRefName b
inRepo (TagRef b _) = "refs/tags" </> getRefName b
inRepo (RemRef r b) = "refs/remotes" </> getRemoteName r </> getRefName b
inRepo (ExpRef p) = getRefName p
parseRef :: Parser Ref
parseRef = "HEAD" *> pure HEAD
<|> Branch <$> ("refs/heads/" *> parseRefName)
<|> TagRef <$> ("refs/tags/" *> parseRefName) <*> pure Nothing
<|> RemRef <$> ("refs/remotes/" *> parseRemoteName <* "/") <*> parseRefName
<|> ExpRef <$> parseRefName
data RefFile = ShaRef Sha1
| SymRef Ref
deriving (Eq, Ord, Show)
parseRefFile :: Parser RefFile
parseRefFile = SymRef <$> (string "ref: " *> parseRef <* eol)
<|> ShaRef <$> (parseSha1Hex <* eol)
parseRefName :: Parser RefName
parseRefName = do
rn <- takeTill isSpace_w8
maybe empty return $ refName rn
parseRemoteName :: Parser RemoteName
parseRemoteName = do
rn <- A.takeWhile (/= 0o57)
maybe empty return $ remoteName rn
mkRef :: ByteString -> Maybe Ref
mkRef = either (const Nothing) Just . parseOnly parseRef
maybeReadFile :: RawFilePath -> IO (Maybe ByteString)
maybeReadFile fp = mwhenFileExists fp (liftIO . readRawFileS $ fp)
parseFile :: Parser a -> RawFilePath -> IO (Maybe a)
parseFile p fp = do file <- maybeReadFile fp
return $ case file of
Nothing -> Nothing
Just f -> either (const Nothing) Just (parseOnly p f)
readRefFile :: RawFilePath -> IO (Maybe RefFile)
readRefFile = parseFile parseRefFile
parsePackedRef :: Parser (Ref, Sha1)
parsePackedRef = do
sha <- parseSha1Hex
void space
ref <- parseRef
eol
case ref of
TagRef t _ -> (,sha) . TagRef t <$> optional ("^" *> parseSha1Hex <* eol)
_ -> return (ref, sha)
parsePackedRefs :: Parser [(Ref, Sha1)]
parsePackedRefs = endOfInput *> pure []
<|> (:) <$> parsePackedRef <*> parsePackedRefs
<|> skipLine *> parsePackedRefs
readPackedRefsFile :: RawFilePath -> IO (Maybe [(Ref, Sha1)])
readPackedRefsFile = parseFile parsePackedRefs