-- | -- Module : Data.Git.Object -- License : BSD-style -- Maintainer : Vincent Hanquez -- Stability : experimental -- Portability : unix -- {-# LANGUAGE DeriveDataTypeable #-} module Data.Git.Types ( -- * Type of types ObjectType(..) -- * Main git types , Tree(..) , Commit(..) , CommitExtra(..) , Blob(..) , Tag(..) , Person(..) , EntName , entName , getEntNameBytes , EntPath , entPathAppend -- * modeperm type , ModePerm(..) , FilePermissions(..) , ObjectFileType(..) , getPermission , getFiletype -- * time type , GitTime(..) , gitTime , gitTimeToLocal -- * Pack delta types , DeltaOfs(..) , DeltaRef(..) -- * Basic types part of other bigger types , TreeEnt ) where import Data.Word import Data.Bits import Data.String import Data.ByteString (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L import Data.Git.Ref import Data.Git.Delta import Data.Git.Imports import Data.Hourglass (Elapsed, TimezoneOffset(..) , timePrint, timeConvert , Time(..), Timeable(..) , LocalTime, localTimeSetTimezone, localTimeFromGlobal) import Data.Data import qualified Data.ByteString.UTF8 as UTF8 -- | type of a git object. data ObjectType = TypeTree | TypeBlob | TypeCommit | TypeTag | TypeDeltaOff | TypeDeltaRef deriving (Show,Eq,Data,Typeable) -- | Git time is number of seconds since unix epoch in the UTC zone with -- the current timezone associated data GitTime = GitTime { gitTimeUTC :: !Elapsed , gitTimeTimezone :: !TimezoneOffset } deriving (Eq) instance Timeable GitTime where timeGetNanoSeconds _ = 0 timeGetElapsedP (GitTime t _) = timeConvert t timeGetElapsed (GitTime t _) = t instance Time GitTime where timeFromElapsedP e = GitTime (timeGetElapsed e) (TimezoneOffset 0) timeFromElapsed e = GitTime e (TimezoneOffset 0) instance Show GitTime where show (GitTime t tz) = timePrint "EPOCH" t ++ " " ++ show tz gitTime :: Integer -> Int -> GitTime gitTime seconds tzMins = GitTime (fromIntegral seconds) (TimezoneOffset tzMins) gitTimeToLocal :: GitTime -> LocalTime Elapsed gitTimeToLocal (GitTime t tz) = localTimeSetTimezone tz (localTimeFromGlobal t) -- | the enum instance is useful when marshalling to pack file. instance Enum ObjectType where fromEnum TypeCommit = 0x1 fromEnum TypeTree = 0x2 fromEnum TypeBlob = 0x3 fromEnum TypeTag = 0x4 fromEnum TypeDeltaOff = 0x6 fromEnum TypeDeltaRef = 0x7 toEnum 0x1 = TypeCommit toEnum 0x2 = TypeTree toEnum 0x3 = TypeBlob toEnum 0x4 = TypeTag toEnum 0x6 = TypeDeltaOff toEnum 0x7 = TypeDeltaRef toEnum n = error ("not a valid object: " ++ show n) newtype ModePerm = ModePerm Word32 deriving (Show,Eq) getPermission :: ModePerm -> FilePermissions getPermission (ModePerm modeperm) = let owner = (modeperm .&. 0x700) `shiftR` 6 group = (modeperm .&. 0x70) `shiftR` 3 other = modeperm .&. 0x7 in FilePermissions (fromIntegral owner) (fromIntegral group) (fromIntegral other) getFiletype :: ModePerm -> ObjectFileType getFiletype (ModePerm modeperm) = case modeperm `shiftR` 12 of _ -> error "filetype unknown" -- | Git object file type data ObjectFileType = FileTypeDirectory | FileTypeRegularFile | FileTypeSymbolicLink | FileTypeGitLink deriving (Show,Eq) -- | traditional unix permission for owner, group and permissions data FilePermissions = FilePermissions { getOwnerPerm :: {-# UNPACK #-} !Perm , getGroupPerm :: {-# UNPACK #-} !Perm , getOtherPerm :: {-# UNPACK #-} !Perm } deriving (Show,Eq) -- | a bitfield representing a typical unix permission: -- * bit 0 represents the read permission -- * bit 1 represents the write permission -- * bit 2 represents the execute permission type Perm = Word8 -- | Entity name newtype EntName = EntName { getEntNameBytes :: ByteString } deriving (Eq,Ord) instance Show EntName where show (EntName e) = UTF8.toString e instance IsString EntName where fromString s = entName $ UTF8.fromString s entName :: ByteString -> EntName entName bs | B.elem slash bs = error ("entity name " ++ show bs ++ " contains an invalid '/' character") | otherwise = EntName bs where slash = 47 entPathAppend :: EntPath -> EntName -> EntPath entPathAppend l e = l ++ [e] type EntPath = [EntName] -- | represent one entry in the tree -- (permission,file or directory name,blob or tree ref) -- name should maybe a filepath, but not sure about the encoding. type TreeEnt hash = (ModePerm,EntName,Ref hash) -- | an author or committer line -- has the format: name time timezone -- FIXME: should be a string, but I don't know if the data is stored -- consistantly in one encoding (UTF8) data Person = Person { personName :: !ByteString , personEmail :: !ByteString , personTime :: !GitTime } deriving (Show,Eq) -- | Represent a root tree with zero to many tree entries. newtype Tree hash = Tree { treeGetEnts :: [TreeEnt hash] } deriving (Show,Eq) -- | Represent a binary blob. newtype Blob hash = Blob { blobGetContent :: L.ByteString } deriving (Show,Eq) -- | Represent a commit object. data Commit hash = Commit { commitTreeish :: !(Ref hash) , commitParents :: [Ref hash] , commitAuthor :: !Person , commitCommitter :: !Person , commitEncoding :: Maybe ByteString , commitExtras :: [CommitExtra] , commitMessage :: !ByteString } deriving (Show,Eq) data CommitExtra = CommitExtra { commitExtraKey :: !ByteString , commitExtraValue :: !ByteString } deriving (Show,Eq) -- | Represent a signed tag. data Tag hash = Tag { tagRef :: !(Ref hash) , tagObjectType :: !ObjectType , tagBlob :: !ByteString , tagName :: !Person , tagS :: !ByteString } deriving (Show,Eq) -- | Delta pointing to an offset. data DeltaOfs hash = DeltaOfs !Word64 !Delta deriving (Show,Eq) -- | Delta pointing to a ref. data DeltaRef hash = DeltaRef !(Ref hash) !Delta deriving (Show,Eq)