with-utf8-1.0.2.4: lib/Main/Utf8.hs
{- SPDX-FileCopyrightText: 2020 Serokell <https://serokell.io/>
-
- SPDX-License-Identifier: MPL-2.0
-}
-----------------------------------------------------------------------------
-- |
--
-- Functions in this module will help you make your /executable/ work
-- correctly with encodings of text files and standard handles.
--
-- /Note: if you are developing a library, see "System.IO.Utf8"./
--
-- = Quick start
--
-- Wrap a call to 'withUtf8' around your @main@:
--
-- @
-- import Main.Utf8 (withUtf8)
--
-- main :: IO ()
-- main = 'withUtf8' $ do
-- putStrLn "Hello, мир!"
-- @
--
-- Basically, this is all you have to do for a program that uses
-- @stdin@ and @stdout@ to interact with the user. However, some
-- programs read input from and write output to files and,
-- at the same time, allow the user to redirect @stdin@ and @stdout@
-- instead of providing explicit file names.
--
-- If this is the case for your executable, you should also wrap
-- @Utf8.@'System.IO.Utf8.withHandle' around the code that passes
-- the handle to a third-party library. It is not necessary to do
-- when passing it to your own library, assuming that it follows
-- the recommendations from the documentation of "System.IO.Utf8".
module Main.Utf8
( withUtf8
, withStdTerminalHandles
) where
import Control.Exception.Safe (MonadMask, bracket)
import Control.Monad.IO.Class (MonadIO, liftIO)
import GHC.IO.Encoding (getLocaleEncoding, setLocaleEncoding, utf8)
import System.IO (stderr, stdin, stdout)
import System.IO.Utf8 (withTerminalHandle)
-- | Make standard handles safe to write anything to them and change
-- program-global default file handle encoding to UTF-8.
--
-- This function will:
--
-- 1. Adjust the encoding of 'stdin', 'stdout', and 'stderr' to
-- enable transliteration, like 'withStdTerminalHandles' does.
-- 2. Call 'setLocaleEncoding' to change the program-global locale
-- encoding to UTF-8.
-- 3. Undo everything when the wrapped action finishes.
withUtf8 :: (MonadIO m, MonadMask m) => m r -> m r
withUtf8 act = withStdTerminalHandles $
bracket
(liftIO $ getLocaleEncoding <* setLocaleEncoding utf8)
(liftIO . setLocaleEncoding)
(const act)
-- | Make standard handles safe to write anything to them.
--
-- This function will for each of 'stdin', 'stdout', 'stderr' do:
--
-- 1. Tweak the existing encoding so that unrepresentable characters
-- will get approximated (aka transliterated) by visually similar
-- ones or question marks.
-- 2. Restore the original encoding when the wrapped action finishes.
--
-- Use this function only if you do not want to change the program-global
-- locale encoding. Otherwise prefer 'withUtf8'.
withStdTerminalHandles :: (MonadIO m, MonadMask m) => m r -> m r
withStdTerminalHandles
= withTerminalHandle stdin
. withTerminalHandle stdout
. withTerminalHandle stderr