content: {-# LANGUAGE AllowAmbiguousTypes #-}
  {-# LANGUAGE ConstraintKinds #-}
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE FlexibleInstances #-}
  {-# LANGUAGE GADTs #-}
  {-# LANGUAGE MultiParamTypeClasses #-}
  {-# LANGUAGE PolyKinds #-}
  {-# LANGUAGE RankNTypes #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE NoImplicitPrelude #-}
  
  module Data.Morpheus.Generic.Proxy
    ( conNameP,
      isRecordP,
      selNameP,
      symbolName,
      CProxy (..),
      CBox (..),
      rep,
      foldProxy,
      runCBox,
    )
  where
  
  import Data.List (init, last)
  import GHC.Generics
    ( C,
      Constructor,
      Generic (..),
      M1 (..),
      Meta,
      S,
      Selector (..),
      U1 (..),
      conIsRecord,
      conName,
    )
  import GHC.TypeLits
  import Relude hiding (init, last, undefined)
  import Prelude (undefined)
  
  conNameP :: forall f t (c :: Meta). (Constructor c, IsString t) => f c -> t
  conNameP _ = fromString $ conName (undefined :: M1 C c U1 a)
  
  dropLiterals :: String -> String
  dropLiterals name
    | not (null name) && (last name == '\'') = init name
    | otherwise = name
  {-# INLINE dropLiterals #-}
  
  isRecordP :: forall f (c :: Meta). (Constructor c) => f c -> Bool
  isRecordP _ = conIsRecord (undefined :: (M1 C c f a))
  
  selNameP :: forall f t (s :: Meta). (Selector s, IsString t) => f s -> t
  selNameP _ = fromString $ dropLiterals $ selName (undefined :: M1 S s f a)
  
  symbolName :: (KnownSymbol a, IsString t) => f a -> t
  symbolName = fromString . symbolVal
  
  -- | constrained proxy
  data CProxy constraint where
    CProxy :: forall f constraint a. (constraint a) => f a -> CProxy constraint
  
  foldProxy :: (forall f a. (c a) => f a -> b) -> CProxy c -> b
  foldProxy f (CProxy x) = f x
  
  data CBox box constraint where
    CBox :: forall constraint box a. (constraint a) => box a -> CBox box constraint
  
  runCBox :: (forall a. (c a) => f a -> b) -> CBox f c -> b
  runCBox f (CBox x) = f x
  
  rep :: f a -> Proxy (Rep a)
  rep _ = Proxy
  