packages feed

h-raylib-5.5.0.0: src/Raylib/Internal/TH.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
{-# LANGUAGE TupleSections #-}

-- | Template Haskell functions used internally
module Raylib.Internal.TH (genLenses, genNative) where

import Control.Lens (makeLensesFor)
import Control.Monad (zipWithM)

#ifdef WEB_FFI

import Language.Haskell.TH
  ( Body (NormalB),
    Clause (Clause),
    Con (RecC),
    Dec (DataD, FunD, SigD),
    DecsQ,
    Exp (AppE, LitE, VarE),
    Info (TyConI),
    Lit (StringL),
    Name,
    TypeQ,
    mkName,
    nameBase,
    reify,
  )
import Raylib.Internal.Web.Native (callRaylibFunction)

#else

import Language.Haskell.TH
  ( Con (RecC),
    Dec (DataD, ForeignD),
    DecsQ,
    Info (TyConI),
    Name,
    TypeQ,
    mkName,
    nameBase,
    reify, Foreign (ImportF), Callconv (CCall), Safety (Safe, Unsafe),
  )

#endif

-- | Creates lenses with an underscore before field names; e.g. @vector2'x@
--   becomes the lens @_vector2'x@
genLenses :: [Name] -> DecsQ
genLenses names = do
  infos <- mapM reify names
  concat <$> zipWithM genLensesForType names infos
  where
    genLensesForType name (TyConI (DataD _ _ _ _ [RecC _ ctors] _)) =
      makeLensesFor mapping name
      where
        mapping = map (\(a, _, _) -> let fName = nameBase a in (fName, '_' : fName)) ctors
    genLensesForType _ _ = error "(genLenses) Received a name that does not refer to a valid type!"

-- | Generates native code for the given functions. On non-web platforms, this
--   means @foreign import@ statements. On web platforms, this means
--   `callRaylibFunction` calls.
genNative ::
  -- | (@hsName@, @cName@, @cFile@, @funType@, @isSafe@)
  [(String, String, String, TypeQ, Bool)] ->
  DecsQ
genNative funs = do
  funs' <- mapM (\(a, b, c, d, e) -> (a,b,c,,e) <$> d) funs
  return (genNative' funs')
  where
    genNative' [] = []
#ifdef WEB_FFI
    genNative' ((hsName, cName, _, funType, _) : xs) =
      [ -- hsName :: funType
        SigD name funType,
        -- hsName = callRaylibFunction "_cName"
        FunD
          name
          [Clause [] (NormalB (AppE (VarE 'callRaylibFunction) (LitE (StringL ('_' : cName))))) []]
      ] ++ genNative' xs
      where
        name = mkName hsName
#else
    genNative' ((hsName, cName, cFile, funType, isSafe) : xs) =
      -- foreign import ccall safe/unsafe "cFile cName" hsName :: funType
      ForeignD (ImportF CCall (if isSafe then Safe else Unsafe) (cFile ++ " " ++ cName) (mkName hsName) funType) : genNative' xs
#endif