module Matcher ( Matcher, run, equals, satisfies, converts, whatever, ) where import Matcher.Prelude import qualified Success.Pure -- | -- Converts the matcher into a conversion function, -- which results in either a successful result or a failure. {-# INLINABLE run #-} run :: Matcher a b -> a -> Either Text b run (Matcher (ReaderT successFn)) input = {-# SCC "run" #-} either (Left . fromMaybe "") Right $ Success.Pure.asEither $ successFn input -- | -- A composable abstraction for checking or converting a context value. newtype Matcher a b = Matcher (ReaderT a (Success Text) b) deriving (Functor, Applicative, Alternative, Monad, MonadPlus) instance Profunctor Matcher where {-# INLINE lmap #-} lmap fn (Matcher (ReaderT successFn)) = Matcher (ReaderT (successFn . fn)) {-# INLINE rmap #-} rmap = fmap instance Category Matcher where {-# INLINE id #-} id = Matcher $ ReaderT $ Success.Pure.success {-# INLINE (.) #-} (.) (Matcher (ReaderT successFn2)) (Matcher (ReaderT successFn1)) = Matcher $ ReaderT $ successFn1 >=> successFn2 instance Arrow Matcher where {-# INLINE arr #-} arr f = Matcher $ ReaderT $ Success.Pure.success . f {-# INLINE first #-} first (Matcher (ReaderT successFn)) = Matcher $ ReaderT $ \(a, b) -> fmap (\a -> (a, b)) $ successFn a -- | -- Tests the matched value on equality with the provided value. {-# INLINE equals #-} equals :: Eq a => a -> Matcher a () equals reference = {-# SCC "equals" #-} Matcher $ ReaderT $ \input -> if input == reference then Success.Pure.success () else Success.Pure.failure "The input doesn't equal the expected value" -- | -- Checks whether the matched value satisfies the provided predicate. {-# INLINE satisfies #-} satisfies :: (a -> Bool) -> Matcher a () satisfies predicate = {-# SCC "satisfies" #-} Matcher $ ReaderT $ \input -> if predicate input then Success.Pure.success () else Success.Pure.failure "The input doesn't satisfy the predicate" -- | -- Tries to convert the matched value to an output value, -- with 'Either' encoding the success or failure of the conversion. {-# INLINE converts #-} converts :: (a -> Either Text b) -> Matcher a b converts match = {-# SCC "converts" #-} Matcher $ ReaderT $ either Success.Pure.failure Success.Pure.success . match -- | -- The matcher, which is always satisfied. {-# INLINE whatever #-} whatever :: Matcher a () whatever = {-# SCC "whatever" #-} Matcher $ ReaderT $ const $ pure ()