packages feed

managed-functions-1.2.2.0: src/Managed/Probe/ToProbe.hs

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-incomplete-patterns #-}

module Managed.Probe.ToProbe
  ( toProbe
  , ToProbe(..)
  ) where

import Control.Monad.Catch (MonadThrow)
import Data.Managed
import Data.Typeable (Proxy(..), TypeRep, Typeable, typeOf)
import Managed.Exception (badNumberOfArgs, noParseArg, throwM)
import Managed.Probe.Internal.Params (paramsCnt)

-- | Converts any suitable function to a 'Probe'
toProbe ::
     forall e fn. (Typeable fn, ToProbe fn e)
  => fn
  -> Probe e
toProbe x =
  let t = typeOf x
   in Probe {typeRep = t, call = checkArgs t (apply (Proxy @e) x)}

-- | Class of functions that can be converted to a Probe
class ToProbe fn e where
  -- | Read arguments from a list, apply them to a function, and encode the result
  apply :: Proxy e -> fn -> [In e] -> IO (Out e)

instance {-# OVERLAPPABLE #-} (Encode a e) => ToProbe a e where
  apply _ c [] = return $ (encode @a @e) c

instance {-# OVERLAPPING #-} (Encode a e) => ToProbe (IO a) e where
  apply _ c [] = (encode @a @e) <$> c

instance {-# OVERLAPPING #-} (Decode a e, ToProbe b e) =>
                             ToProbe (a -> b) e where
  apply _ f (x:xs) = do
    r <- decodeSingle (Proxy @e) x
    apply (Proxy @e) (f r) xs

-- Helper functions
decodeSingle ::
     forall a e m. (MonadThrow m, Decode a e)
  => Proxy e
  -> In e
  -> m a
decodeSingle _ x'
  | (Just x) <- (decode @a @e) x' = return x
  | otherwise = throwM noParseArg

checkArgs :: TypeRep -> ([a] -> IO b) -> [a] -> IO b
checkArgs t = withArgs (paramsCnt t)

withArgs :: Int -> ([a] -> IO b) -> [a] -> IO b
withArgs n f args
  | n == length args = f args
  | otherwise = throwM $ badNumberOfArgs n args