{-# LANGUAGE DataKinds #-}

-- | Finding files.
-- Lifted from Stack.
module Path.Find
  ( findFileUp
  ) where

import Control.Monad.Catch
import Control.Monad.IO.Class
import Data.List
import Path
import Path.IO

-- | Find the location of a file matching the given predicate.
findFileUp ::
     (MonadIO m, MonadThrow m)
  => Path Abs Dir -- ^ Start here.
  -> (Path Abs File -> Bool) -- ^ Predicate to match the file.
  -> Maybe (Path Abs Dir) -- ^ Do not ascend above this directory.
  -> m (Maybe (Path Abs File)) -- ^ Absolute file path.
findFileUp = findPathUp snd

-- | Find the location of a path matching the given predicate.
findPathUp ::
     (MonadIO m, MonadThrow m)
  => (([Path Abs Dir], [Path Abs File]) -> [Path Abs t])
              -- ^ Choose path type from pair.
  -> Path Abs Dir -- ^ Start here.
  -> (Path Abs t -> Bool) -- ^ Predicate to match the path.
  -> Maybe (Path Abs Dir) -- ^ Do not ascend above this directory.
  -> m (Maybe (Path Abs t)) -- ^ Absolute path.
findPathUp pathType dir p upperBound = do
  entries <- listDir dir
  case find p (pathType entries) of
    Just path -> return (Just path)
    Nothing
      | Just dir == upperBound -> return Nothing
      | parent dir == dir -> return Nothing
      | otherwise -> findPathUp pathType (parent dir) p upperBound