{-# LANGUAGE FlexibleInstances #-} -- | The 'Oid' type represents a Git hash value, whether partial or full. In -- general users do not interact with this type much, as all the top-level -- functions which expect hash values typically expect strings. module Data.Git.Oid ( Oid(..) , COid(..) , ObjRef(..) , Ident(..) , wrapOidPtr , compareCOid , compareCOidLen , equalCOid , stringToOid ) where import Bindings.Libgit2.Oid import Control.Exception import Control.Monad import Data.ByteString.Unsafe import Data.Git.Errors import Data.Stringable as S import Foreign.Ptr import Foreign.ForeignPtr import System.IO.Unsafe -- | 'COid' is a type wrapper for a foreign pointer to libgit2's 'git_oid' -- structure. Users should not have to deal with this type. newtype COid = COid (ForeignPtr C'git_oid) instance Show COid where show (COid x) = toString $ unsafePerformIO $ withForeignPtr x (unsafePackMallocCString <=< c'git_oid_allocfmt) -- | 'ObjRef' refers to either a Git object of a particular type (if it has -- already been loaded into memory), or it refers to the 'COid' of that -- object in the repository. This permits deferred loading of objects -- within potentially very large structures, such as trees and commits. -- However, it also means that every access to a sub-object must use -- loadObject from the type class 'Updatable'. data ObjRef a = IdRef COid | ObjRef a wrapOidPtr :: Ptr C'git_oid -> IO (ObjRef a) wrapOidPtr = newForeignPtr_ >=> return . IdRef . COid -- | An 'Ident' abstracts the fact that some objects won't have an identifier -- until they are written to disk -- even if sufficient information exists -- to determine that hash value. If construct as a 'Pending' value, it is -- an 'IO' action that writes the object to disk and retrieves the resulting -- hash value from Git; if 'Stored', it is a 'COid' that is known to the -- repository. data Ident a = Pending (a -> IO COid) | Stored COid -- | 'Oid' represents either a full or partial SHA1 hash code used to identify -- Git objects. data Oid = Oid COid | PartialOid COid Int instance Show Oid where show (Oid x) = show x show (PartialOid x l) = take l $ show x -- | Compare two 'COid' values for equality. More typical is to use 'compare'. compareCOid :: COid -> COid -> Ordering (COid x) `compareCOid` (COid y) = let c = unsafePerformIO $ withForeignPtr x $ \x' -> withForeignPtr y $ \y' -> c'git_oid_cmp x' y' in c `compare` 0 -- | Compare two 'COid' values for equality, but only up to a certain length. compareCOidLen :: COid -> COid -> Int -> Ordering compareCOidLen (COid x) (COid y) l = let c = unsafePerformIO $ withForeignPtr x $ \x' -> withForeignPtr y $ \y' -> c'git_oid_ncmp x' y' (fromIntegral l) in c `compare` 0 instance Ord COid where compare = compareCOid -- | 'True' if two 'COid' values are equal. equalCOid :: Ord a => a -> a -> Bool x `equalCOid` y = (x `compare` y) == EQ instance Eq COid where (==) = equalCOid instance Eq Oid where (Oid x) == (Oid y) = x `equalCOid` y (PartialOid x xl) == (PartialOid y yl) = xl == yl && compareCOidLen x y xl == EQ _ == _ = False -- | Convert a hash string to a 'Maybe' 'Oid' value. If the string is less -- than 40 hexadecimal digits, the result will be of type 'PartialOid'. -- -- >>> stringToOid "a143ecf" -- Just a143ecf -- >>> stringToOid "a143ecf" >>= (\(Just (PartialOid _ l)) -> return $ l == 7) -- True -- -- >>> let hash = "6cfc2ca31732fb6fa6b54bae6e586a57a0611aab" -- >>> stringToOid hash -- Just 6cfc2ca31732fb6fa6b54bae6e586a57a0611aab -- >>> stringToOid hash >>= (\(Just (Oid _)) -> return True) -- True stringToOid :: CStringable a => a -> IO (Maybe Oid) stringToOid str | len > 40 = throwIO ObjectIdTooLong | otherwise = do oid <- mallocForeignPtr withCStringable str $ \cstr -> withForeignPtr oid $ \ptr -> do r <- if len == 40 then c'git_oid_fromstr ptr cstr else c'git_oid_fromstrn ptr cstr (fromIntegral len) if r < 0 then return Nothing else return . Just $ if len == 40 then Oid (COid oid) else PartialOid (COid oid) len where len = S.length str -- Oid.hs