{-| 
  'System.Rpm.Combinators' provides a mechanism for comparing
  attributes of an Rpm against some sort of specification.
-}

module System.Rpm.Combinators 
    (
      Rpm(..)
    , RpmP
    , zeroP
    , oneP
    , notP
    , (.==.)
    , (./=.)
    , (.<=.)
    , (.<.)
    , (.>=.)
    , (.>.)
    , (.&&.)
    , (.||.)
    -- * Example: Common Uses
    -- $exampleOfUse
    ) where

import System.Rpm.Info (RpmInfo(..))

type Rpm a = RpmInfo -> a


{-| 
A predicate type that takes an 'RpmInfo' datatype and returns a
value of Bool depending on its evaluation.
-}
type RpmP = Rpm Bool


{-|
A standard lifting function.
-}
liftP :: (a -> a -> b) -- ^ The function to lift
      -> Rpm a         -- ^ The field to extract from 'RpmInfo'
      -> a             -- ^ Passed to the Second argument of the function
      -> Rpm b         -- ^ The result wrapped in 'Rpm'

liftP f g val rpmInfo= g rpmInfo `f` val


{-|
A more conventional lifting function.
-}
liftP2 :: (a -> b -> c) 
       -> Rpm a 
       -> Rpm b 
       -> Rpm c

liftP2 f g h rpmInfo = g rpmInfo `f` h rpmInfo


{-| 
The 'zeroP' combinator, much like the 'oneP' combinator, is more
for completeness than anything.
-}
zeroP :: RpmP  -- ^ The constant combinator that always returns
               -- 'False' when run.
zeroP _ = False


oneP :: RpmP  -- ^ The constant combinator that always returns 'False'
              -- when run.
oneP _ = True


(.==.) :: Eq a => Rpm a  -- ^ Some field of 'RpmInfo'
       -> a              -- ^ The value to check equality against
       -> RpmP           
(.==.) = liftP (==)


(./=.) :: Eq a => Rpm a -> a -> RpmP
(./=.) = liftP (/=)


(.<=.) :: Ord a => Rpm a -> a -> RpmP
(.<=.) = liftP (<=)


(.<.) :: Ord a => Rpm a -> a -> RpmP
(.<.) = liftP (<)


(.>=.) :: Ord a => Rpm a -> a -> RpmP
(.>=.) = liftP (>=)


(.>.) :: Ord a => Rpm a -> a -> RpmP
(.>.) = liftP (>)


{-|
@notP@ is used to negate its 'RpmP' when run.
-}
notP :: RpmP -- ^ Predicate to negate
     -> RpmP -- ^ Negated predicate
notP p =  not . p 


{-| 
This is a logical combinator used for constructing more complex
sequences of combinators by requiring both predicates to be true.
-}
(.&&.) :: RpmP 
       -> RpmP 
       -> RpmP
(.&&.) = liftP2 (&&)


{-| 
This is a logical combinator used for constructing more complex
sequences of combinators by requiring one of the predicates to be true.
-}
(.||.) :: RpmP 
       -> RpmP 
       -> RpmP
(.||.) = liftP2 (||)

{-| $exampleOfUse
Example of use.

> main :: IO ()
> main = putStrLn "Hello"

-}

-- nameMatchesFilename :: RpmP
-- nameMatchesFilename rpmInfo = fileName .==. (name rpmInfo)

-- Then, to find where name doesn't match filename,
-- findAll (.!. nameMatchesFilename)
-- or, assertAll (nameMatchesFilename)
-- or, assertAll (buildHost .==. "my1.machine.org" .||. buildHost .==. "my2.machine.org")

-- Conditional predicate generation
-- ifThenElse :: RpmP -> RpmP -> RpmP
-- ifThenElse ( file `matchesP` "foo*" )
--            ( size .<=. 1234)


-- assertEqual .!. (.!. zeroP) == zeroP
-- assertEqual (.!. zeroP) == oneP