content: {-# LANGUAGE DuplicateRecordFields #-}
  {-# LANGUAGE GADTs #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE TypeOperators #-}
  {-# LANGUAGE UndecidableInstances #-}
  module Semantic.IO
    ( findFilesInDir
    ) where
  
  import Prelude hiding (readFile)
  
  import           Control.Monad.IO.Class
  import           System.Directory.Tree (AnchoredDirTree (..))
  import qualified System.Directory.Tree as Tree
  import           System.FilePath
  
  pathIsMinified :: FilePath -> Bool
  pathIsMinified = isExtensionOf ".min.js"
  
  -- Recursively find files in a directory.
  findFilesInDir :: MonadIO m => FilePath -> [String] -> [FilePath] -> m [FilePath]
  findFilesInDir path exts excludeDirs = do
    _:/dir <- liftIO $ Tree.build path
    pure $ (onlyFiles . Tree.filterDir (withExtensions exts) . Tree.filterDir (notIn excludeDirs)) dir
    where
      -- Build a list of only FilePath's (remove directories and failures)
      onlyFiles (Tree.Dir _ fs)   = concatMap onlyFiles fs
      onlyFiles (Tree.Failed _ _) = []
      onlyFiles (Tree.File _ f)   = [f]
  
      -- Predicate for Files with one of the extensions in 'exts'.
      withExtensions exts (Tree.File n _)
        | pathIsMinified n            = False
        | takeExtension n `elem` exts = True
        | otherwise                   = False
      withExtensions _ _              = True
  
      -- Predicate for contents NOT in a directory
      notIn dirs (Tree.Dir n _)
        | (x:_) <- n, x == '.' = False -- Don't include directories that start with '.'.
        | n `elem` dirs = False
        | otherwise = True
      notIn _ _ = True
  