    {-# 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 = [ChildProperty] -> Props (MetaTypes metatypes)
    forall metatypes. [ChildProperty] -> Props metatypes
    Props ((Property (MetaTypes metatypes) -> ChildProperty)
    -> [Property (MetaTypes metatypes)] -> [ChildProperty]
    forall a b. (a -> b) -> [a] -> [b]
    map Property (MetaTypes metatypes) -> ChildProperty
    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) =
    	Desc -> Propellor Result -> Property (MetaTypes metatypes)
    forall {k} (metatypes :: k).
    SingI metatypes =>
    Desc -> Propellor Result -> Property (MetaTypes metatypes)
    property Desc
    desc ([ChildProperty] -> Propellor Result
    ensureChildProperties [ChildProperty]
    cs)
    		Property (MetaTypes metatypes)
    -> [ChildProperty] -> Property (MetaTypes metatypes)
    forall p. IsProp p => p -> [ChildProperty] -> p
    `addChildren` [ChildProperty]
    cs
      where
    	cs :: [ChildProperty]
    cs = (ChildProperty -> ChildProperty)
    -> [ChildProperty] -> [ChildProperty]
    forall a b. (a -> b) -> [a] -> [b]
    map ChildProperty -> ChildProperty
    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) =
    	Desc -> Propellor Result -> Property (MetaTypes metatypes)
    forall {k} (metatypes :: k).
    SingI metatypes =>
    Desc -> Propellor Result -> Property (MetaTypes metatypes)
    property Desc
    desc ([ChildProperty] -> Result -> Propellor Result
    combineSatisfy [ChildProperty]
    cs Result
    NoChange)
    		Property (MetaTypes metatypes)
    -> [ChildProperty] -> Property (MetaTypes metatypes)
    forall p. IsProp p => p -> [ChildProperty] -> p
    `addChildren` [ChildProperty]
    cs
      where
    	cs :: [ChildProperty]
    cs = (ChildProperty -> ChildProperty)
    -> [ChildProperty] -> [ChildProperty]
    forall a b. (a -> b) -> [a] -> [b]
    map ChildProperty -> ChildProperty
    forall p. IsProp p => p -> ChildProperty
    toChildProperty [ChildProperty]
    ps

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