Commando (empty) → 0.1.0.0
raw patch · 4 files changed
+149/−0 lines, 4 filesdep +basedep +fsnotifydep +optparse-applicativesetup-changed
Dependencies added: base, fsnotify, optparse-applicative, process, system-fileio, system-filepath, text
Files
- Commando.cabal +23/−0
- Commando.hs +101/−0
- LICENSE +23/−0
- Setup.hs +2/−0
+ Commando.cabal view
@@ -0,0 +1,23 @@+name: Commando+version: 0.1.0.0+synopsis: Watch some files; Rerun a command+description: Watch some files; Rerun a command+homepage: https://github.com/sordina/Commando+license: MIT+license-file: LICENSE+author: Lyndon Maydwell+maintainer: lyndon@sordina.net+category: Development+build-type: Simple+cabal-version: >=1.8++executable commando+ main-is: Commando.hs+ build-depends:+ base >= 4.0 && < 5.0,+ text >= 0.10 && < 0.12,+ system-filepath >= 0.4 && < 0.5,+ optparse-applicative >= 0.5 && < 0.6,+ system-fileio >= 0.3 && < 0.4,+ fsnotify >= 0.0.10 && < 0.1,+ process >= 1.1 && < 1.2
+ Commando.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE OverloadedStrings #-}++module Main where++import Prelude hiding (FilePath)+import Control.Monad (void, when)+import System.Process (rawSystem, runCommand, runInteractiveCommand)+import System.FSNotify (startManager, watchTree, stopManager)+import Filesystem.Path.CurrentOS (FilePath, fromText)+import Data.Text (pack)+import System.IO (hPutStrLn, hGetContents, hSetBuffering, BufferMode(..))+import GHC.IO.Handle (hClose, hFlush)+import GHC.IO.Handle.Types (Handle)+import System.Process.Internals (ProcessHandle)+import Control.Applicative ((<$>), (<*>))+import Data.Monoid (mempty, Monoid, (<>))+import Control.Concurrent (forkIO)+import Data.Maybe (fromMaybe)++import qualified Options.Applicative.Builder.Internal as X+import qualified Options.Applicative as O++type RunningProcess = (Handle, Handle, Handle, ProcessHandle)++data Options = Options { command :: String+ , quiet :: Bool+ , consumer :: Bool+ , stdin :: Bool+ , persist :: Bool+ , directory :: FilePath+ } deriving Show++main :: IO ()+main = O.execParser (O.info (O.helper <*> options) mempty) >>= start++options :: O.Parser Options+options = Options+ <$> defStr "echo event" ( O.metavar "COMMAND" <> O.help "Command run on events")+ <*> O.switch ( O.short 'q' <> O.long "quiet" <> O.help "Hide non-essential output")+ <*> O.switch ( O.short 'c' <> O.long "consumer" <> O.help "Pass events as argument to command")+ <*> O.switch ( O.short 'i' <> O.long "stdin" <> O.help "Pipe events to command")+ <*> O.switch ( O.short 'p' <> O.long "persist" <> O.help "Pipe events to persistent command")+ <*> (dir <$> defStr "." ( O.metavar "DIRECTORY" <> O.help "Directory to monitor" ))++defStr :: String -> X.Mod X.ArgumentFields String -> O.Parser String+defStr a = def a . O.argument O.str++def :: a -> O.Parser a -> O.Parser a+def a = fmap (fromMaybe a) . O.optional++dir :: String -> FilePath+dir = fromText . pack++start :: Options -> IO ()+start o = do+ man <- startManager+ rc <- if persist o then Just <$> startPipe (command o)+ else return Nothing++ void $ forkIO $ whenM rc $ \(_,so,_,_) -> hGetContents so >>= putStr++ when (not $ quiet o) $ putStrLn "press return to stop"++ let cmd = command o++ void $ watchTree man (directory o) (const True)+ $ case (consumer o, stdin o || persist o )+ of (True , _ ) -> void . rawSystem cmd . return . show+ (_ , True ) -> void . pipe rc cmd . show+ (_ , _ ) -> const $ void $ runCommand cmd++ void $ getLine++ void $ stopManager man++ whenM rc pipeClose++whenM :: Monad m => Maybe a -> (a -> m ()) -> m ()+whenM m f = maybe (return ()) f m++startPipe :: String -> IO RunningProcess+startPipe cmd = do+ rc@(hStdIn,_,_,_) <- runInteractiveCommand cmd+ hSetBuffering hStdIn NoBuffering+ return rc++pipe :: Maybe RunningProcess -> String -> String -> IO ()+pipe Nothing cmd arg = startPipe cmd >>= pipeSend arg >>= pipeOutput >>= pipeClose+pipe (Just rp) _ arg = void $ pipeSend arg rp++pipeOutput :: RunningProcess -> IO RunningProcess+pipeOutput r@(_,so,_,_) = hGetContents so >>= putStr >> return r++pipeClose :: RunningProcess -> IO ()+pipeClose (hStdIn, _, _, _) = hClose hStdIn++pipeSend :: String -> RunningProcess -> IO RunningProcess+pipeSend param rc@(hStdIn, _hStdOut, _stderr, _process) = do+ hPutStrLn hStdIn param+ hFlush hStdIn+ return rc
+ LICENSE view
@@ -0,0 +1,23 @@+The MIT License (MIT)++Copyright (c) 2013 Lyndon Maydwell++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+THE SOFTWARE.++
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain