haskdeep-0.2.0.1: src/HaskDeep/Computation.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DoAndIfThenElse #-}
-- |
-- Module : HaskDeep.Computation
-- Copyright : Mauro Taraborelli 2012
-- License : BSD3
--
-- Maintainer : maurotaraborelli@gmail.com
-- Stability : experimental
-- Portability : unknown
--
-- Computes hashes traversing recursively through a directory structure.
-- Uses a list of known hashes to audit a set of files.
--
-- Internal module.
module HaskDeep.Computation
(
-- * Computation
compute -- Hash ctx a => HaskDeepConfiguration -> ComputationMode a -> IO HashSet
)
where
import Control.Monad (liftM)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Resource (ResourceT, runResourceT)
import Prelude hiding (FilePath)
import Crypto.Classes (Hash)
import qualified Crypto.Conduit as CC
import Data.Conduit (($$))
import qualified Data.Conduit.Combinators as CCB
import qualified Data.Conduit.Binary as CB
import qualified Data.Text as T
import Data.Time (UTCTime)
import qualified System.Directory as D
import System.FilePath (FilePath, makeRelative)
import qualified System.PosixCompat.Files as PF
import Text.Regex.TDFA ((=~))
import Text.Regex.TDFA.Text ()
import HaskDeep.Configuration
import HaskDeep.ComputationMode
import HaskDeep.HashSet (HashInfo(..), HashSet)
import qualified HaskDeep.HashSet as HS
-- | Compute @HashSet@ traversing recursively through the directory structure.
compute :: Hash ctx a => HaskDeepConfiguration
-> ComputationMode a
-> IO HashSet
compute conf cm = runResourceT $ CCB.sourceDirectoryDeep False root $$ CCB.foldM insert_hash empty_with_symbol
where
root = rootDirectory conf
regex = excludeRegex conf
mod_from = includeModFrom conf
mod_upto = includeModUpTo conf
empty_with_symbol = HS.setSymbol (symbol cm) HS.empty
insert_hash :: HashSet -> FilePath -> ResourceT IO HashSet
insert_hash hs fp = do let fpt = T.pack $ makeRelative root fp
fmt <- liftIO $ D.getModificationTime fp
if excRegex fpt regex || excMod fmt mod_from mod_upto
then return hs
else do s <- liftIO $ getFileSize fp
h <- liftM (runComputation cm) $ runResourceT
$ CB.sourceFile fp $$ CC.sinkHash
return $ HS.insert (HashInfo fpt s h) hs
getFileSize :: FilePath -> IO Integer
getFileSize path = do
stat <- PF.getFileStatus path
return (toInteger $ PF.fileSize stat)
excRegex :: T.Text -> Maybe T.Text -> Bool
excRegex fpt (Just rule) = fpt =~ rule
excRegex _ Nothing = False
excMod :: UTCTime -> Maybe UTCTime -> Maybe UTCTime -> Bool
excMod fmt (Just modFrom) (Just modUpTo) = (fmt < modFrom) || (fmt > modUpTo)
excMod fmt (Just modFrom) Nothing = fmt < modFrom
excMod fmt Nothing (Just modUpTo) = fmt > modUpTo
excMod _ Nothing Nothing = False