packages feed

haskdeep-0.1.0.0: 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           Data.Maybe (fromJust)
import           Prelude hiding (FilePath)

import           Crypto.Classes (Hash)
import qualified Crypto.Conduit as CC
import           Data.Conduit (($$))
import qualified Data.Conduit as C
import qualified Data.Conduit.Filesystem as CF
import qualified Data.Conduit.List as CL
import qualified Data.Text as T
import qualified Filesystem as FS
import           Filesystem.Path.CurrentOS (FilePath)
import qualified Filesystem.Path.CurrentOS as FSC
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 = CF.traverse False root $$ CL.foldM insert_hash empty_with_symbol
    where
      root              = rootDirectory conf
      rule              = ignoreRule conf
      empty_with_symbol = HS.setSymbol (symbol cm) HS.empty

      insert_hash :: HashSet -> FilePath -> IO HashSet
      insert_hash hs fp = do let fpt = fpToText $ relativize root fp
                             if ignore fpt rule
                             then return hs
                             else do s <- FS.getSize fp
                                     h <- liftM (runComputation cm) $ C.runResourceT
                                          $ CF.sourceFile fp $$ CC.sinkHash
                                     return $ HS.insert (HashInfo fpt s h) hs

relativize :: FilePath -> FilePath -> FilePath
relativize root fp = fromJust $ FSC.stripPrefix root fp

fpToText :: FilePath -> T.Text
fpToText = either id id . FSC.toText

ignore :: T.Text -> Maybe T.Text -> Bool
ignore fpt (Just rule) = fpt =~ rule
ignore _   Nothing     = False