command-qq 0.2.1.0 → 0.2.2.0
raw patch · 13 files changed
+370/−361 lines, 13 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ System.Command.QQ: substituteVars :: String -> Q Exp
Files
- CHANGELOG.md +9/−4
- command-qq.cabal +6/−6
- example/CommandT.hs +128/−0
- examples/CommandT.hs +0/−128
- src/System/Command/QQ.hs +14/−10
- test/Doctests.hs +13/−0
- test/Spec.hs +1/−0
- test/System/Command/QQ/EmbedSpec.hs +105/−0
- test/System/Command/QQ/EvalSpec.hs +94/−0
- tests/Doctests.hs +0/−13
- tests/Spec.hs +0/−1
- tests/System/Command/QQ/EmbedSpec.hs +0/−105
- tests/System/Command/QQ/EvalSpec.hs +0/−94
CHANGELOG.md view
@@ -1,9 +1,14 @@+0.2.2.0+=======++ * Exported `substituteVars`+ 0.2.1.0 ======= - * Add a bunch of predefined quasiquoters to `System.Command.QQ.Predef`+ * Added a bunch of predefined quasiquoters to `System.Command.QQ.Predef` - * Add `Embed` instances for `Data.Text.Text` and `Data.Text.Lazy.Text`+ * Added `Embed` instances for `Data.Text.Text` and `Data.Text.Lazy.Text` 0.2.0.0 =======@@ -12,6 +17,6 @@ * Moved `Eval` onto `Text` to speed I/O up. - * Quasiquoters support literals and constructors with no arguments+ * Added support for embedding literals and constructors with no arguments - * More `Embed` instances+ * Mored `Embed` instances
command-qq.cabal view
@@ -1,5 +1,5 @@ name: command-qq-version: 0.2.1.0+version: 0.2.2.0 synopsis: Quasiquoters for external commands description: Features:@@ -39,7 +39,7 @@ extra-source-files: CHANGELOG.md README.md- examples/CommandT.hs+ example/CommandT.hs source-repository head type: git@@ -48,7 +48,7 @@ source-repository this type: git location: https://github.com/biegunka/command-qq- tag: 0.2.1.0+ tag: 0.2.2.0 library default-language: Haskell2010@@ -70,7 +70,7 @@ build-depends: base == 4.* , doctest- hs-source-dirs: tests+ hs-source-dirs: test main-is: Doctests.hs test-suite spec@@ -83,8 +83,8 @@ , template-haskell , text hs-source-dirs:- tests- , examples+ test+ , example main-is: Spec.hs other-modules: System.Command.QQ.EmbedSpec
+ example/CommandT.hs view
@@ -0,0 +1,128 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+-- | Example DSL made on top of "System.Command.QQ"+--+-- Provides [semi-]convenient way to run external commands+-- in sequence and parse their output.+--+-- DSL does not use any custom quasiquoters but provides 'Eval'+-- instances for custom datatypes that implement desired semantics+module CommandT where++import Control.Applicative -- base+ ( Applicative(..), Alternative(..) )+import Control.Monad (MonadPlus(..)) -- base+import Control.Monad.IO.Class (MonadIO(..)) -- transformers+import Control.Monad.Trans.Class (MonadTrans(..)) -- transformers+import Control.Monad.Trans.Except -- transformers+import Data.Monoid (Last(..)) -- base+import Data.Text.Lazy (Text) -- text+import System.Exit (ExitCode(..)) -- base+import System.Command.QQ (Eval(..)) -- command-qq++-- $setup+-- >>> :set -XQuasiQuotes+-- >>> import System.Command.QQ+-- >>> import qualified Data.Text.Lazy as T+-- >>> let lengths = [sh|while read line; do echo ${#line}; done|] :: Text -> CommandT IO Text+++infixl 1 >>! -- same as >>=+++-- | External commands sequencing result+--+-- Every external command results either in failure (thus provides non-zero+-- exit code and @stderr@) or some value (typically its @stdout@)+--+-- For example:+--+-- >>> runCommandT $ [sh|echo -e "hello\nworld!!!"|] >>= lengths+-- Right "5\n8\n"+--+-- 'CommandT' implements the usual 'Alternative' instance semantics:+--+-- >>> runCommandT $ [sh|exit 1|] <|> [sh|echo hello|]+-- Right "hello\n"+--+-- If everything fails, then last failure is returned:+--+-- >>> do Left (Last (Just (Failure _ i _))) <- runCommandT $ [sh|exit 1|] <|> [sh|exit 3|]; print i+-- 3+newtype CommandT m a = CommandT { unCommandT :: ExceptT (Last Failure) m a }++-- | Failed command with exit code and @stderr@+data Failure = Failure Command Int Text+ deriving (Show, Read)++-- | Command name and arguments+data Command = Command String [String]+ deriving (Show, Read)++-- | Run external commands and get the result+runCommandT :: CommandT m a -> m (Either (Last Failure) a)+runCommandT = runExceptT . unCommandT++instance (Functor m, Monad m) => Functor (CommandT m) where+ fmap f (CommandT x) = CommandT (fmap f x)++instance (Functor m, Monad m) => Applicative (CommandT m) where+ pure = CommandT . pure+ CommandT f <*> CommandT x = CommandT (f <*> x)++instance (Functor m, Monad m) => Monad (CommandT m) where+ return = pure+ CommandT x >>= k = CommandT (x >>= unCommandT . k)++instance (Functor m, Monad m) => Alternative (CommandT m) where+ empty = CommandT empty+ CommandT f <|> CommandT x = CommandT (f <|> x)++instance (Functor m, Monad m) => MonadPlus (CommandT m) where+ mzero = empty+ mplus = (<|>)++instance MonadTrans CommandT where+ lift = CommandT . lift++instance (Functor m, MonadIO m) => MonadIO (CommandT m) where+ liftIO = lift . liftIO++instance (o ~ Text, MonadIO m) => Eval (CommandT m o) where+ eval command args = CommandT . ExceptT $ do+ (status, out, err) <- liftIO $ eval command args+ return $ case status of+ ExitSuccess -> Right out+ ExitFailure i -> Left (Last (Just (Failure (Command command args) i err)))++instance (i ~ Text, o ~ Text, MonadIO m) => Eval (i -> CommandT m o) where+ eval command args input = CommandT . ExceptT $ do+ (status, out, err) <- liftIO $ eval command args input+ return $ case status of+ ExitSuccess -> Right out+ ExitFailure i -> Left (Last (Just (Failure (Command command args) i err)))+++-- | Pass @stderr@ of failed external command to function+--+-- If nothing has failed, we do not have @stderr@, really:+--+-- >>> runCommandT $ [sh|echo -e "hello\nworld!!!">&2|] >>! lengths+-- Left (Last {getLast = Nothing})+--+-- If something has failed, we do have @stderr@ to play with:+--+-- >>> runCommandT $ [sh|echo -e "hello\nworld!!!">&2; exit 1|] >>! lengths+-- Right "5\n8\n"+--+-- And playing may involve arbitrary Haskell functions, of course:+--+-- >>> runCommandT $ [sh|echo -e "hello\nworld!!!">&2; exit 1|] >>! lengths . T.unlines . reverse . T.lines+-- Right "8\n5\n"+(>>!) :: Monad m => CommandT m a -> (Text -> CommandT m b) -> CommandT m b+x >>! k = CommandT . ExceptT $ do+ t <- runCommandT x+ case t of+ Left (Last Nothing) -> return (Left (Last Nothing))+ Left (Last (Just (Failure _ _ err))) -> runCommandT $ k err+ Right _ -> return (Left (Last Nothing))
− examples/CommandT.hs
@@ -1,128 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TypeFamilies #-}--- | Example DSL made on top of "System.Command.QQ"------ Provides [semi-]convenient way to run external commands--- in sequence and parse their output.------ DSL does not use any custom quasiquoters but provides 'Eval'--- instances for custom datatypes that implement desired semantics-module CommandT where--import Control.Applicative -- base- ( Applicative(..), Alternative(..) )-import Control.Monad (MonadPlus(..)) -- base-import Control.Monad.IO.Class (MonadIO(..)) -- transformers-import Control.Monad.Trans.Class (MonadTrans(..)) -- transformers-import Control.Monad.Trans.Either -- either-import Data.Monoid (Last(..)) -- base-import Data.Text.Lazy (Text) -- text-import System.Exit (ExitCode(..)) -- base-import System.Command.QQ (Eval(..)) -- command-qq---- $setup--- >>> :set -XQuasiQuotes--- >>> import System.Command.QQ--- >>> import qualified Data.Text.Lazy as T--- >>> let lengths = [sh|while read line; do echo ${#line}; done|] :: Text -> CommandT IO Text---infixl 1 >>! -- same as >>=----- | External commands sequencing result------ Every external command results either in failure (thus provides non-zero--- exit code and @stderr@) or some value (typically its @stdout@)------ For example:------ >>> runCommandT $ [sh|echo -e "hello\nworld!!!"|] >>= lengths--- Right "5\n8\n"------ 'CommandT' implements the usual 'Alternative' instance semantics:------ >>> runCommandT $ [sh|exit 1|] <|> [sh|echo hello|]--- Right "hello\n"------ If everything fails, then last failure is returned:------ >>> do Left (Last (Just (Failure _ i _))) <- runCommandT $ [sh|exit 1|] <|> [sh|exit 3|]; print i--- 3-newtype CommandT m a = CommandT { unCommandT :: EitherT (Last Failure) m a }---- | Failed command with exit code and @stderr@-data Failure = Failure Command Int Text- deriving (Show, Read)---- | Command name and arguments-data Command = Command String [String]- deriving (Show, Read)---- | Run external commands and get the result-runCommandT :: CommandT m a -> m (Either (Last Failure) a)-runCommandT = runEitherT . unCommandT--instance Monad m => Functor (CommandT m) where- fmap f (CommandT x) = CommandT (fmap f x)--instance Monad m => Applicative (CommandT m) where- pure = CommandT . pure- CommandT f <*> CommandT x = CommandT (f <*> x)--instance Monad m => Monad (CommandT m) where- return = pure- CommandT x >>= k = CommandT (x >>= unCommandT . k)--instance Monad m => Alternative (CommandT m) where- empty = CommandT empty- CommandT f <|> CommandT x = CommandT (f <|> x)--instance Monad m => MonadPlus (CommandT m) where- mzero = empty- mplus = (<|>)--instance MonadTrans CommandT where- lift = CommandT . lift--instance MonadIO m => MonadIO (CommandT m) where- liftIO = lift . liftIO--instance (o ~ Text, MonadIO m) => Eval (CommandT m o) where- eval command args = CommandT . EitherT $ do- (status, out, err) <- liftIO $ eval command args- return $ case status of- ExitSuccess -> Right out- ExitFailure i -> Left (Last (Just (Failure (Command command args) i err)))--instance (i ~ Text, o ~ Text, MonadIO m) => Eval (i -> CommandT m o) where- eval command args input = CommandT . EitherT $ do- (status, out, err) <- liftIO $ eval command args input- return $ case status of- ExitSuccess -> Right out- ExitFailure i -> Left (Last (Just (Failure (Command command args) i err)))----- | Pass @stderr@ of failed external command to function------ If nothing has failed, we do not have @stderr@, really:------ >>> runCommandT $ [sh|echo -e "hello\nworld!!!">&2|] >>! lengths--- Left (Last {getLast = Nothing})------ If something has failed, we do have @stderr@ to play with:------ >>> runCommandT $ [sh|echo -e "hello\nworld!!!">&2; exit 1|] >>! lengths--- Right "5\n8\n"------ And playing may involve arbitrary Haskell functions, of course:------ >>> runCommandT $ [sh|echo -e "hello\nworld!!!">&2; exit 1|] >>! lengths . T.unlines . reverse . T.lines--- Right "8\n5\n"-(>>!) :: Monad m => CommandT m a -> (Text -> CommandT m b) -> CommandT m b-x >>! k = CommandT . EitherT $ do- t <- runCommandT x- case t of- Left (Last Nothing) -> return (Left (Last Nothing))- Left (Last (Just (Failure _ _ err))) -> runCommandT $ k err- Right _ -> return (Left (Last Nothing))
src/System/Command/QQ.hs view
@@ -6,11 +6,15 @@ module System.Command.QQ ( -- * Quasiquoters -- ** Default shell- sh_, sh+ sh_+ , sh -- ** Constructors- , shell, interpreter+ , shell+ , interpreter -- * Customizations- , quoter, callCommand+ , quoter+ , callCommand+ , substituteVars , module System.Command.QQ.Embed , module System.Command.QQ.Eval ) where@@ -96,7 +100,7 @@ -- and producing Haskell expression. -- -- Other kinds of quasiquoters (patterns, types or--- declarations quasiquoters) will fail in compile time+-- declarations quasiquoters) will fail at compile time quoter :: (String -> Q Exp) -> QuasiQuoter quoter quote = QuasiQuoter { quoteExp = quote@@ -115,7 +119,7 @@ -> String -- ^ Quasiquoter contents -> Q Exp callCommand path args string =- [e| eval path (args ++ [$(string2exp string)]) |]+ [e| eval path (args ++ [$(substituteVars string)]) |] -- | Construct Haskell expression for external command call callCommand_@@ -124,11 +128,12 @@ -> String -- ^ Quasiquoter contents -> Q Exp callCommand_ path args string =- [e| eval path (args ++ [$(string2exp string)]) :: IO () |]+ [e| eval path (args ++ [$(substituteVars string)]) :: IO () |] --- | Parse references to Haskell variables-string2exp :: String -> Q Exp-string2exp = raw where+-- | Construct Haskell expression from the string, substituting variables+-- for their values. Variable expansion uses a ruby-like syntax+substituteVars :: String -> Q Exp+substituteVars = raw where raw, var :: String -> Q Exp raw (break (== '#') -> parts) = case parts of (before, '#':'{' :after) -> [e| before ++ $(var after) |]@@ -140,7 +145,6 @@ var (break (== '}') -> parts) = case parts of (b:efore, '}':after) | isLower b -> external (VarE (mkName (b:efore))) after- | isUpper b -> external (ConE (mkName (b:efore))) after | isUpper b -> external (ConE (mkName (b:efore))) after | Just i <- readMaybe (b:efore) -> external (LitE (IntegerL i)) after | Just d <- readMaybe (b:efore) -> external (LitE (RationalL (toRational (d :: Double)))) after
+ test/Doctests.hs view
@@ -0,0 +1,13 @@+module Main where++import Test.DocTest (doctest)+++main :: IO ()+main = doctest+ [ "src/System/Command/QQ.hs"+ , "src/System/Command/QQ/Embed.hs"+ , "src/System/Command/QQ/Eval.hs"+ , "src/System/Command/QQ/Predef.hs"+ , "example/CommandT.hs"+ ]
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ test/System/Command/QQ/EmbedSpec.hs view
@@ -0,0 +1,105 @@+module System.Command.QQ.EmbedSpec (spec) where++import Data.Int+import Data.Ratio+import Data.Word+import Foreign.C.Types+import System.Command.QQ+import Test.Hspec+++spec :: Spec+spec =+ describe "embedding" $ do+ describe "Haskell types" $ do+ describe "numeric types" $ do+ it "embeds Integer" $+ embed (4 :: Integer) `shouldBe` "4"++ it "embeds Int" $+ embed (4 :: Int) `shouldBe` "4"++ it "embeds Int8" $+ embed (4 :: Int8) `shouldBe` "4"++ it "embeds Int16" $+ embed (4 :: Int16) `shouldBe` "4"++ it "embeds Int32" $+ embed (4 :: Int32) `shouldBe` "4"++ it "embeds Int64" $+ embed (4 :: Int64) `shouldBe` "4"++ it "embeds Word" $+ embed (4 :: Word) `shouldBe` "4"++ it "embeds Word8" $+ embed (4 :: Word8) `shouldBe` "4"++ it "embeds Word16" $+ embed (4 :: Word16) `shouldBe` "4"++ it "embeds Word32" $+ embed (4 :: Word32) `shouldBe` "4"++ it "embeds Word64" $+ embed (4 :: Word64) `shouldBe` "4"++ it "embeds Float" $+ embed (7 :: Float) `shouldBe` "7.0"++ it "embeds Double" $+ embed (7 :: Double) `shouldBe` "7.0"++ it "embeds Rational" $+ embed (3 % 5) `shouldBe` "0.6"++ it "embeds Char" $+ embed 'e' `shouldBe` "e"++ it "embeds String" $+ embed "foo" `shouldBe` "foo"++ describe "C types" $ do+ it "embeds CChar" $+ embed (4 :: CChar) `shouldBe` "4"++ it "embeds CSChar" $+ embed (4 :: CSChar) `shouldBe` "4"++ it "embeds CUChar" $+ embed (4 :: CUChar) `shouldBe` "4"++ it "embeds CShort" $+ embed (4 :: CShort) `shouldBe` "4"++ it "embeds CUShort" $+ embed (4 :: CUShort) `shouldBe` "4"++ it "embeds CInt" $+ embed (4 :: CInt) `shouldBe` "4"++ it "embeds CUInt" $+ embed (4 :: CUInt) `shouldBe` "4"++ it "embeds CLong" $+ embed (4 :: CLong) `shouldBe` "4"++ it "embeds CULong" $+ embed (4 :: CULong) `shouldBe` "4"++ it "embeds CSize" $+ embed (4 :: CSize) `shouldBe` "4"++ it "embeds CLLong" $+ embed (4 :: CLLong) `shouldBe` "4"++ it "embeds CULLong" $+ embed (4 :: CULLong) `shouldBe` "4"++ it "embeds CFloat" $+ embed (7 :: CFloat) `shouldBe` "7.0"++ it "embeds CDouble" $+ embed (7 :: CDouble) `shouldBe` "7.0"
+ test/System/Command/QQ/EvalSpec.hs view
@@ -0,0 +1,94 @@+{-# LANGUAGE ExtendedDefaultRules #-}+{-# LANGUAGE QuasiQuotes #-}+module System.Command.QQ.EvalSpec (spec) where++import Data.Text.Lazy (Text, pack)+import System.Command.QQ+import System.Exit (ExitCode(..))+import Test.Hspec+++spec :: Spec+spec = do+ describe "quasiquotation" $ do+ context "unicode" $ do+ it "works with unicode symbols in the String output" $+ [sh|echo -n "ДМИТРИЙ МАЛИКОВ"|] `shouldReturn` "ДМИТРИЙ МАЛИКОВ"++ it "works with unicode symbols in the Text output" $+ [sh|echo -n "ЕГОР ЛЕТОВ"|] `shouldReturn` text "ЕГОР ЛЕТОВ"++ context "exit code" $ do+ it "is possible to get successful exit code" $+ [sh|exit 0|] `shouldReturn` ExitSuccess++ it "is possible to get unsuccessful exit code" $+ [sh|exit 4|] `shouldReturn` ExitFailure 4++ it "is possible to get all of exitcode, stdout, and stderr" $+ [sh|echo -n hello; echo -n world >&2; exit 4|] `shouldReturn`+ (ExitFailure 4, text "hello", text "world")++ describe "embedding" $ do+ describe "variable embeddings" $ do++ it "can embed integers" $+ let foo = 7 in [sh|echo #{foo}|] `shouldReturn` "7\n"++ it "can embed doubles" $+ let foo = 7.0 in [sh|echo #{foo}|] `shouldReturn` "7.0\n"++ it "can embed characters" $+ let foo = 'z' in [sh|echo #{foo}|] `shouldReturn` "z\n"++ it "can embed strings" $+ let foo = "hello" in [sh|echo #{foo}|] `shouldReturn` "hello\n"++ describe "multi-line embeddings" $ do+ it "supports multiline commands" $+ [sh|+ echo hello+ echo world+ echo !!!+ |] `shouldReturn` "hello\nworld\n!!!\n"++ it "supports embeddings in multiline commands" $+ let foo = 4+ bar = 7+ in [sh|+ echo #{foo}+ echo #{bar}+ |] `shouldReturn` "4\n7\n"++ describe "escapings" $ do+ it "is possible to write #{} literally as a comment" $ do+ [sh|echo #\{foo}|] `shouldReturn` "\n"+ [sh|echo #\\{foo}|] `shouldReturn` "\n"++ it "is possible to write #{} literally as a string" $ do+ [sh|echo "#\{foo}"|] `shouldReturn` "#{foo}\n"+ [sh|echo "#\\{foo}"|] `shouldReturn` "#\\{foo}\n"++ describe "literals" $ do+ it "is possible to embed integer literals" $+ [sh|echo -n #{4}|] `shouldReturn` "4"++ it "is possible to embed rational literals" $+ [sh|echo -n #{4.0}|] `shouldReturn` "4.0"++ it "is possible to embed char literals" $+ [sh|echo -n #{'q'}|] `shouldReturn` "q"++ it "is possible to embed string literals" $+ [sh|echo -n #{"hello"}|] `shouldReturn` "hello"++ describe "custom data types" $+ it "is possible to embed custom data types" $+ [sh|echo -n hello#{Bang}|] `shouldReturn` "hello!"++data Bang = Bang++instance Embed Bang where embed Bang = "!"++text :: String -> Text+text = pack
− tests/Doctests.hs
@@ -1,13 +0,0 @@-module Main where--import Test.DocTest (doctest)---main :: IO ()-main = doctest- [ "src/System/Command/QQ.hs"- , "src/System/Command/QQ/Embed.hs"- , "src/System/Command/QQ/Eval.hs"- , "src/System/Command/QQ/Predef.hs"- , "examples/CommandT.hs"- ]
− tests/Spec.hs
@@ -1,1 +0,0 @@-{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
− tests/System/Command/QQ/EmbedSpec.hs
@@ -1,105 +0,0 @@-module System.Command.QQ.EmbedSpec (spec) where--import Data.Int-import Data.Ratio-import Data.Word-import Foreign.C.Types-import System.Command.QQ-import Test.Hspec---spec :: Spec-spec =- describe "embedding" $ do- describe "Haskell types" $ do- describe "numeric types" $ do- it "embeds Integer" $- embed (4 :: Integer) `shouldBe` "4"-- it "embeds Int" $- embed (4 :: Int) `shouldBe` "4"-- it "embeds Int8" $- embed (4 :: Int8) `shouldBe` "4"-- it "embeds Int16" $- embed (4 :: Int16) `shouldBe` "4"-- it "embeds Int32" $- embed (4 :: Int32) `shouldBe` "4"-- it "embeds Int64" $- embed (4 :: Int64) `shouldBe` "4"-- it "embeds Word" $- embed (4 :: Word) `shouldBe` "4"-- it "embeds Word8" $- embed (4 :: Word8) `shouldBe` "4"-- it "embeds Word16" $- embed (4 :: Word16) `shouldBe` "4"-- it "embeds Word32" $- embed (4 :: Word32) `shouldBe` "4"-- it "embeds Word64" $- embed (4 :: Word64) `shouldBe` "4"-- it "embeds Float" $- embed (7 :: Float) `shouldBe` "7.0"-- it "embeds Double" $- embed (7 :: Double) `shouldBe` "7.0"-- it "embeds Rational" $- embed (3 % 5) `shouldBe` "0.6"-- it "embeds Char" $- embed 'e' `shouldBe` "e"-- it "embeds String" $- embed "foo" `shouldBe` "foo"-- describe "C types" $ do- it "embeds CChar" $- embed (4 :: CChar) `shouldBe` "4"-- it "embeds CSChar" $- embed (4 :: CSChar) `shouldBe` "4"-- it "embeds CUChar" $- embed (4 :: CUChar) `shouldBe` "4"-- it "embeds CShort" $- embed (4 :: CShort) `shouldBe` "4"-- it "embeds CUShort" $- embed (4 :: CUShort) `shouldBe` "4"-- it "embeds CInt" $- embed (4 :: CInt) `shouldBe` "4"-- it "embeds CUInt" $- embed (4 :: CUInt) `shouldBe` "4"-- it "embeds CLong" $- embed (4 :: CLong) `shouldBe` "4"-- it "embeds CULong" $- embed (4 :: CULong) `shouldBe` "4"-- it "embeds CSize" $- embed (4 :: CSize) `shouldBe` "4"-- it "embeds CLLong" $- embed (4 :: CLLong) `shouldBe` "4"-- it "embeds CULLong" $- embed (4 :: CULLong) `shouldBe` "4"-- it "embeds CFloat" $- embed (7 :: CFloat) `shouldBe` "7.0"-- it "embeds CDouble" $- embed (7 :: CDouble) `shouldBe` "7.0"
− tests/System/Command/QQ/EvalSpec.hs
@@ -1,94 +0,0 @@-{-# LANGUAGE ExtendedDefaultRules #-}-{-# LANGUAGE QuasiQuotes #-}-module System.Command.QQ.EvalSpec (spec) where--import Data.Text.Lazy (Text, pack)-import System.Command.QQ-import System.Exit (ExitCode(..))-import Test.Hspec---spec :: Spec-spec = do- describe "quasiquotation" $ do- context "unicode" $ do- it "works with unicode symbols in the String output" $- [sh|echo -n "ДМИТРИЙ МАЛИКОВ"|] `shouldReturn` "ДМИТРИЙ МАЛИКОВ"-- it "works with unicode symbols in the Text output" $- [sh|echo -n "ЕГОР ЛЕТОВ"|] `shouldReturn` text "ЕГОР ЛЕТОВ"-- context "exit code" $ do- it "is possible to get successful exit code" $- [sh|exit 0|] `shouldReturn` ExitSuccess-- it "is possible to get unsuccessful exit code" $- [sh|exit 4|] `shouldReturn` ExitFailure 4-- it "is possible to get all of exitcode, stdout, and stderr" $- [sh|echo -n hello; echo -n world >&2; exit 4|] `shouldReturn`- (ExitFailure 4, text "hello", text "world")-- describe "embedding" $ do- describe "variable embeddings" $ do-- it "can embed integers" $- let foo = 7 in [sh|echo #{foo}|] `shouldReturn` "7\n"-- it "can embed doubles" $- let foo = 7.0 in [sh|echo #{foo}|] `shouldReturn` "7.0\n"-- it "can embed characters" $- let foo = 'z' in [sh|echo #{foo}|] `shouldReturn` "z\n"-- it "can embed strings" $- let foo = "hello" in [sh|echo #{foo}|] `shouldReturn` "hello\n"-- describe "multi-line embeddings" $ do- it "supports multiline commands" $- [sh|- echo hello- echo world- echo !!!- |] `shouldReturn` "hello\nworld\n!!!\n"-- it "supports embeddings in multiline commands" $- let foo = 4- bar = 7- in [sh|- echo #{foo}- echo #{bar}- |] `shouldReturn` "4\n7\n"-- describe "escapings" $ do- it "is possible to write #{} literally as a comment" $ do- [sh|echo #\{foo}|] `shouldReturn` "\n"- [sh|echo #\\{foo}|] `shouldReturn` "\n"-- it "is possible to write #{} literally as a string" $ do- [sh|echo "#\{foo}"|] `shouldReturn` "#{foo}\n"- [sh|echo "#\\{foo}"|] `shouldReturn` "#\\{foo}\n"-- describe "literals" $ do- it "is possible to embed integer literals" $- [sh|echo -n #{4}|] `shouldReturn` "4"-- it "is possible to embed rational literals" $- [sh|echo -n #{4.0}|] `shouldReturn` "4.0"-- it "is possible to embed char literals" $- [sh|echo -n #{'q'}|] `shouldReturn` "q"-- it "is possible to embed string literals" $- [sh|echo -n #{"hello"}|] `shouldReturn` "hello"-- describe "custom data types" $- it "is possible to embed custom data types" $- [sh|echo -n hello#{Bang}|] `shouldReturn` "hello!"--data Bang = Bang--instance Embed Bang where embed Bang = "!"--text :: String -> Text-text = pack