module Checkmate.Discover ( FileDeltas , discover , discoverDirectory , discoverFile , parseDiff ) where import Control.Monad.Parallel as P import Data.Range.Range as Range import Data.Set as S import Data.Text import System.Directory import System.FilePath import Text.Diff.Parse (parseDiff) import Text.Diff.Parse.Types import Checkmate.Check import Checkmate.Diff import Checkmate.Parser.CheckFile import Checkmate.Parser.IndentBlock checkFileNames :: Set FilePath checkFileNames = fromList -- CHECK: All checklist filenames that Checkmate recognize should be -- written in the project docs (i.e. README.md). [ "CHECK" , ".check" ] discover :: FilePath -> FileDeltas -> IO Checklist discover baseDirPath deltas = do checklists <- P.sequence $ fmap (discoverFile baseDirPath) deltas dirChecklists <- forM deltas $ \ FileDelta { fileDeltaDestFile = p } -> do let dirPath' = takeDirectory $ unpack p discoverDirectory baseDirPath dirPath' return . unions $ checklists ++ dirChecklists discoverDirectory :: FilePath -> FilePath -> IO Checklist discoverDirectory baseDirPath dirPath = fmap unions $ forM checkFileNames' $ \ checkFileName -> do let checkFilePath = dirPath' checkFileName dirExist <- doesDirectoryExist dirPath' if not dirExist then return S.empty else do checkFileExist <- doesFileExist checkFilePath checklist <- if checkFileExist then do result <- parseCheckFile checkFilePath return $ case result of Left _ -> S.empty Right checklist -> checklist else return S.empty parentChecklist <- if dirPath == "." || dirPath == "" || parent == dirPath then return S.empty else discoverDirectory baseDirPath parent return $ S.union checklist parentChecklist where checkFileNames' :: [FilePath] checkFileNames' = toList checkFileNames dirPath' :: FilePath dirPath' = baseDirPath dirPath parent :: FilePath parent = takeDirectory dirPath discoverFile :: FilePath -> FileDelta -> IO Checklist discoverFile _ FileDelta { fileDeltaContent = Binary } = return S.empty discoverFile baseDirPath FileDelta { fileDeltaContent = Hunks hunks , fileDeltaDestFile = filePathT } = do result <- parseSourceFile filePath return $ case result of Left _ -> S.empty Right checklist -> fromList [ c | c@Check { checkScope = FileBlock _ range } <- toList checklist , Prelude.any (rangesOverlap range) ranges ] where filePath :: FilePath filePath = baseDirPath unpack filePathT ranges :: [Range.Range Int] ranges = [rangeFromDiffRange r | Hunk { hunkDestRange = r } <- hunks]