fswatcher 0.1.3 → 0.2.0
raw patch · 2 files changed
+35/−18 lines, 2 filesdep +optparse-applicativedep +regex-pcre-builtin
Dependencies added: optparse-applicative, regex-pcre-builtin
Files
- fswatcher.cabal +3/−1
- fswatcher.hs +32/−17
fswatcher.cabal view
@@ -1,7 +1,7 @@ name: fswatcher category: Tools build-type: Simple-version: 0.1.3+version: 0.2.0 synopsis: Watch a file/directory and run a command when it's modified description: A simple program that watches a file or a directory and runs a given command whenever the file or a file within the@@ -23,6 +23,8 @@ , fsnotify >= 0.1 , system-filepath >= 0.4 , directory >= 1.2+ , optparse-applicative >= 0.11+ , regex-pcre-builtin >= 0.94 ghc-options: -Wall main-is: fswatcher.hs
fswatcher.hs view
@@ -1,34 +1,46 @@+{-# LANGUAGE OverloadedStrings #-}+ import System.IO (hPutStrLn, stderr) import System.Posix.Files (getFileStatus, isDirectory)-import System.Environment (getArgs, getProgName) import System.Directory (canonicalizePath, getCurrentDirectory) import Filesystem.Path ((</>), directory) import Filesystem.Path.CurrentOS (decodeString, encodeString) import Data.String (fromString) import System.FSNotify (Event (..), StopListening, WatchManager, startManager,- stopManager, watchTree, watchDir)-import System.Exit (ExitCode (..), exitSuccess, exitFailure)+ stopManager, watchTree, watchDir, eventPath)+import System.Exit (ExitCode (..), exitSuccess) import System.Process (createProcess, proc, waitForProcess)-import Control.Monad (void, when)+import Control.Monad (void) import System.Posix.Signals (installHandler, Handler(Catch), sigINT, sigTERM) import Control.Concurrent (forkIO, killThread) import Control.Concurrent.MVar+import Text.Regex.PCRE+import Options.Applicative +import Opts+ data FileType = File | Directory deriving Eq -- Watches a file or directory and whenever a “modified” event is registered we -- put () in the MVar that acts as a run trigger. `tryPutMVar` is used to avoid -- re-running the command many times if the file/dir is changed more than once -- while the command is already running.-watch :: FileType -> WatchManager -> String -> MVar () -> IO StopListening-watch filetype m path trigger =+watch :: FileType -> WatchManager -> String -> MVar () -> WatchOpt -> IO StopListening+watch filetype m path trigger opt = let watchFun = case filetype of- Directory -> watchTree m path (const True)+ Directory -> watchTree m path (matchFiles opt) File -> watchDir m (encodeString $ directory $ decodeString path) isThisFile in watchFun (\_ -> void $ tryPutMVar trigger ()) where isThisFile (Modified p _) = p == fromString path isThisFile _ = False+ matchFiles :: WatchOpt -> Event -> Bool+ matchFiles wo event = let p = eventPath event+ includes = includePath wo+ excludes = excludePath wo+ in+ (null includes || p =~ includePath wo)+ && (null excludes || not (p =~ excludePath wo)) runCmd :: String -> [String] -> MVar () -> IO () runCmd cmd args trigger = do@@ -41,13 +53,12 @@ ExitFailure n -> "Process returned " ++ show n runCmd cmd args trigger -main :: IO ()-main = do- argv <- getArgs- when (length argv < 2) $ getProgName >>= usage >> exitFailure+runWatch :: WatchOpt -> IO ()+runWatch opt = do - let [path,cmd] = take 2 argv- let args = drop 2 argv+ let path = watchPath opt+ let cmd = head $ actionCmd opt+ let args = drop 1 $ actionCmd opt m <- startManager @@ -65,7 +76,7 @@ runTrigger <- newEmptyMVar runThread <- forkIO $ runCmd cmd args runTrigger- stopWatcher <- watch filetype m canonicalPath runTrigger+ stopWatcher <- watch filetype m canonicalPath runTrigger opt -- Calculate the full path in order to print the "real" file when watching a -- path with one or more symlinks.@@ -83,6 +94,10 @@ stopManager m killThread runThread exitSuccess- where usage n = hPutStrLn stderr $ "Usage: " ++ n- ++ " <file/directory to watch>"- ++ " <command to run> [arguments for command]"++main :: IO ()+main = execParser opts >>= runWatch+ where+ opts = info (helper <*> watchOpt)+ ( fullDesc+ <> progDesc "monitors a file or a directory for changes and runs a given command.")