silently 1.1.5 → 1.2
raw patch · 6 files changed
+133/−224 lines, 6 filesdep +deepseqdep ~base
Dependencies added: deepseq
Dependency ranges changed: base
Files
- silently.cabal +16/−14
- src/System/IO/Silently.hs +88/−0
- src/other/System/IO/Silently.hs +0/−76
- src/unix/System/IO/Silently.hs +0/−65
- src/windows/System/IO/Silently.hs +0/−69
- test/Spec.hs +29/−0
silently.cabal view
@@ -1,6 +1,6 @@ name: silently-version: 1.1.5-cabal-version: >= 1.2+version: 1.2+cabal-version: >= 1.8 build-type: Simple license: BSD3 license-file: LICENSE@@ -15,22 +15,24 @@ category: author: Trystan Spangler tested-with: GHC ==7.0-extra-source-files:- src/windows/System/IO/Silently.hs- src/unix/System/IO/Silently.hs- src/other/System/IO/Silently.hs +source-repository head+ type: git+ location: https://github.com/trystan/silently+ Library- build-depends: base >=4 && <=5, directory -any+ build-depends:+ base >=4 && <=5+ , directory+ , deepseq exposed-modules: System.IO.Silently exposed: True buildable: True - if os(windows)- hs-source-dirs: src/windows- else- if os(linux) || os(osx) || os(freebsd) || os(openbsd) || os(netbsd)- hs-source-dirs: src/unix- else- hs-source-dirs: src/other+ hs-source-dirs:+ src + if os(windows)+ cpp-options: -DWINDOWS+ if os(linux) || os(osx) || os(freebsd) || os(openbsd) || os(netbsd)+ cpp-options: -DUNIX
+ src/System/IO/Silently.hs view
@@ -0,0 +1,88 @@+{-# LANGUAGE CPP #-}+-- | Need to prevent output to the terminal, a file, or stderr? Need to capture it and use it for+-- your own means? Now you can, with 'silence' and 'capture'.++module System.IO.Silently (+ silence, hSilence,+ capture, hCapture+) where++import GHC.IO.Handle (hDuplicate, hDuplicateTo)+import System.IO+import System.IO.Error+import Control.Exception (bracket)+import Control.DeepSeq+import System.Directory (removeFile,getTemporaryDirectory)++mNullDevice :: Maybe FilePath+#ifdef WINDOWS+mNullDevice = Just "NUL"+#elif UNIX+mNullDevice = Just "/dev/null"+#else+mNullDevice = Nothing+#endif++-- | Run an IO action while preventing all output to stdout.+silence :: IO a -> IO a+silence = hSilence [stdout]++-- | Run an IO action while preventing all output to the given handles.+hSilence :: [Handle] -> IO a -> IO a+hSilence handles action = case mNullDevice of+ Just nullDevice -> bracket (openFile nullDevice AppendMode)+ hClose+ prepareAndRun++ Nothing -> do+ tmpDir <- getTempOrCurrentDirectory+ bracket (openTempFile tmpDir "silence")+ cleanup+ (prepareAndRun . snd)++ where+ cleanup (tmpFile,tmpHandle) = do+ hClose tmpHandle+ removeFile tmpFile+ prepareAndRun tmpHandle = go handles+ where+ go [] = action+ go hs = goBracket go tmpHandle hs+++getTempOrCurrentDirectory :: IO String+getTempOrCurrentDirectory = getTemporaryDirectory `catchIOError` (\_ -> return ".")++-- | Run an IO action while preventing and capturing all output to stdout.+-- This will, as a side effect, create and delete a temp file in the temp directory or current directory if there is no temp directory.+capture :: IO a -> IO (String, a)+capture = hCapture [stdout]++-- | Run an IO action while preventing and capturing all output to the given handles.+-- This will, as a side effect, create and delete a temp file in the temp directory or current directory if there is no temp directory.+hCapture :: [Handle] -> IO a -> IO (String, a)+hCapture handles action = do+ tmpDir <- getTempOrCurrentDirectory+ bracket (openTempFile tmpDir "capture")+ cleanup+ (prepareAndRun . snd)+ where+ cleanup (tmpFile,tmpHandle) = do+ hClose tmpHandle+ removeFile tmpFile+ prepareAndRun tmpHandle = go handles+ where+ go [] = do+ a <- action+ mapM_ hFlush handles+ hSeek tmpHandle AbsoluteSeek 0+ str <- hGetContents tmpHandle+ str `deepseq` return (str,a)+ go hs = goBracket go tmpHandle hs++goBracket :: ([Handle] -> IO a) -> Handle -> [Handle] -> IO a+goBracket go tmpHandle (h:hs) = bracket (do old <- hDuplicate h+ hDuplicateTo tmpHandle h+ return old)+ (\old -> hDuplicateTo old h >> hClose old)+ (\_ -> go hs)
− src/other/System/IO/Silently.hs
@@ -1,76 +0,0 @@---- | Need to prevent output to the terminal, a file, or stderr? Need to capture it and use it for--- your own means? Now you can, with 'silence' and 'capture'.--module System.IO.Silently (- silence, hSilence,- capture, hCapture-) where--import GHC.IO.Handle (hDuplicate, hDuplicateTo)-import System.IO (Handle, stdout, hClose, openTempFile)-import Control.Exception (bracket)-import System.Directory (removeFile,getTemporaryDirectory)---- | Run an IO action while preventing all output to stdout.--- This will, as a side effect, create and delete a temp file in the current directory.-silence :: IO a -> IO a-silence = hSilence [stdout]---- | Run an IO action while preventing all output to the given handles.--- This will, as a side effect, create and delete a temp file in the current directory.-hSilence :: [Handle] -> IO a -> IO a-hSilence handles action = do- tmpDir <- getTempOrCurrentDirectory- bracket (openTempFile tmpDir "silence")- cleanup- prepareAndRun- where- cleanup (tmpFile,tmpHandle) = do- hClose tmpHandle- removeFile tmpFile- prepareAndRun (_,tmpHandle) = go handles- where- go [] = action- go hs = goBracket go tmpHandle hs---getTempOrCurrentDirectory :: IO String-getTempOrCurrentDirectory = getTemporaryDirectory `Prelude.catch` (\ex -> return ".")---- | Run an IO action while preventing and capturing all output to stdout.--- This will, as a side effect, create and delete a temp file in the temp directory or current directory if there is no temp directory.-capture :: IO a -> IO (String, a)-capture = hCapture [stdout]---- | Run an IO action while preventing and capturing all output to the given handles.--- This will, as a side effect, create and delete a temp file in the temp directory or current directory if there is no temp directory.-hCapture :: [Handle] -> IO a -> IO (String, a)-hCapture handles action = do- tmpDir <- getTempOrCurrentDirectory- bracket (openTempFile tmpDir "capture")- cleanup- prepareAndRun- where- cleanup (tmpFile,tmpHandle) = do- hClose tmpHandle- removeFile tmpFile- prepareAndRun (tmpFile,tmpHandle) = go handles- where- go [] = do- a <- action- hClose tmpHandle- str <- readFile tmpFile- forceList str- return (str,a)- go hs = goBracket go tmpHandle hs--forceList [] = return ()-forceList (x:xs) = forceList xs--goBracket :: ([Handle] -> IO a) -> Handle -> [Handle] -> IO a-goBracket go tmpHandle (h:hs) = bracket (do old <- hDuplicate h- hDuplicateTo tmpHandle h- return old)- (\old -> hDuplicateTo old h >> hClose old)- (\_ -> go hs)
− src/unix/System/IO/Silently.hs
@@ -1,65 +0,0 @@---- | Need to prevent output to the terminal, a file, or stderr? Need to capture it and use it for--- your own means? Now you can, with 'silence' and 'capture'.--module System.IO.Silently (- silence, hSilence,- capture, hCapture-) where--import GHC.IO.Handle (hDuplicate, hDuplicateTo)-import System.IO (Handle, stdout, hClose, openTempFile, openFile, IOMode(..))-import Control.Exception (bracket)-import System.Directory (removeFile,getTemporaryDirectory)---- | Run an IO action while preventing all output to stdout.-silence :: IO a -> IO a-silence = hSilence [stdout]---- | Run an IO action while preventing all output to the given handles.-hSilence :: [Handle] -> IO a -> IO a-hSilence handles action = bracket (openFile "/dev/null" AppendMode)- hClose- prepareAndRun- where- prepareAndRun tmpHandle = go handles- where- go [] = action- go hs = goBracket go tmpHandle hs---getTempOrCurrentDirectory :: IO String-getTempOrCurrentDirectory = getTemporaryDirectory `Prelude.catch` (\ex -> return ".")---- | Run an IO action while preventing and capturing all output to stdout.--- This will, as a side effect, create and delete a temp file in the temp directory or current directory if there is no temp directory.-capture :: IO a -> IO (String, a)-capture = hCapture [stdout]---- | Run an IO action while preventing and capturing all output to the given handles.--- This will, as a side effect, create and delete a temp file in the temp directory or current directory if there is no temp directory.-hCapture :: [Handle] -> IO a -> IO (String, a)-hCapture handles action = do- tmpDir <- getTempOrCurrentDirectory- bracket (openTempFile tmpDir "capture")- cleanup- prepareAndRun- where- cleanup (tmpFile,tmpHandle) = do- hClose tmpHandle- removeFile tmpFile- prepareAndRun (tmpFile,tmpHandle) = go handles- where- go [] = do- a <- action- hClose tmpHandle- str <- readFile tmpFile- return (str,a)- go hs = goBracket go tmpHandle hs--goBracket :: ([Handle] -> IO a) -> Handle -> [Handle] -> IO a-goBracket go tmpHandle (h:hs) = bracket (do old <- hDuplicate h- hDuplicateTo tmpHandle h- return old)- (\old -> hDuplicateTo old h >> hClose old)- (\_ -> go hs)
− src/windows/System/IO/Silently.hs
@@ -1,69 +0,0 @@---- | Need to prevent output to the terminal, a file, or stderr? Need to capture it and use it for--- your own means? Now you can, with 'silence' and 'capture'.--module System.IO.Silently (- silence, hSilence,- capture, hCapture-) where--import GHC.IO.Handle (hDuplicate, hDuplicateTo)-import System.IO (Handle, stdout, hClose, openTempFile, openFile, IOMode(..))-import Control.Exception (bracket)-import System.Directory (removeFile,getTemporaryDirectory)---- | Run an IO action while preventing all output to stdout.-silence :: IO a -> IO a-silence = hSilence [stdout]---- | Run an IO action while preventing all output to the given handles.-hSilence :: [Handle] -> IO a -> IO a-hSilence handles action = bracket (openFile "NUL" AppendMode)- hClose- prepareAndRun- where- prepareAndRun tmpHandle = go handles- where- go [] = action- go hs = goBracket go tmpHandle hs---getTempOrCurrentDirectory :: IO String-getTempOrCurrentDirectory = getTemporaryDirectory `Prelude.catch` (\ex -> return ".")---- | Run an IO action while preventing and capturing all output to stdout.--- This will, as a side effect, create and delete a temp file in the temp directory or current directory if there is no temp directory.-capture :: IO a -> IO (String, a)-capture = hCapture [stdout]---- | Run an IO action while preventing and capturing all output to the given handles.--- This will, as a side effect, create and delete a temp file in the temp directory or current directory if there is no temp directory.-hCapture :: [Handle] -> IO a -> IO (String, a)-hCapture handles action = do- tmpDir <- getTempOrCurrentDirectory- bracket (openTempFile tmpDir "capture")- cleanup- prepareAndRun- where- cleanup (tmpFile,tmpHandle) = do- hClose tmpHandle- removeFile tmpFile- prepareAndRun (tmpFile,tmpHandle) = go handles- where- go [] = do- a <- action- hClose tmpHandle- str <- readFile tmpFile- forceList str- return (str,a)- go hs = goBracket go tmpHandle hs--forceList [] = return ()-forceList (x:xs) = forceList xs--goBracket :: ([Handle] -> IO a) -> Handle -> [Handle] -> IO a-goBracket go tmpHandle (h:hs) = bracket (do old <- hDuplicate h- hDuplicateTo tmpHandle h- return old)- (\old -> hDuplicateTo old h >> hClose old)- (\_ -> go hs)
+ test/Spec.hs view
@@ -0,0 +1,29 @@+module Main (main) where++import Test.Hspec.ShouldBe++import System.IO+import System.IO.Silently+import System.Directory++import Control.Exception++main :: IO ()+main = hspec spec++spec :: Spec+spec = do++ describe "hSilence" $ do+ it "prevents output to a given handle" $ let file = "test/foo.txt" in do+ h <- openFile file ReadWriteMode+ hSilence [h] $ do+ hPutStrLn h "foo bar baz"+ hFlush h+ hSeek h AbsoluteSeek 0+ hGetContents h `shouldReturn` ""+ `finally` removeFile file++ describe "capture" $ do+ it "captures stdout" $ do+ capture (putStr "foo" >> return 23) `shouldReturn` ("foo", 23 :: Int)