{-# 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 ps = Props (map toChildProperty 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 desc (Props ps) =
        property desc (ensureChildProperties cs)
                `addChildren` cs
  where
        cs = map toChildProperty 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 desc (Props ps) =
        property desc (combineSatisfy cs NoChange)
                `addChildren` cs
  where
        cs = map toChildProperty ps

combineSatisfy :: [ChildProperty] -> Result -> Propellor Result
combineSatisfy [] rs = return rs
combineSatisfy (p:ps) rs = do
        r <- maybe (return NoChange) catchPropellor (getSatisfy p)
        case r of
                FailedChange -> return FailedChange
                _ -> combineSatisfy ps (r <> rs)