{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-} module Propellor.Property.List ( props, PropertyList(..), PropertyListType, ) where import Propellor.Types import Propellor.Engine import Propellor.PropAccum import Data.Monoid -- | Starts accumulating a list of properties. -- -- > propertyList "foo" $ props -- > & someproperty -- > ! oldproperty -- > & otherproperty props :: PropList props = PropList [] data PropList = PropList [Property HasInfo] instance PropAccum PropList where PropList l `addProp` p = PropList (toProp p : l) PropList l `addPropFront` p = PropList (l ++ [toProp p]) getProperties (PropList l) = reverse l class PropertyList l where -- | 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 propigate overall success/failure. -- -- Note that Property HasInfo and Property NoInfo are not the same -- type, and so cannot be mixed in a list. To make a list of -- mixed types, which can also include RevertableProperty, -- use `props` propertyList :: Desc -> l -> Property (PropertyListType l) -- | Combines a list of properties, resulting in one property that -- ensures each in turn. Stops if a property fails. combineProperties :: Desc -> l -> Property (PropertyListType l) -- | Type level function to calculate whether a PropertyList has Info. type family PropertyListType t type instance PropertyListType [Property HasInfo] = HasInfo type instance PropertyListType [Property NoInfo] = NoInfo type instance PropertyListType PropList = HasInfo instance PropertyList [Property NoInfo] where propertyList desc ps = simpleProperty desc (ensureProperties ps) ps combineProperties desc ps = simpleProperty desc (combineSatisfy ps NoChange) ps instance PropertyList [Property HasInfo] where -- It's ok to use ignoreInfo here, because the ps are made the -- child properties of the property, and so their info is visible -- that way. propertyList desc ps = infoProperty desc (ensureProperties $ map ignoreInfo ps) mempty ps combineProperties desc ps = infoProperty desc (combineSatisfy ps NoChange) mempty ps instance PropertyList PropList where propertyList desc = propertyList desc . getProperties combineProperties desc = combineProperties desc . getProperties combineSatisfy :: [Property i] -> Result -> Propellor Result combineSatisfy [] rs = return rs combineSatisfy (l:ls) rs = do r <- ensureProperty $ ignoreInfo l case r of FailedChange -> return FailedChange _ -> combineSatisfy ls (r <> rs)