unicode-data-security-0.1.0: lib/Unicode/Char/Identifiers/Security.hs
-- |
-- Module : Unicode.Char.Identifiers.Security
-- Copyright : (c) 2021 Composewell Technologies and Contributors
-- License : Apache-2.0
-- Maintainer : streamly@composewell.com
-- Stability : experimental
--
-- Unicode Security Mechanisms functions based on
-- [Unicode Technical Standard #39](https://www.unicode.org/reports/tr39/).
--
-- @since 0.1.0
module Unicode.Char.Identifiers.Security
( -- * Identifier status
isAllowedInIdentifier
-- * Identifier type
, T.IdentifierType(..)
, identifierTypes
, isIdentifierTypeAllowed
-- * Confusables
, confusablePrototype
-- , prototype
, intentionalConfusables
, isIntentionallyConfusable
)
where
import Data.List.NonEmpty (NonEmpty)
import Data.Maybe (isJust)
import qualified GHC.Foreign as Foreign
import qualified GHC.IO.Encoding as Encoding
import System.IO.Unsafe (unsafePerformIO)
import qualified Unicode.Internal.Char.Security.Confusables as C
import qualified Unicode.Internal.Char.Security.IdentifierStatus as S
import qualified Unicode.Internal.Char.Security.IdentifierType as T
import qualified Unicode.Internal.Char.Security.IntentionalConfusables as IC
-- | Returns 'True' if the given character is allowed in an identifier.
--
-- * /Restricted/ characters should be treated with caution when considering
-- possible use in identifiers, and should be disallowed unless there is
-- good reason to allow them in the environment in question.
-- * /Allowed/ characters are not typically used as is by implementations.
-- Instead, they are applied as filters to the set of supported characters.
--
-- @since 0.1.0
{-# INLINE isAllowedInIdentifier #-}
isAllowedInIdentifier :: Char -> Bool
isAllowedInIdentifier = S.isAllowedInIdentifier
-- | Return 'True' if the given 'T.IdentifierType' is allowed.
--
-- @since 0.1.0
{-# INLINE isIdentifierTypeAllowed #-}
isIdentifierTypeAllowed :: T.IdentifierType -> Bool
isIdentifierTypeAllowed = \case
T.Inclusion -> True
T.Recommended -> True
_ -> False
-- | Returns the 'IdentifierType's corresponding to a character.
--
-- @since 0.1.0
{-# INLINE identifierTypes #-}
identifierTypes :: Char -> NonEmpty T.IdentifierType
identifierTypes = T.decodeIdentifierTypes . T.identifierTypes
-- | Returns the /prototype/ of a character if it is /unintentionally/
-- confusable, else 'Nothing'.
--
-- @since 0.1.0
{-# INLINE confusablePrototype #-}
confusablePrototype :: Char -> Maybe String
confusablePrototype = fmap decode . C.confusablePrototype
where
decode = unsafePerformIO . Foreign.peekCString Encoding.utf8
-- [TODO] Assess the need for this function
-- -- | Returns the /prototype/ of a character.
-- --
-- -- Note: returns the character itself if it is not /unintentionally/ confusable.
-- --
-- -- @since 0.1.0
-- {-# INLINE prototype #-}
-- prototype :: Char -> String
-- prototype c = fromMaybe [c] (confusablePrototype c)
-- | Returns the list of /intentional/ confusables of a character, if any.
--
-- @since 0.1.0
{-# INLINE intentionalConfusables #-}
intentionalConfusables :: Char -> String
intentionalConfusables = maybe mempty decode . IC.intentionalConfusables
where
decode = unsafePerformIO . Foreign.peekCString Encoding.utf8
-- | Returns 'True' if the character is /intentionally/ confusable.
--
-- @since 0.1.0
{-# INLINE isIntentionallyConfusable #-}
isIntentionallyConfusable :: Char -> Bool
isIntentionallyConfusable = isJust . IC.intentionalConfusables