content: module Unused.Cache.DirectoryFingerprint
      ( FingerprintOutcome(..)
      , sha
      ) where
  
  import Control.Monad.Reader (ReaderT, asks, liftIO, runReaderT)
  import qualified Data.Char as C
  import qualified Data.Maybe as M
  import qualified System.Directory as D
  import qualified System.Process as P
  import Unused.Cache.FindArgsFromIgnoredPaths (findArgs)
  import Unused.Util (safeHead, safeReadFile)
  
  newtype MD5ExecutablePath = MD5ExecutablePath
      { toMD5String :: String
      }
  
  type MD5Config = ReaderT MD5ExecutablePath IO
  
  newtype FingerprintOutcome =
      MD5ExecutableNotFound [String]
  
  sha :: IO (Either FingerprintOutcome String)
  sha = do
      md5Executable' <- md5Executable
      case md5Executable' of
          Just exec ->
              Right . getSha <$>
              runReaderT (fileList >>= sortInput >>= md5Result) (MD5ExecutablePath exec)
          Nothing -> return $ Left $ MD5ExecutableNotFound supportedMD5Executables
    where
      getSha = takeWhile C.isAlphaNum . M.fromMaybe "" . safeHead . lines
  
  fileList :: MD5Config String
  fileList = do
      filterNamePathArgs <- liftIO $ findArgs <$> ignoredPaths
      md5exec <- asks toMD5String
      let args =
              [".", "-type", "f", "-not", "-path", "*/.git/*"] ++
              filterNamePathArgs ++ ["-exec", md5exec, "{}", "+"]
      liftIO $ P.readProcess "find" args ""
  
  sortInput :: String -> MD5Config String
  sortInput = liftIO . P.readProcess "sort" ["-k", "2"]
  
  md5Result :: String -> MD5Config String
  md5Result r = do
      md5exec <- asks toMD5String
      liftIO $ P.readProcess md5exec [] r
  
  ignoredPaths :: IO [String]
  ignoredPaths = either (const []) id <$> (fmap lines <$> safeReadFile ".gitignore")
  
  md5Executable :: IO (Maybe String)
  md5Executable = safeHead . concat <$> mapM D.findExecutables supportedMD5Executables
  
  supportedMD5Executables :: [String]
  supportedMD5Executables = ["md5", "md5sum"]
  