turtle 1.3.2 → 1.3.3
raw patch · 8 files changed
+121/−21 lines, 8 filesdep +semigroups
Dependencies added: semigroups
Files
- CHANGELOG.md +11/−0
- src/Turtle/Bytes.hs +23/−1
- src/Turtle/Line.hs +8/−6
- src/Turtle/Prelude.hs +58/−3
- src/Turtle/Shell.hs +4/−2
- src/Turtle/Tutorial.hs +15/−6
- test/RegressionMaskingException.hs +0/−2
- turtle.cabal +2/−1
CHANGELOG.md view
@@ -1,3 +1,14 @@+1.3.3++* Bug fix: Change `textToLines` to behave like `Data.Text.splitOn "\n"`+ instead of `Data.Text.unlines`+ * This fixes weird behavior around handling empty strings. `splitOn` does+ the right thing, but `unlines` does not. For example, this indirectly+ fixes a regression in `sed`, which would discard empty lines+* Bug fix: `which`/`whichAll` now behave correctly on Windows+* Add new `cptree`/`single` utilities+* Documentation fixes+ 1.3.2 * Fix bugs in subprocess management
src/Turtle/Bytes.hs view
@@ -20,7 +20,6 @@ -- * Subprocess management , proc , shell- , system , procs , shells , inproc@@ -31,6 +30,12 @@ , shellStrict , procStrictWithErr , shellStrictWithErr++ , system+ , stream+ , streamWithErr+ , systemStrict+ , systemStrictWithErr ) where import Control.Applicative@@ -357,6 +362,10 @@ Exception.bracket open close' handle ) +{-| `systemStrict` generalizes `shellStrict` and `procStrict` by allowing you to+ supply your own custom `CreateProcess`. This is for advanced users who feel+ comfortable using the lower-level @process@ API+-} systemStrict :: MonadIO io => Process.CreateProcess@@ -397,6 +406,11 @@ restore (Process.waitForProcess ph) <* halt a ) )) (Data.ByteString.hGetContents hOut) ) ) +{-| `systemStrictWithErr` generalizes `shellStrictWithErr` and+ `procStrictWithErr` by allowing you to supply your own custom+ `CreateProcess`. This is for advanced users who feel comfortable using+ the lower-level @process@ API+-} systemStrictWithErr :: MonadIO io => Process.CreateProcess@@ -471,6 +485,10 @@ -- ^ Chunks of bytes read from process output inshell cmd = stream (Process.shell (Data.Text.unpack cmd)) +{-| `stream` generalizes `inproc` and `inshell` by allowing you to supply your+ own custom `CreateProcess`. This is for advanced users who feel comfortable+ using the lower-level @process@ API+-} stream :: Process.CreateProcess -- ^ Command@@ -511,6 +529,10 @@ Async.withAsync (feedIn restore) k ) )) inhandle hOut <|> (liftIO (Process.waitForProcess ph *> halt a) *> empty) +{-| `streamWithErr` generalizes `inprocWithErr` and `inshellWithErr` by allowing+ you to supply your own custom `CreateProcess`. This is for advanced users+ who feel comfortable using the lower-level @process@ API+-} streamWithErr :: Process.CreateProcess -- ^ Command
src/Turtle/Line.hs view
@@ -18,6 +18,7 @@ #if __GLASGOW_HASKELL__ >= 708 import Data.Coerce #endif+import Data.List.NonEmpty (NonEmpty(..)) import Data.String #if __GLASGOW_HASKELL__ >= 710 #else@@ -27,6 +28,8 @@ import Data.Typeable import Control.Exception +import qualified Data.List.NonEmpty+ -- | The `NewlineForbidden` exception is thrown when you construct a `Line` -- using an overloaded string literal or by calling `fromString` explicitly -- and the supplied string contains newlines. This is a programming error to@@ -67,12 +70,12 @@ lineToText (Line t) = t -- | Split text into lines. The inverse of `linesToText`.-textToLines :: Text -> [Line]+textToLines :: Text -> NonEmpty Line textToLines = #if __GLASGOW_HASKELL__ >= 708- coerce Text.lines+ Data.List.NonEmpty.fromList . coerce (Text.splitOn "\n") #else- map unsafeTextToLine . Text.lines+ Data.List.NonEmpty.fromList . map unsafeTextToLine . Text.splitOn "\n" #endif -- | Merge lines into a single text value.@@ -89,9 +92,8 @@ textToLine :: Text -> Maybe Line textToLine = fromSingleton . textToLines where- fromSingleton [] = Just (Line "")- fromSingleton [a] = Just a- fromSingleton _ = Nothing+ fromSingleton (a :| []) = Just a+ fromSingleton _ = Nothing -- | Convert a text value into a line. -- Precondition (unchecked): the argument does not contain newlines.
src/Turtle/Prelude.hs view
@@ -125,6 +125,7 @@ , mkdir , mktree , cp+ , cptree , rm , rmdir , rmtree@@ -182,6 +183,7 @@ , limitWhile , cache , parallel+ , single -- * Folds , countChars@@ -194,7 +196,6 @@ -- * Subprocess management , proc , shell- , system , procs , shells , inproc@@ -206,6 +207,12 @@ , procStrictWithErr , shellStrictWithErr + , system+ , stream+ , streamWithErr+ , systemStrict+ , systemStrictWithErr+ -- * Permissions , Permissions , chmod@@ -544,6 +551,11 @@ bracket open close' handle ) ++{-| `systemStrict` generalizes `shellStrict` and `procStrict` by allowing you to+ supply your own custom `CreateProcess`. This is for advanced users who feel+ comfortable using the lower-level @process@ API+-} systemStrict :: MonadIO io => Process.CreateProcess@@ -582,6 +594,11 @@ restore (liftIO (Process.waitForProcess ph)) `finally` halt a ) )) (Text.hGetContents hOut) ) ) +{-| `systemStrictWithErr` generalizes `shellStrictWithErr` and+ `procStrictWithErr` by allowing you to supply your own custom+ `CreateProcess`. This is for advanced users who feel comfortable using+ the lower-level @process@ API+-} systemStrictWithErr :: MonadIO io => Process.CreateProcess@@ -652,6 +669,10 @@ -- ^ Lines of standard output inshell cmd = stream (Process.shell (unpack cmd)) +{-| `stream` generalizes `inproc` and `inshell` by allowing you to supply your+ own custom `CreateProcess`. This is for advanced users who feel comfortable+ using the lower-level @process@ API+-} stream :: Process.CreateProcess -- ^ Command@@ -687,6 +708,10 @@ mask (\restore -> withAsync (feedIn restore) (restore . k)))) inhandle hOut <|> (liftIO (Process.waitForProcess ph *> halt a) *> empty) +{-| `streamWithErr` generalizes `inprocWithErr` and `inshellWithErr` by allowing+ you to supply your own custom `CreateProcess`. This is for advanced users+ who feel comfortable using the lower-level @process@ API+-} streamWithErr :: Process.CreateProcess -- ^ Command@@ -999,6 +1024,21 @@ cp :: MonadIO io => FilePath -> FilePath -> io () cp oldPath newPath = liftIO (Filesystem.copyFile oldPath newPath) +-- | Copy a directory tree+cptree :: MonadIO io => FilePath -> FilePath -> io ()+cptree oldTree newTree = sh (do+ oldPath <- lstree oldTree+ -- The `system-filepath` library treats a path like "/tmp" as a file and not+ -- a directory and fails to strip it as a prefix from `/tmp/foo`. Adding+ -- `(</> "")` to the end of the path makes clear that the path is a+ -- directory+ Just suffix <- return (Filesystem.stripPrefix (oldTree </> "") oldPath)+ let newPath = newTree </> suffix+ isFile <- testfile newPath+ if isFile+ then cp oldPath newPath+ else mktree newPath )+ -- | Remove a file rm :: MonadIO io => FilePath -> io () rm path = liftIO (Filesystem.removeFile path)@@ -1211,8 +1251,8 @@ whichAll :: FilePath -> Shell FilePath whichAll cmd = do Just paths <- need "PATH"- path <- select (Text.split (== ':') paths)- let path' = Filesystem.fromText path </> cmd+ path <- select (Filesystem.splitSearchPathString . Text.unpack $ paths)+ let path' = path </> cmd True <- testfile path' @@ -1825,3 +1865,18 @@ return (Pair x Nothing) done' (Pair x _) = done x++-- | Returns the result of a 'Shell' that outputs a single+-- line:+--+-- > main = do+-- > Just directory <- single (inshell "pwd" empty)+-- > print directory+single :: MonadIO io => Shell a -> io (Maybe a)+single s = do+ as <- fold s Control.Foldl.list+ case as of+ [a] -> return (Just a)+ _ -> do+ let msg = format ("single: expected 1 line of input but there were "%d%" lines of input") (length as)+ die msg
src/Turtle/Shell.hs view
@@ -76,6 +76,8 @@ import Control.Monad.Managed (MonadManaged(..), with) import Control.Foldl (Fold(..), FoldM(..)) import qualified Control.Foldl as Foldl+import Data.Foldable (Foldable)+import qualified Data.Foldable import Data.Monoid import Data.String (IsString(..)) import Prelude -- Fix redundant import warnings@@ -164,10 +166,10 @@ fromString str = pure (fromString str) -- | Convert a list to a `Shell` that emits each element of the list-select :: [a] -> Shell a+select :: Foldable f => f a -> Shell a select as = Shell (\(FoldM step begin done) -> do x0 <- begin let step' a k x = do x' <- step x a k $! x'- foldr step' done as $! x0 )+ Data.Foldable.foldr step' done as $! x0 )
src/Turtle/Tutorial.hs view
@@ -448,14 +448,21 @@ -- -- @ -- Prelude Turtle> :type echo--- echo :: `Text` -> `IO` ()+-- echo :: `Line` -> `IO` () -- @ -- -- The above type says that `echo` is a function whose argument is a value of--- type `Text` and whose result is a subroutine (`IO`) with an empty return+-- type `Line` and whose result is a subroutine (`IO`) with an empty return -- value (denoted @\'()\'@). ----- Now we can understand the type error: `echo` expects a `Text` argument but+-- `Line` is a wrapper around `Text` and represents a `Text` value with no+-- internal newlines:+--+-- @+-- newtype `Line` = `Line` `Text`+-- @+--+-- Now we can understand the type error: `echo` expects a `Line` argument but -- `datefile` returns a `UTCTime`, which is not the same thing. Unlike Bash, -- not everything is `Text` in Haskell and the compiler will not cast or coerce -- types for you.@@ -474,16 +481,18 @@ -- `print`. -- -- This library provides a helper function that lets you convert any type that--- implements `Show` into a `Text` value:+-- implements `Show` into any other type that implements `IsString`: -- -- @ -- \-\- This behaves like Python's \`repr\` function--- `repr` :: `Show` a => a -> `Text`+-- `repr` :: (`Show` a, `IsString` b) => a -> b -- @ -- -- You could therefore implement `print` in terms of `echo` and `repr`: -- -- > print x = echo (repr x)+--+-- ... which works because `Line` implements `IsString` -- $shell --@@ -492,7 +501,7 @@ -- @turtle@: -- -- @--- $ stack ghci+-- \$ stack ghci -- Prelude> :set -XOverloadedStrings -- Prelude> import Turtle -- Prelude Turtle> `cd` \"/tmp\"
test/RegressionMaskingException.hs view
@@ -2,8 +2,6 @@ import Turtle -import qualified System.Timeout- -- This test fails by hanging main :: IO () main = runManaged (do
turtle.cabal view
@@ -1,5 +1,5 @@ Name: turtle-Version: 1.3.2+Version: 1.3.3 Cabal-Version: >=1.10 Build-Type: Simple License: BSD3@@ -59,6 +59,7 @@ hostname < 1.1 , managed >= 1.0.3 && < 1.1 , process >= 1.0.1.1 && < 1.5 ,+ semigroups >= 0.5.0 && < 0.19, system-filepath >= 0.3.1 && < 0.5 , system-fileio >= 0.2.1 && < 0.4 , stm < 2.5 ,