packages feed

moonlight-core-0.1.0.0: src-basis/Moonlight/Core/Identifier.hs

{-# LANGUAGE RoleAnnotations #-}

-- | Validated, namespace-tagged identifier tokens ('IdentifierToken') with
-- checked and scoped constructors.
module Moonlight.Core.Identifier
  ( IdentifierToken,
    mkIdentifierToken,
    mkIdentifierTokenWith,
    mkScopedIdentifier,
    renderIdentifierToken,
    renderScopedIdentifier,
    isValidIdentifier,
  )
where

import Data.Char (isAlphaNum)
import Data.Text (Text)
import qualified Data.Text as Text
import Moonlight.Internal.Unsound (IdentifierToken (..))
import Prelude (Bool, Char, Maybe (..), fmap, not, (&&), (.), (==), (||))

mkIdentifierToken :: Text -> Maybe (IdentifierToken namespace)
mkIdentifierToken =
  mkIdentifierTokenWith isValidIdentifier

mkIdentifierTokenWith :: (Text -> Bool) -> Text -> Maybe (IdentifierToken namespace)
mkIdentifierTokenWith predicate rawInput =
  let candidate = Text.strip rawInput
   in if predicate candidate
        then Just (IdentifierToken candidate)
        else Nothing

mkScopedIdentifier :: (IdentifierToken namespace -> identifier) -> Text -> Maybe identifier
mkScopedIdentifier wrapIdentifier =
  fmap wrapIdentifier . mkIdentifierToken

renderIdentifierToken :: IdentifierToken namespace -> Text
renderIdentifierToken (IdentifierToken identifier) =
  identifier

renderScopedIdentifier :: (identifier -> IdentifierToken namespace) -> identifier -> Text
renderScopedIdentifier unwrapIdentifier =
  renderIdentifierToken . unwrapIdentifier

isValidIdentifier :: Text -> Bool
isValidIdentifier candidate =
  not (Text.null candidate) && Text.all isIdentifierCharacter candidate

isIdentifierCharacter :: Char -> Bool
isIdentifierCharacter character =
  isAlphaNum character || character == '-' || character == '_'