module Git.Object where

import Conduit
import Control.Monad
import Data.Function
import Data.Maybe
import Git.Types
import Prelude hiding (FilePath)

listObjects :: MonadGit r m
            => Maybe (CommitOid r) -- ^ A commit we may already have
            -> CommitOid r         -- ^ The commit we need
            -> Bool                -- ^ Include commit trees also?
            -> m [ObjectOid r]     -- ^ All the objects in between
listObjects mhave need alsoTrees =
    runConduit $ sourceObjects mhave need alsoTrees .| sinkList

traverseObjects :: MonadGit r m => (ObjectOid r -> m a) -> CommitOid r -> m [a]
traverseObjects f need = mapM f =<< listObjects Nothing need False

traverseObjects_ :: MonadGit r m => (ObjectOid r -> m ()) -> CommitOid r -> m ()
traverseObjects_ = (void .) . traverseObjects

-- | Given a list of objects (commit and top-level trees) return by
--   'listObjects', expand it to include all subtrees and blobs as well.
--   Ordering is preserved.
expandTreeObjects :: MonadGit r m => ConduitT (ObjectOid r) (ObjectOid r) m ()
expandTreeObjects = awaitForever $ \obj -> case obj of
    TreeObjOid toid -> do
        yield $ TreeObjOid toid
        tr <- lift $ lookupTree toid
        sourceTreeEntries tr
            .| awaitForever (\ent -> case ent of
                (_, BlobEntry oid _) -> yield $ BlobObjOid oid
                (_, TreeEntry oid)   -> yield $ TreeObjOid oid
                _ -> return ())
    _ -> yield obj

listAllObjects :: MonadGit r m
               => Maybe (CommitOid r) -> CommitOid r -> m [ObjectOid r]
listAllObjects mhave need =
    runConduit $ sourceObjects mhave need True .| expandTreeObjects .| sinkList
