-- | This module defines functions which are useful for determining if
-- a given name is a legal JavaScript name according to
-- <https://stackoverflow.com/questions/1661197/what-characters-are-valid-for-javascript-variable-names this post>.
module Data.Aeson.TypeScript.LegalName where

import Data.Char
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Set as Set


-- | The return type is the illegal characters that are in the name. If the
-- input has no illegal characters, then you have 'Nothing'.
checkIllegalNameChars :: NonEmpty Char -> Maybe (NonEmpty Char)
checkIllegalNameChars :: NonEmpty Char -> Maybe (NonEmpty Char)
checkIllegalNameChars (Char
firstChar :| [Char]
restChars) = forall a. [a] -> Maybe (NonEmpty a)
NonEmpty.nonEmpty forall a b. (a -> b) -> a -> b
$
  let
    legalFirstCategories :: Set GeneralCategory
legalFirstCategories =
      forall a. Ord a => [a] -> Set a
Set.fromList
        [ GeneralCategory
UppercaseLetter
        , GeneralCategory
LowercaseLetter
        , GeneralCategory
TitlecaseLetter
        , GeneralCategory
ModifierLetter
        , GeneralCategory
OtherLetter
        , GeneralCategory
LetterNumber
        ]
    legalRestCategories :: Set GeneralCategory
legalRestCategories =
      forall a. Ord a => [a] -> Set a
Set.fromList
        [ GeneralCategory
NonSpacingMark
        , GeneralCategory
SpacingCombiningMark
        , GeneralCategory
DecimalNumber
        , GeneralCategory
ConnectorPunctuation
        ]
        forall a. Ord a => Set a -> Set a -> Set a
`Set.union` Set GeneralCategory
legalFirstCategories
    isIllegalFirstChar :: Char -> Bool
isIllegalFirstChar Char
c = Bool -> Bool
not forall a b. (a -> b) -> a -> b
$
      Char
c forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
'$', Char
'_'] Bool -> Bool -> Bool
|| Char -> GeneralCategory
generalCategory Char
c forall a. Ord a => a -> Set a -> Bool
`Set.member` Set GeneralCategory
legalFirstCategories
    isIllegalRestChar :: Char -> Bool
isIllegalRestChar Char
c = Bool -> Bool
not forall a b. (a -> b) -> a -> b
$
      Char -> GeneralCategory
generalCategory Char
c forall a. Ord a => a -> Set a -> Bool
`Set.member` Set GeneralCategory
legalRestCategories
  in
    forall a. (a -> Bool) -> [a] -> [a]
filter Char -> Bool
isIllegalFirstChar [Char
firstChar] forall a. Semigroup a => a -> a -> a
<> forall a. (a -> Bool) -> [a] -> [a]
filter Char -> Bool
isIllegalRestChar [Char]
restChars