{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Propellor.Property.List (
	props,
	Props,
	toProps,
	propertyList,
	combineProperties,
) where

import Propellor.Types
import Propellor.Types.Core
import Propellor.Types.MetaTypes
import Propellor.PropAccum
import Propellor.Engine
import Propellor.Exception

import Data.Monoid
import Prelude

toProps :: [Property (MetaTypes metatypes)] -> Props (MetaTypes metatypes)
toProps :: forall {k} (metatypes :: k).
[Property (MetaTypes metatypes)] -> Props (MetaTypes metatypes)
toProps [Property (MetaTypes metatypes)]
ps = forall metatypes. [ChildProperty] -> Props metatypes
Props (forall a b. (a -> b) -> [a] -> [b]
map forall p. IsProp p => p -> ChildProperty
toChildProperty [Property (MetaTypes metatypes)]
ps)

-- | Combines a list of properties, resulting in a single property
-- that when run will run each property in the list in turn,
-- and print out the description of each as it's run. Does not stop
-- on failure; does propagate overall success/failure.
--
-- For example:
--
-- > propertyList "foo" $ props
-- > 	& bar
-- > 	& baz
propertyList :: SingI metatypes => Desc -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
propertyList :: forall {k} (metatypes :: k).
SingI metatypes =>
Desc
-> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
propertyList Desc
desc (Props [ChildProperty]
ps) = 
	forall {k} (metatypes :: k).
SingI metatypes =>
Desc -> Propellor Result -> Property (MetaTypes metatypes)
property Desc
desc ([ChildProperty] -> Propellor Result
ensureChildProperties [ChildProperty]
cs)
		forall p. IsProp p => p -> [ChildProperty] -> p
`addChildren` [ChildProperty]
cs
  where
	cs :: [ChildProperty]
cs = forall a b. (a -> b) -> [a] -> [b]
map forall p. IsProp p => p -> ChildProperty
toChildProperty [ChildProperty]
ps

-- | Combines a list of properties, resulting in one property that
-- ensures each in turn. Stops if a property fails.
--
-- > combineProperties "foo" $ props
-- > 	& bar
-- > 	& baz
--
-- This is similar to using `mconcat` with a list of properties,
-- except it can combine together different types of properties.
combineProperties :: SingI metatypes => Desc -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
combineProperties :: forall {k} (metatypes :: k).
SingI metatypes =>
Desc
-> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
combineProperties Desc
desc (Props [ChildProperty]
ps) = 
	forall {k} (metatypes :: k).
SingI metatypes =>
Desc -> Propellor Result -> Property (MetaTypes metatypes)
property Desc
desc ([ChildProperty] -> Result -> Propellor Result
combineSatisfy [ChildProperty]
cs Result
NoChange)
		forall p. IsProp p => p -> [ChildProperty] -> p
`addChildren` [ChildProperty]
cs
  where
	cs :: [ChildProperty]
cs = forall a b. (a -> b) -> [a] -> [b]
map forall p. IsProp p => p -> ChildProperty
toChildProperty [ChildProperty]
ps

combineSatisfy :: [ChildProperty] -> Result -> Propellor Result
combineSatisfy :: [ChildProperty] -> Result -> Propellor Result
combineSatisfy [] Result
rs = forall (m :: * -> *) a. Monad m => a -> m a
return Result
rs
combineSatisfy (ChildProperty
p:[ChildProperty]
ps) Result
rs = do
	Result
r <- forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall (m :: * -> *) a. Monad m => a -> m a
return Result
NoChange) forall (m :: * -> *).
(MonadIO m, MonadCatch m) =>
m Result -> m Result
catchPropellor (forall p. IsProp p => p -> Maybe (Propellor Result)
getSatisfy ChildProperty
p)
	case Result
r of
		Result
FailedChange -> forall (m :: * -> *) a. Monad m => a -> m a
return Result
FailedChange
		Result
_ -> [ChildProperty] -> Result -> Propellor Result
combineSatisfy [ChildProperty]
ps (Result
r forall a. Semigroup a => a -> a -> a
<> Result
rs)