{-# LANGUAGE CPP #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
module Data.Conduit.FSNotify
(
sourceFileChanges
, acquireSourceFileChanges
, FileChangeSettings
, mkFileChangeSettings
, setWatchConfig
, setRelative
, setRecursive
, setPredicate
, FS.Event (..)
, FS.eventTime
, FS.eventPath
, FS.WatchConfig (..)
, FS.Debounce (..)
) where
import Control.Exception (assert)
import Data.Conduit
import Control.Monad.Trans.Resource (MonadResource, release)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad (forever)
import qualified System.FSNotify as FS
import System.Directory (canonicalizePath)
import Control.Concurrent.Chan
import Data.List (stripPrefix)
import System.FilePath (addTrailingPathSeparator)
import qualified Data.Acquire as A
data FileChangeSettings = FileChangeSettings
{ fcsDir :: !FilePath
, fcsWatchConfig :: !FS.WatchConfig
, fcsRelative :: !Bool
, fcsRecursive :: !Bool
, fcsPredicate :: !(FS.Event -> Bool)
}
setWatchConfig :: FS.WatchConfig -> FileChangeSettings -> FileChangeSettings
setWatchConfig x fcs = fcs { fcsWatchConfig = x }
setRelative :: Bool -> FileChangeSettings -> FileChangeSettings
setRelative x fcs = fcs { fcsRelative = x }
setRecursive :: Bool -> FileChangeSettings -> FileChangeSettings
setRecursive x fcs = fcs { fcsRecursive = x }
setPredicate :: (FS.Event -> Bool) -> FileChangeSettings -> FileChangeSettings
setPredicate x fcs = fcs { fcsPredicate = x }
mkFileChangeSettings :: FilePath
-> FileChangeSettings
mkFileChangeSettings dir = FileChangeSettings
{ fcsDir = dir
, fcsWatchConfig = FS.defaultConfig
, fcsRelative = True
, fcsRecursive = True
, fcsPredicate = const True
}
sourceFileChanges :: MonadResource m
=> FileChangeSettings
-> ConduitM i FS.Event m ()
sourceFileChanges fcs = do
(key, src) <- A.allocateAcquire $ acquireSourceFileChanges fcs
src
release key
acquireSourceFileChanges
:: MonadIO m
=> FileChangeSettings
-> A.Acquire (ConduitM i FS.Event m ())
acquireSourceFileChanges FileChangeSettings {..} = do
man <- A.mkAcquire (FS.startManagerConf fcsWatchConfig) FS.stopManager
root' <- liftIO $ canonicalizePath fcsDir
chan <- liftIO newChan
let watchChan = if fcsRecursive then FS.watchTreeChan else FS.watchDirChan
_ <- A.mkAcquire (watchChan man root' fcsPredicate chan) id
return $ forever $ do
event <- liftIO $ readChan chan
if fcsRelative
then do
let fp = FS.eventPath event
case stripPrefix (addTrailingPathSeparator root') fp of
Nothing -> assert False $ return ()
Just suffix
| null suffix -> return ()
| otherwise -> yield $
case event of
#if MIN_VERSION_fsnotify(0, 3, 0)
FS.Added _ time dir -> FS.Added suffix time dir
FS.Modified _ time dir -> FS.Modified suffix time dir
FS.Removed _ time dir -> FS.Removed suffix time dir
FS.Unknown _ time str -> FS.Unknown suffix time str
#else
FS.Added _ time -> FS.Added suffix time
FS.Modified _ time -> FS.Modified suffix time
FS.Removed _ time -> FS.Removed suffix time
#endif
else yield event