turtle 1.5.14 → 1.5.15
raw patch · 7 files changed
+213/−19 lines, 7 filesdep +faildep +streaming-commonsdep ~basedep ~textdep ~time
Dependencies added: fail, streaming-commons
Dependency ranges changed: base, text, time
Files
- CHANGELOG.md +8/−0
- src/Turtle/Bytes.hs +144/−0
- src/Turtle/Prelude.hs +41/−2
- src/Turtle/Shell.hs +4/−6
- src/Turtle/Tutorial.hs +4/−4
- test/cptree.hs +2/−1
- turtle.cabal +10/−6
CHANGELOG.md view
@@ -1,3 +1,11 @@+1.5.15++* Add `toLines`+* Add `Turtle.Bytes.{fromUTF8,toUTF8}`+* Add `Turtle.Bytes.{compress,decompress}`+* Always expose a `MonadFail` instance, relying on the `fail` package+ where needed. Related GHC 8.8 preparedness.+ 1.5.14 * Fix `cptree` to copy symlinks instead of descending into them
src/Turtle/Bytes.hs view
@@ -16,6 +16,12 @@ , append , stderr , strict+ , compress+ , decompress+ , WindowBits(..)+ , Zlib.defaultWindowBits+ , fromUTF8+ , toUTF8 -- * Subprocess management , proc@@ -43,7 +49,9 @@ import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.Managed (MonadManaged(..)) import Data.ByteString (ByteString)+import Data.Streaming.Zlib (Inflate, Popper, PopperRes(..), WindowBits(..)) import Data.Text (Text)+import Data.Text.Encoding (Decoding(..)) import Filesystem.Path (FilePath) import Prelude hiding (FilePath) import System.Exit (ExitCode(..))@@ -61,7 +69,10 @@ import qualified Control.Monad import qualified Control.Monad.Managed as Managed import qualified Data.ByteString+import qualified Data.Streaming.Zlib as Zlib import qualified Data.Text+import qualified Data.Text.Encoding as Encoding+import qualified Data.Text.Encoding.Error as Encoding.Error import qualified Foreign import qualified System.IO import qualified System.Process as Process@@ -657,3 +668,136 @@ -> Shell (Either ByteString ByteString) -- ^ Chunks of either output (`Right`) or error (`Left`) inshellWithErr cmd = streamWithErr (Process.shell (Data.Text.unpack cmd))++-- | Internal utility used by both `compress` and `decompress`+fromPopper :: Popper -> Shell ByteString+fromPopper popper = loop+ where+ loop = do+ result <- liftIO popper++ case result of+ PRDone ->+ empty+ PRNext compressedByteString ->+ return compressedByteString <|> loop+ PRError exception ->+ liftIO (Exception.throwIO exception)++{-| Compress a stream using @zlib@++ Note that this can decompress streams that are the concatenation of+ multiple compressed streams (just like @gzip@)++>>> let compressed = select [ "ABC", "DEF" ] & compress 0 defaultWindowBits+>>> compressed & decompress defaultWindowBits & view+"ABCDEF"+>>> (compressed <|> compressed) & decompress defaultWindowBits & view+"ABCDEF"+"ABCDEF"+-}+compress+ :: Int+ -- ^ Compression level+ -> WindowBits+ -- ^+ -> Shell ByteString+ -- ^+ -> Shell ByteString+compress compressionLevel windowBits bytestrings = do+ deflate <- liftIO (Zlib.initDeflate compressionLevel windowBits)++ let loop = do+ bytestring <- bytestrings++ popper <- liftIO (Zlib.feedDeflate deflate bytestring)++ fromPopper popper++ let wrapUp = do+ let popper = liftIO (Zlib.finishDeflate deflate)++ fromPopper popper++ loop <|> wrapUp++data DecompressionState = Uninitialized | Decompressing Inflate++-- | Decompress a stream using @zlib@ (just like the @gzip@ command)+decompress :: WindowBits -> Shell ByteString -> Shell ByteString+decompress windowBits (Shell k) = Shell k'+ where+ k' (FoldShell step begin done) = k (FoldShell step' begin' done')+ where+ begin' = (begin, Uninitialized)++ step' (x0, Uninitialized) compressedByteString = do+ inflate <- Zlib.initInflate windowBits++ step' (x0, Decompressing inflate) compressedByteString+ step' (x0, Decompressing inflate) compressedByteString = do+ popper <- Zlib.feedInflate inflate compressedByteString++ let loop x = do+ result <- popper++ case result of+ PRDone -> do+ compressedByteString' <- Zlib.getUnusedInflate inflate++ if Data.ByteString.null compressedByteString'+ then return (x, Decompressing inflate)+ else do+ decompressedByteString <- Zlib.finishInflate inflate++ x' <- step x decompressedByteString++ step' (x', Uninitialized) compressedByteString'+ PRNext decompressedByteString -> do+ x' <- step x decompressedByteString++ loop x'+ PRError exception -> do+ Exception.throwIO exception++ loop x0++ done' (x0, Uninitialized) = do+ done x0+ done' (x0, Decompressing inflate) = do+ decompressedByteString <- Zlib.finishInflate inflate++ x0' <- step x0 decompressedByteString++ done' (x0', Uninitialized)++{-| Decode a stream of bytes as UTF8 `Text`++ NOTE: This function will throw a pure exception (i.e. an `error`) if UTF8+ decoding fails (mainly due to limitations in the @text@ package's stream+ decoding API)+-}+toUTF8 :: Shell ByteString -> Shell Text+toUTF8 (Shell k) = Shell k'+ where+ k' (FoldShell step begin done) =+ k (FoldShell step' begin' done')+ where+ begin' =+ (mempty, Encoding.streamDecodeUtf8With Encoding.Error.strictDecode, begin)++ step' (prefix, decoder, x) suffix = do+ let bytes = prefix <> suffix++ let Some text prefix' decoder' = decoder bytes ++ x' <- step x text++ return (prefix', decoder', x')++ done' (_, _, x) = do+ done x++-- | Encode a stream of bytes as UTF8 `Text`+fromUTF8 :: Shell Text -> Shell ByteString+fromUTF8 = fmap Encoding.encodeUtf8
src/Turtle/Prelude.hs view
@@ -206,6 +206,7 @@ , sort , sortOn , sortBy+ , toLines -- * Folds , countChars@@ -300,7 +301,7 @@ import Control.Foldl (Fold(..), genericLength, handles, list, premap) import qualified Control.Foldl import qualified Control.Foldl.Text-import Control.Monad (guard, liftM, msum, when, unless, (>=>), mfilter)+import Control.Monad (foldM, guard, liftM, msum, when, unless, (>=>), mfilter) import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.Managed (MonadManaged(..), managed, managed_, runManaged) #ifdef mingw32_HOST_OS@@ -308,6 +309,8 @@ #endif import Data.IORef (newIORef, readIORef, writeIORef) import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty(..))+import qualified Data.List.NonEmpty as NonEmpty import Data.Monoid ((<>)) import Data.Ord (comparing) import qualified Data.Set as Set@@ -354,7 +357,7 @@ touchFile ) import System.Posix.Files (createSymbolicLink) #endif-import Prelude hiding (FilePath)+import Prelude hiding (FilePath, lines) import Turtle.Pattern (Pattern, anyChar, chars, match, selfless, sepBy) import Turtle.Shell@@ -2166,3 +2169,39 @@ -- [(1,'a'),(2,'c'),(3,'d'),(3,'e'),(4,'b'),(7,'f')] sortBy :: (Functor io, MonadIO io) => (a -> a -> Ordering) -> Shell a -> io [a] sortBy f s = List.sortBy f <$> fold s list++{-| Group an arbitrary stream of `Text` into newline-delimited `Line`s++>>> stdout (toLines ("ABC" <|> "DEF" <|> "GHI")+ABCDEFGHI+>>> stdout (toLines empty) -- Note that this always emits at least 1 `Line`++>>> stdout (toLines ("ABC\nDEF" <|> "" <|> "GHI\nJKL"))+ABC+DEFGHI+JKL+-}+toLines :: Shell Text -> Shell Line+toLines (Shell k) = Shell k'+ where+ k' (FoldShell step begin done) =+ k (FoldShell step' begin' done')+ where+ step' (Pair x prefix) text = do+ let suffix :| lines = Turtle.Line.textToLines text++ let line = prefix <> suffix++ let lines' = line :| lines++ x' <- foldM step x (NonEmpty.init lines')++ let prefix' = NonEmpty.last lines'++ return (Pair x' prefix')++ begin' = (Pair begin "")++ done' (Pair x prefix) = do+ x' <- step x prefix+ done x'
src/Turtle/Shell.hs view
@@ -81,9 +81,7 @@ import Control.Monad.Catch (MonadThrow(..), MonadCatch(..)) import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.Managed (MonadManaged(..), with)-#if MIN_VERSION_base(4,9,0) import qualified Control.Monad.Fail as Fail-#endif import Control.Foldl (Fold(..), FoldM(..)) import qualified Control.Foldl as Foldl import Data.Foldable (Foldable)@@ -182,7 +180,9 @@ let step1 x a = _foldShell (f a) (FoldShell step0 x return) _foldShell m (FoldShell step1 begin0 done0) ) - fail _ = mzero+#if!(MIN_VERSION_base(4,13,0))+ fail = Fail.fail+#endif instance Alternative Shell where empty = Shell (\(FoldShell _ begin done) -> done begin)@@ -213,10 +213,8 @@ instance MonadCatch Shell where m `catch` k = Shell (\f-> _foldShell m f `catch` (\e -> _foldShell (k e) f)) -#if MIN_VERSION_base(4,9,0) instance Fail.MonadFail Shell where- fail = Prelude.fail-#endif+ fail _ = mzero #if __GLASGOW_HASKELL__ >= 804 instance Monoid a => Semigroup (Shell a) where
src/Turtle/Tutorial.hs view
@@ -1490,10 +1490,10 @@ -- $monadio ----- If you are sick of having type `liftIO` everywhere, you can omit it. This--- is because all subroutines in @turtle@ are overloaded using the `MonadIO`--- type class, like our original `pwd` command where we first encountered the--- the `MonadIO` type:+-- If you are sick of having to type `liftIO` everywhere, you can omit it.+-- This is because all subroutines in @turtle@ are overloaded using the+-- `MonadIO` type class, like our original `pwd` command where we first+-- encountered the the `MonadIO` type: -- -- @ -- Prelude Turtle> :type pwd
test/cptree.hs view
@@ -3,10 +3,11 @@ import Turtle import Filesystem.Path.CurrentOS () import System.IO.Temp (withSystemTempDirectory)+import qualified Control.Monad.Fail as Fail import Control.Monad (unless) check :: String -> Bool-> IO ()-check errorMessage successs = unless successs $ fail errorMessage+check errorMessage successs = unless successs $ Fail.fail errorMessage main :: IO () main = withSystemTempDirectory "tempDir" (runTest . fromString)
turtle.cabal view
@@ -1,5 +1,5 @@ Name: turtle-Version: 1.5.14+Version: 1.5.15 Cabal-Version: >=1.10 Build-Type: Simple License: BSD3@@ -53,7 +53,7 @@ ansi-wl-pprint >= 0.6 && < 0.7 , async >= 2.0.0.0 && < 2.3 , bytestring >= 0.9.1.8 && < 0.11,- clock >= 0.4.1.2 && < 0.8 ,+ clock >= 0.4.1.2 && < 0.9 , containers >= 0.5.0.0 && < 0.7 , directory >= 1.0.7 && < 1.4 , exceptions >= 0.4 && < 0.11,@@ -61,21 +61,24 @@ hostname < 1.1 , managed >= 1.0.3 && < 1.1 , process >= 1.0.1.1 && < 1.7 ,- semigroups >= 0.5.0 && < 0.19,+ semigroups >= 0.5.0 && < 0.20, system-filepath >= 0.3.1 && < 0.5 , system-fileio >= 0.2.1 && < 0.4 , stm < 2.6 ,+ streaming-commons < 0.3 , temporary < 1.4 ,- text < 1.3 ,- time < 1.9 ,+ text >= 1.0.0 && < 1.3 ,+ time < 1.10, transformers >= 0.2.0.0 && < 0.6 ,- optparse-applicative >= 0.13 && < 0.15,+ optparse-applicative >= 0.13 && < 0.16, optional-args >= 1.0 && < 2.0 , unix-compat >= 0.4 && < 0.6 if os(windows) Build-Depends: Win32 >= 2.2.0.1 && < 2.9 else Build-Depends: unix >= 2.5.1.0 && < 2.8+ if !impl(ghc >= 8.0)+ Build-Depends: fail >= 4.9.0.0 && < 4.10 Exposed-Modules: Turtle, Turtle.Bytes,@@ -129,6 +132,7 @@ Default-Language: Haskell2010 Build-Depends: base >= 4 && < 5,+ fail, temporary, system-filepath >= 0.4, turtle