shell-monad 0.6.2 → 0.6.3
raw patch · 4 files changed
+82/−14 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Control.Monad.Shell: instance Applicative Script
+ Control.Monad.Shell: newVarFrom :: (NameHinted namehint, Param param) => param -> namehint -> Script (Term Var t)
+ Control.Monad.Shell.Quote: instance [overlap ok] IsString (Quoted Text)
Files
- CHANGELOG +11/−0
- Control/Monad/Shell.hs +66/−13
- Control/Monad/Shell/Quote.hs +4/−0
- shell-monad.cabal +1/−1
CHANGELOG view
@@ -1,3 +1,14 @@+shell-monad (0.6.3) unstable; urgency=medium++ * Add Applicative instance for Script. (Thanks, Abhinav Gupta)+ * Add IsString instance for Quoted Text. (Thanks, Abhinav Gupta)+ * Add newVarFrom. (Thanks, Abhinav Gupta)+ * Fixed "test (foo)" to generate shell code "test ( foo )", where+ before it had generated the invalid shell code "test (foo)".+ (Thanks, Ville)++ -- Joey Hess <id@joeyh.name> Sat, 14 Mar 2015 13:38:13 -0400+ shell-monad (0.6.2) unstable; urgency=medium * Avoid using $_ as this doesn't work in bash.
Control/Monad/Shell.hs view
@@ -1,4 +1,7 @@ -- | This is a shell monad, for generating shell scripts.+--+-- The emphasis is on generating shell code that will work in any POSIX+-- compliant shell. {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleInstances #-}@@ -31,6 +34,7 @@ NameHinted, static, newVar,+ newVarFrom, newVarContaining, setVar, globalVar,@@ -108,7 +112,7 @@ deriving (Eq, Ord, Show) simpleVar :: forall a. VarName -> Term Var a-simpleVar name = VarTerm $ V+simpleVar name = VarTerm V { varName = name -- Used to expand the variable; can be overridden for other -- types of variable expansion.@@ -119,9 +123,16 @@ , expandVar = \_ (VarName n) -> Q ("$" <> n) } --- | Treats the Text as a glob, which expands to one parameter per+-- | Treats the Text as a glob.+--+-- When used as a 'Param' to a command, it expands to one parameter per -- matching file. --+-- > forCmd (cmd "ls" (glob "*/*.cabal")) $ \file ->+-- > cmd "echo" file+--+-- When used in a 'caseOf', it matches text against the glob.+-- -- The input is assumed to be a well-formed glob. Characters in it that -- are not alphanumeric and are not wildcard characters will be escaped -- before it is exposed to the shell. This allows eg, spaces in globs.@@ -185,6 +196,13 @@ newtype Script a = Script (Env -> ([Expr], Env, a)) deriving (Functor) +instance Applicative Script where+ pure a = Script $ \env -> ([], env, a)+ Script f <*> Script a = Script $ \env0 ->+ let (expr1, env1, f') = f env0+ (expr2, env2, a') = a env1+ in (expr1 <> expr2, env2, f' a')+ instance Monad Script where return ret = Script $ \env -> ([], env, ret) a >>= b = Script $ \start -> let@@ -257,7 +275,7 @@ go (Pipe e1 e2) = go e1 <> " | " <> go e2 go (And e1 e2) = go e1 <> " && " <> go e2 go (Or e1 e2) = go e1 <> " || " <> go e2- go (Redir e r) = let use = (\t -> go e <> " " <> t) in case r of+ go (Redir e r) = let use t = go e <> " " <> t in case r of (RedirToFile fd f) -> use $ redirFd fd (Just stdOutput) <> "> " <> L.pack f (RedirToFileAppend fd f) ->@@ -289,7 +307,7 @@ -- Redirections have a default Fd; for example, ">" defaults to redirecting -- stdout. In this case, the file descriptor number does not need to be -- included.-redirFd :: Fd -> (Maybe Fd) -> L.Text+redirFd :: Fd -> Maybe Fd -> L.Text redirFd fd deffd | Just fd == deffd = "" | otherwise = showFd fd@@ -348,7 +366,7 @@ -- | A Param is anything that can be used as the parameter of a command. class Param a where- toTextParam :: a -> (Env -> L.Text)+ toTextParam :: a -> Env -> L.Text -- | Text arguments are automatically quoted. instance Param L.Text where@@ -359,7 +377,7 @@ toTextParam = toTextParam . L.pack instance Param UntypedVar where- toTextParam v = \env -> "\"" <> getQ (expandVar v env (varName v)) <> "\""+ toTextParam v env = "\"" <> getQ (expandVar v env (varName v)) <> "\"" instance Param (Term Var a) where toTextParam (VarTerm v) = toTextParam v@@ -378,13 +396,13 @@ -- | Allows passing the output of a command as a parameter. instance Param Output where- toTextParam (Output s) = \env ->+ toTextParam (Output s) env = let t = toLinearScript $ fst $ runScript env s in "\"$(" <> t <> ")\"" -- | Allows passing an Arithmetic Expression as a parameter. instance Param Arith where- toTextParam a = \env -> + toTextParam a env = let t = fmtArith env a in "\"" <> t <> "\"" @@ -440,7 +458,7 @@ -- To provide a naming hint, use either 'NamedLike'. -- -- @--- v1 <- 'newVar' ('NamedLike' "x")+-- v1 <- 'newVar' ('NamedLike' \"x\") -- @ class NameHinted h where hinted :: (Maybe L.Text -> a) -> h -> a@@ -472,6 +490,35 @@ v <- newVarUnsafe namehint Script $ \env -> ([Cmd (getName v <> "=" <> value)], env, v) +-- | Creates a new shell variable with an initial value coming from any+-- 'Param'.+--+-- For example,+--+-- > packageName <- newVarFrom+-- > (Output $+-- > cmd "grep" "-i" "name\\s*:" (glob "*.cabal") -|-+-- > cmd "perl" "-pe" "s/^name\\s*:\\s*//i")+-- > (NamedLike "packageName")+--+-- Use this with 'WithVar' to store to modified value of a variable in a new+-- variable.+--+-- > home <- globalVar "HOME"+-- > cabalDir <- newVarContaining (WithVar home (<> "/.cabal")) ()+-- +-- Or to capture the output of an arithmetic operation.+--+-- > sum <- newVarFrom (val x `APlus` 1) ()+--+newVarFrom+ :: (NameHinted namehint, Param param)+ => param -> namehint -> Script (Term Var t)+newVarFrom param namehint = do+ v <- newVarUnsafe namehint+ Script $ \env ->+ ([Cmd (getName v <> "=" <> toTextParam param env)], env, v)+ -- | Creates a new shell variable, with an initial value which can -- be anything that can be shown. --@@ -729,6 +776,12 @@ -- | Matches the value of the Var against the Quoted Text (which can -- be generated by 'glob'), and runs the Script action associated -- with the first match.+--+-- > arg <- takeParameter ()+-- > caseOf arg+-- > [ (quote "-h", showHelp)+-- > , (glob "-*", cmd "echo" "Unknown option:" arg)+-- > ] caseOf :: forall a. Term Var a -> [(Quoted L.Text, Script ())] -> Script () caseOf _ [] = return () caseOf v l = go True l@@ -743,7 +796,7 @@ -- > : ;; *) : -- > echo default -- > : ;; esac- go _ [] = add $ Cmd $ ";; esac"+ go _ [] = add $ Cmd ";; esac" go atstart ((t, s):rest) = do env <- getEnv let leader = if atstart@@ -768,7 +821,7 @@ readVar v = add $ Cmd $ "read " <> getQ (quote (getName v)) -- | By default, shell scripts continue running past commands that exit--- nonzero. Use "stopOnFailure True" to make the script stop on the first+-- nonzero. Use 'stopOnFailure True' to make the script stop on the first -- such command. stopOnFailure :: Bool -> Script () stopOnFailure b = add $ Cmd $ "set " <> (if b then "-" else "+") <> "e"@@ -915,7 +968,7 @@ go (TFileNonEmpty p) = unop "-s" (pv p) go (TFileExecutable p) = unop "-x" (pv p) - paren t = "\\(" <> t <> "\\)"+ paren t = "\\( " <> t <> " \\)" binop a o b = paren $ a <> " " <> o <> " " <> b unop o v = paren $ o <> " " <> v@@ -1073,7 +1126,7 @@ pred a = AMinus a (ANum 1) toEnum = ANum . fromIntegral enumFrom a = a : enumFrom (succ a)- enumFromThen a b = a : enumFromThen b ((b `AMult` (ANum 2)) `AMinus` a)+ enumFromThen a b = a : enumFromThen b ((b `AMult` ANum 2) `AMinus` a) fromEnum = error "fromEnum not implemented for Arith" enumFromTo = error "enumFromTo not implemented for Arith" enumFromThenTo = error "enumFromToThen not implemented for Arith"
Control/Monad/Shell/Quote.hs view
@@ -12,6 +12,7 @@ ) where import qualified Data.Text.Lazy as L+import Data.String import Data.Monoid import Data.Char @@ -52,3 +53,6 @@ -- | An arbitrary value. newtype Val v = Val v++instance IsString (Quoted L.Text) where+ fromString = quote
shell-monad.cabal view
@@ -1,5 +1,5 @@ Name: shell-monad-Version: 0.6.2+Version: 0.6.3 Cabal-Version: >= 1.8 License: BSD3 Maintainer: Joey Hess <id@joeyh.name>