packages feed

grisette-0.13.0.1: src/Grisette/Internal/TH/Ctor/Common.hs

-- |
-- Module      :   Grisette.Internal.TH.Ctor.Common
-- Copyright   :   (c) Sirui Lu 2024
-- License     :   BSD-3-Clause (see the LICENSE file)
--
-- Maintainer  :   siruilu@cs.washington.edu
-- Stability   :   Experimental
-- Portability :   GHC only
module Grisette.Internal.TH.Ctor.Common
  ( withNameTransformer,
    prefixTransformer,
    decapitalizeTransformer,
  )
where

import Control.Monad (unless)
import Data.Char (isAlphaNum, toLower)
import Data.Foldable (traverse_)
import Language.Haskell.TH (Dec, Name, Q, nameBase)
import Language.Haskell.TH.Datatype
  ( ConstructorInfo (constructorName),
    DatatypeInfo (datatypeCons),
    reifyDatatype,
  )

checkName :: String -> Q ()
checkName name =
  unless (all (\x -> isAlphaNum x || x == '\'' || x == '_') name) $
    fail
      ( "Constructor name contain invalid characters, consider providing a "
          ++ "custom name: "
          ++ show name
      )

-- | Generate smart constructor given a type name, using a name transformer
-- to transform constructor names.
withNameTransformer ::
  -- | A function that generates decs given a list of constructor names and a
  -- type name
  ([String] -> Name -> Q [Dec]) ->
  -- | A function that transforms constructor names
  (String -> String) ->
  -- | The type to generate the wrappers for
  Name ->
  Q [Dec]
withNameTransformer namedGen nameTransformer typName = do
  d <- reifyDatatype typName
  let constructorNames = nameBase . constructorName <$> datatypeCons d
  let transformedNames = nameTransformer <$> constructorNames
  traverse_ checkName transformedNames
  namedGen transformedNames typName

-- | A name transformer that prefixes a string to the constructor name
prefixTransformer :: String -> String -> String
prefixTransformer = (++)

-- | A name transformer that converts the first character to lowercase
decapitalizeTransformer :: String -> String
decapitalizeTransformer (x : xs) = toLower x : xs
decapitalizeTransformer [] = []