packages feed

unicode-data-security-0.3.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
    ( -- * Unicode version
      unicodeVersion
      -- * Identifier status
    , isAllowedInIdentifier

      -- * Identifier type
    , T.IdentifierType(..)
    , identifierTypes
    , isIdentifierTypeAllowed

      -- * Confusables
    , confusablePrototype
    -- , prototype
    , intentionalConfusables
    , isIntentionallyConfusable
    )
where

import Data.List.NonEmpty (NonEmpty)
import GHC.Exts (eqChar#, indexCharOffAddr#, isTrue#)

import Unicode.Internal.Bits.Security (unpackCStringUtf8#)
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
import Unicode.Internal.Char.Security.Version (unicodeVersion)

-- | 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 -> String
confusablePrototype c = unpackCStringUtf8# (C.confusablePrototype c)

-- [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 c = unpackCStringUtf8# (IC.intentionalConfusables c)

-- | Returns 'True' if the character is /intentionally/ confusable.
--
-- @since 0.1.0
{-# INLINE isIntentionallyConfusable #-}
isIntentionallyConfusable :: Char -> Bool
isIntentionallyConfusable c = isTrue# (c# `eqChar#` '\0'#)
    where
    !addr = IC.intentionalConfusables c
    !c# = indexCharOffAddr# addr 0#