shh 0.2.0.0 → 0.2.0.1
raw patch · 6 files changed
+50/−29 lines, 6 filesdep ~async
Dependency ranges changed: async
Files
- README.md +3/−3
- app/Example.hs +10/−4
- shh.cabal +9/−1
- src/Shh.hs +1/−0
- src/Shh/Internal.hs +26/−20
- test/Test.hs +1/−1
README.md view
@@ -1,7 +1,7 @@ # Shh -[](http://hackage.haskell.org/package/shh)-[](https://travis-ci.org/luke-clifton/shh)+[](http://hackage.haskell.org/package/shh)+[](https://travis-ci.org/luke-clifton/shh) Shh is a library to enable convinient shell-like programming in Haskell. It works well in scripts, and from GHCi, allowing you to use GHCi as a shell.@@ -104,7 +104,7 @@ Enable Temlpate Haskell and load the environment {-# LANGUAGE TemplateHaskell #-}- $(loadEnv)+ $(loadEnv SearchPath) You now have all your executables available as simple to read Haskell functions.
app/Example.hs view
@@ -8,14 +8,20 @@ import Data.Monoid import Data.Char import Data.List+import Control.Concurrent.Async -$(loadEnv)+-- Load everything...+-- $(loadEnv) --- We could also have been a little more explicit about it.--- load ["sleep", "echo", "cat"]+-- OR -- +-- We could also be a little more explicit about it.+$(load Absolute ["sleep", "echo", "cat", "xxd"])+ main :: IO () main = do -- Crash the program if we are missing any executables. [] <- missingExecutables- (sleep 1 >> echo "Hello" >> sleep 2) |> cat+ concurrently_+ ((sleep 1 >> echo "Hello" >> sleep 2) |> cat)+ (echo "A" >> sleep 1 >> echo "b" |> xxd)
shh.cabal view
@@ -1,5 +1,5 @@ name: shh-version: 0.2.0.0+version: 0.2.0.1 synopsis: Simple shell scripting from Haskell description: Provides a shell scripting environment for Haskell. It helps you all external binaries, and allows you to@@ -42,9 +42,14 @@ default-language: Haskell2010 build-depends: base >=4.9,+ async, shh hs-source-dirs: app main-is: Example.hs+ build-tools:+ coreutils,+ vault,+ vim test-suite shh-tests@@ -56,6 +61,9 @@ tasty-quickcheck, tasty-hunit, shh+ build-tools:+ perl,+ vim hs-source-dirs: test main-is: Test.hs type: exitcode-stdio-1.0
src/Shh.hs view
@@ -53,6 +53,7 @@ -- | == Constructing Arguments , ExecArg(..) -- | == Template Haskell helpers+ , ExecReference(..) , load , loadEnv , loadAnnotated
src/Shh/Internal.hs view
@@ -399,28 +399,34 @@ exe s = toArgs [s] -- | Create a function for the executable named-loadExe :: String -> Q [Dec]-loadExe s = loadExeAs s s+loadExe :: ExecReference -> String -> Q [Dec]+loadExe ref s = loadExeAs ref s s +-- | Specify how executables should be referenced.+data ExecReference+ = Absolute -- ^ Find executables on PATH, but store their absolute path+ | SearchPath -- ^ Always search on PATH+ -- | @$(loadExeAs fnName executable)@ defines a function called @fnName@ -- which executes the path in @executable@.-loadExeAs :: String -> String -> Q [Dec]-loadExeAs fnName executable =+loadExeAs :: ExecReference -> String -> String -> Q [Dec]+loadExeAs ref fnName executable = -- TODO: Can we place haddock markup in TH generated functions. -- TODO: Can we palce the man page for each function in there xD -- https://ghc.haskell.org/trac/ghc/ticket/5467 let name = mkName $ fnName- impl = valD (varP name) (normalB [|- exe executable+ impl executableRef = valD (varP name) (normalB [|+ exe executableRef |]) [] typn = mkName "a" typ = SigD name (ForallT [PlainTV typn] [AppT (ConT ''Unit) (VarT typn), AppT (ConT ''ExecArgs) (VarT typn)] (VarT typn)) in do- isExe <- runIO $ checkExecutable executable- when (not isExe) $ error $ "Attempted to load '" ++ executable ++ "', but it isn't executable"- i <- impl- return $ [typ,i]+ runIO (Dir.findExecutable executable) >>= \case+ Nothing -> error $ "Attempted to load '" ++ executable ++ "', but it is not executable"+ Just absExe -> do+ i <- impl (case ref of { Absolute -> absExe; SearchPath -> executable })+ return $ [typ,i] -- | Checks if a String is a valid Haskell identifier. validIdentifier :: String -> Bool@@ -441,8 +447,8 @@ -- are ignored. It also creates the IO action @missingExecutables@ which will -- do a runtime check to ensure all the executables that were found at -- compile time still exist.-loadEnv :: Q [Dec]-loadEnv = loadAnnotatedEnv id+loadEnv :: ExecReference -> Q [Dec]+loadEnv ref = loadAnnotatedEnv ref id -- | Test to see if an executable can be found either on the $PATH or absolute. checkExecutable :: FilePath -> IO Bool@@ -451,14 +457,14 @@ -- | Load the given executables into the program, checking their executability -- and creating a function @missingExecutables@ to do a runtime check for their -- availability.-load :: [String] -> Q [Dec]-load = loadAnnotated id+load :: ExecReference -> [String] -> Q [Dec]+load ref = loadAnnotated ref id -- | Same as `load`, but allows you to modify the function names.-loadAnnotated :: (String -> String) -> [String] -> Q [Dec]-loadAnnotated f bins = do+loadAnnotated :: ExecReference -> (String -> String) -> [String] -> Q [Dec]+loadAnnotated ref f bins = do let pairs = mapMaybe getAnnotation bins- ds <- fmap join $ mapM (uncurry loadExeAs) pairs+ ds <- fmap join $ mapM (uncurry (loadExeAs ref)) pairs d <- valD (varP (mkName "missingExecutables")) (normalB [| filterM (fmap not . checkExecutable) bins |]) []@@ -473,10 +479,10 @@ -- | Like `loadEnv`, but allows you to modify the function name that would -- be generated.-loadAnnotatedEnv :: (String -> String) -> Q [Dec]-loadAnnotatedEnv f = do+loadAnnotatedEnv :: ExecReference -> (String -> String) -> Q [Dec]+loadAnnotatedEnv ref f = do bins <- runIO pathBins- loadAnnotated f bins+ loadAnnotated ref f bins -- | Function that splits '\0' seperated list of strings. Useful in conjuction -- with @find . "-print0"@.
test/Test.hs view
@@ -7,7 +7,7 @@ import Test.Tasty.HUnit import Test.Tasty.QuickCheck -$(loadEnv)+$(loadEnv SearchPath) main = do putStrLn "################################################"