text-icu-translit-0.1.0.7: Data/Text/ICU/Translit/ICUHelper.hsc
{-# LANGUAGE DeriveDataTypeable, MultiWayIf #-}
module Data.Text.ICU.Translit.ICUHelper
(
ICUError(..)
, UChar
, UErrorCode
, isFailure
, errorName
, handleError
, handleFilledOverflowError
, throwOnError
) where
-- Many functions in this module are straight from the
-- Data.Text.ICU.Error.Internal (text-icu).
--
-- XXX TODO:
-- ⋆ export this and similar functionality somewhere;
-- or
-- ⋆ merge text-icu-* into text-icu?
import Control.Exception (Exception, throwIO)
import Data.Typeable (Typeable)
import Foreign.C.Types (CInt(..))
import Foreign.C.String (CString, peekCString)
import qualified System.IO.Unsafe as IO (unsafePerformIO)
import Foreign
type UErrorCode = CInt
type UChar = Word16
newtype ICUError = ICUError {
fromErrorCode :: UErrorCode
} deriving (Eq, Typeable)
instance Show ICUError where
show code = "ICUError " ++ errorName code
instance Exception ICUError
#include <unicode/utypes.h>
-- | Indicate whether the given error code is a failure.
isFailure :: ICUError -> Bool
{-# INLINE isFailure #-}
isFailure = (> 0) . fromErrorCode
-- | Throw an exception if the given code is actually an error.
throwOnError :: UErrorCode -> IO ()
{-# INLINE throwOnError #-}
throwOnError code = do
let err = (ICUError code)
if isFailure err
then throwIO err
else return ()
handleError :: (Ptr UErrorCode -> IO a) -> IO a
{-# INLINE handleError #-}
handleError action = with 0 $ \errPtr -> do
ret <- action errPtr
throwOnError =<< peek errPtr
return ret
-- | Deal with ICU functions that report a buffer overflow error if we
-- give them an insufficiently large buffer. The difference between
-- this function and
-- 'Data.Text.ICU.Error.Internal.handleOverflowError' is that this one
-- doesn't change the contents of the provided buffer, while the
-- latter assumes buffers to be write-only.
handleFilledOverflowError :: (Storable a) =>
Ptr a
-- ^ Initial buffer.
-> Int
-- ^ Initial buffer size.
-> (Ptr a -> Int32 -> Ptr UErrorCode -> IO Int32)
-- ^ Function that retrieves data.
-> (Ptr a -> Int -> IO b)
-- ^ Function that fills destination buffer if no
-- overflow occurred.
-> IO b
handleFilledOverflowError text len0 fill retrieve =
do buf0 <- mallocArray len0
copyArray buf0 text len0
go buf0 len0
where
go buf len = alloca $ \errPtr -> do
poke errPtr 0
len' <- fill buf (fromIntegral len) errPtr
err <- peek errPtr
if | err == (#const U_BUFFER_OVERFLOW_ERROR)
-> do buf' <- reallocArray buf (fromIntegral len')
copyArray buf' text len0
go buf' (fromIntegral len')
| err > 0
-> throwIO (ICUError err)
| otherwise
-> retrieve buf (fromIntegral len')
-- | Return a string representing the name of the given error code.
errorName :: ICUError -> String
errorName code = IO.unsafePerformIO $
peekCString (u_errorName (fromErrorCode code))
foreign import ccall unsafe "trans.h __hs_translit_u_errorName" u_errorName
:: UErrorCode -> CString