From 5b98b490bcc87b97d75d98ad7dccb503c8a47aa6 Mon Sep 17 00:00:00 2001 From: Kirill Elagin Date: Sun, 16 Feb 2020 21:49:07 -0500 Subject: [PATCH] Implement hWithEncoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function allows one to not only safely set the best encoding on a file descriptor, but also restore the previous one. The idea is that it will be used on file descriptors that come from the outside and so we don’t know how they were used and will be used after us. --- CHANGELOG.md | 1 + README.md | 11 +++++------ lib/System/IO/Utf8.hs | 17 +++++++++++------ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5fffe1..dbe664e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Initial release. - `withUtf8StdHandles` - `hSetEncoding` +- `hWithEncoding` - `openFile` - `withFile` - `readFile` diff --git a/README.md b/README.md index e874d77..230bf3f 100644 --- a/README.md +++ b/README.md @@ -57,17 +57,16 @@ All these functions will make sure that the content will be treated as if it was encoded in UTF-8 (it is 2020, what else can it be encoded in?). If, for some reason, you really need to use `withFile`/`openFile` from `base`, -just call `hSetEncoding h`, where `h` is your handle and `hSetEncoding` comes -from `System.IO.Utf8` for your convenience: +or you got your file handle from somewhere else, wrap the code that works +with it in a call to `hWithEncoding` from `System.IO.Utf8`: ```haskell import qualified System.IO as IO import qualified System.IO.Utf8 as Utf8 -doSomethingWithAFile :: IO () -doSomethingWithAFile = IO.withFile "file.txt" IO.ReadMode $ \h -> do - Utf8.hSetEncoding h - {- ... work with the file ... -} +doSomethingWithAFile :: IO.Handle -> IO () +doSomethingWithAFile h = Utf8.hWithEncoding h $ do + {- ... work with the file ... -} ``` ### Step 4: Write files using UTF-8 diff --git a/lib/System/IO/Utf8.hs b/lib/System/IO/Utf8.hs index 68e53d0..1838e27 100644 --- a/lib/System/IO/Utf8.hs +++ b/lib/System/IO/Utf8.hs @@ -24,6 +24,7 @@ module System.IO.Utf8 ( withUtf8StdHandles , hSetEncoding + , hWithEncoding , openFile , withFile @@ -66,13 +67,10 @@ hSetBestUtf8Enc h = liftIO $ IO.hGetEncoding h >>= \case -- After the action finishes, restores the original encodings. withUtf8StdHandles :: IO a -> IO a withUtf8StdHandles action = - withConfiguredHandle stdin $ - withConfiguredHandle stdout $ - withConfiguredHandle stderr $ + hWithEncoding stdin $ + hWithEncoding stdout $ + hWithEncoding stderr $ action - where - withConfiguredHandle :: IO.Handle -> IO a -> IO a - withConfiguredHandle h = bracket (hSetBestUtf8Enc h) ($ h) . const -- | Set handle encoding to the best possible. @@ -93,6 +91,13 @@ withUtf8StdHandles action = hSetEncoding :: MonadIO m => IO.Handle -> m () hSetEncoding = liftIO . void . hSetBestUtf8Enc +-- | Temporarily set handle encoding to the best possible. +-- +-- This is like 'hSetEncoding', but it will restore the encoding +-- to the previous one when the action is done. +hWithEncoding :: (MonadIO m, MonadMask m) => IO.Handle -> m r -> m r +hWithEncoding h = bracket (hSetBestUtf8Enc h) ($ h) . const + -- | Like @openFile@, but sets the file encoding to UTF-8, regardless -- of the current locale.