packages feed

git-phoenix-0.0.1: src/Data/Git/Phoenix/ShaCollision.hs

module Data.Git.Phoenix.ShaCollision where

import Data.List.NonEmpty (groupWith)
import Data.Binary qualified as B
import Data.ByteString.Lazy qualified as L
import Data.ByteString.Lazy.Char8 qualified as L8
import Data.Git.Phoenix.App
import Data.Git.Phoenix.Io
import Data.Git.Phoenix.Object
import Data.Git.Phoenix.Prelude


disambiguateByPair :: PhoenixM m => GitObjType -> [FilePath] -> m [FilePath]
disambiguateByPair tt links =
  fmap (snd . head) . groupWith fst . sort . catMaybes <$> mapM go links
  where
    go l = do
      withCompressed l $ \bs -> do
        case classifyGitObject bs of
          Just x | x == tt -> pure $ Just (bs, l)
                 | otherwise -> pure Nothing
          Nothing -> pure Nothing

uniqBs :: PhoenixExtractM m =>
  GitPath x ->
  Tagged Compressed LByteString ->
  GitObjType ->
  m FilePath
uniqBs ambiHash cbs expectedGitObjType = do
  case parseFileLinks cbs of
    [_] -> fail $ show cbs <> " is not ambiguous"
    [] -> fail $ show cbs <> " is emply list"
    links -> do
      disLinks <- disambiguateByPair expectedGitObjType $ fmap L8.unpack links
      case disLinks of
        [] -> fail $ show cbs <> " is emply list after dis"
        [a] -> pure a
        uniqLinks -> chooseOneLink uniqLinks
  where
    chooseOneLink links = do
      forM_ (zip [0 :: Int ..] links) $ \(i, l) -> putStrLn $ printf "%4d) %s" i l
      putStrLn "-----------------------------------------------------------"
      putStrLn $ "Enter link number to disambiguate SHA " <> toFp ambiHash <> " of " <> show expectedGitObjType
      i <- readNumber 0 (length links - 1)
      case links !? i of
        Nothing -> fail $ "Link index out of range: " <> show i <> " for " <> show (length links)
        Just l -> pure l

parseFileLinks :: Tagged Compressed LByteString -> [LByteString]
parseFileLinks (Tagged preCbs) = go cbs
  where
    go bs =
      case L.splitAt encodedIntLen bs of
        ("", _) -> []
        (binLen, bs') ->
          case B.decodeOrFail binLen of
            Right (_, _, fsLinkLen) ->
              case L.splitAt fsLinkLen bs' of
                (fsLinkBs, bs'')
                  | L.length fsLinkBs == fsLinkLen ->
                    fsLinkBs : go bs''
                  | otherwise ->
                      error $ "Expected link len " <> show fsLinkLen
                        <> " but got " <> show (L.length fsLinkBs)
            Left e ->
              error $ "List of files with collided SHA is corrupted (error: "
                <> show e <> ") near: "  <> show bs

    cbs = L.drop (L.length compressedDisambiguate) preCbs