    {-# LANGUAGE GeneralizedNewtypeDeriving #-}
    {-# LANGUAGE FlexibleInstances #-}
    {-# LANGUAGE UndecidableInstances #-}
    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE ConstraintKinds #-}
    {-# LANGUAGE PolyKinds #-}
    {-# LANGUAGE DataKinds #-}
    {-# LANGUAGE DeriveDataTypeable #-}

    module Propellor.Types (
    	-- * Core data types
    	  Host(..)
    	, Property(..)
    	, property
    	, property''
    	, Desc
    	, RevertableProperty(..)
    	, (<!>)
    	, Propellor(..)
    	, LiftPropellor(..)
    	, Info
    	-- * Types of properties
    	, UnixLike
    	, Linux
    	, DebianLike
    	, Debian
    	, Buntish
    	, ArchLinux
    	, FreeBSD
    	, HasInfo
    	, type (+)
    	, TightenTargets(..)
    	, TightenTargetsAllowed
    	-- * Combining and modifying properties
    	, Combines(..)
    	, CombinedType
    	, ResultCombiner
    	, adjustPropertySatisfy
    	-- * Other included types
    	, module Propellor.Types.OS
    	, module Propellor.Types.ConfigurableValue
    	, module Propellor.Types.Dns
    	, module Propellor.Types.Result
    	, module Propellor.Types.ZFS
    	) where

    import GHC.TypeLits hiding (type (+))
    import GHC.Exts (Constraint)
    import Data.Type.Bool
    import qualified Data.Semigroup as Sem
    import Data.Monoid
    import Control.Applicative
    import Prelude

    import Propellor.Types.Core
    import Propellor.Types.Info
    import Propellor.Types.OS
    import Propellor.Types.ConfigurableValue
    import Propellor.Types.Dns
    import Propellor.Types.Result
    import Propellor.Types.MetaTypes
    import Propellor.Types.ZFS

    -- | The core data type of Propellor, this represents a property
    -- that the system should have, with a description, and an action to ensure
    -- it has the property.
    --
    -- There are different types of properties that target different OS's,
    -- and so have different metatypes.
    -- For example: "Property DebianLike" and "Property FreeBSD".
    --
    -- Also, some properties have associated `Info`, which is indicated in
    -- their type: "Property (HasInfo + DebianLike)"
    --
    -- There are many associated type families, which are mostly used
    -- internally, so you needn't worry about them.
    data Property metatypes = Property metatypes Desc (Maybe (Propellor Result)) Info [ChildProperty]

    instance Show (Property metatypes) where
    	show :: Property metatypes -> String
    show Property metatypes
    p = String
    "property " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ ShowS
    forall a. Show a => a -> String
    show (Property metatypes -> String
    forall p. IsProp p => p -> String
    getDesc Property metatypes
    p)

    -- | Constructs a Property, from a description and an action to run to
    -- ensure the Property is met.
    --
    -- Due to the polymorphic return type of this function, most uses will need
    -- to specify a type signature. This lets you specify what OS the property
    -- targets, etc.
    --
    -- For example:
    --
    -- > foo :: Property Debian
    -- > foo = property "foo" $ do
    -- >	...
    -- > 	return MadeChange
    property
    	:: SingI metatypes
    	=> Desc
    	-> Propellor Result
    	-> Property (MetaTypes metatypes)
    property :: forall {k} (metatypes :: k).
    SingI metatypes =>
    String -> Propellor Result -> Property (MetaTypes metatypes)
    property String
    d Propellor Result
    a = MetaTypes metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property (MetaTypes metatypes)
    forall metatypes.
    metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property metatypes
    Property MetaTypes metatypes
    forall {k} (t :: k). SingI t => Sing t
    sing String
    d (Propellor Result -> Maybe (Propellor Result)
    forall a. a -> Maybe a
    Just Propellor Result
    a) Info
    forall a. Monoid a => a
    mempty [ChildProperty]
    forall a. Monoid a => a
    mempty

    property''
    	:: SingI metatypes
    	=> Desc
    	-> Maybe (Propellor Result)
    	-> Property (MetaTypes metatypes)
    property'' :: forall {k} (metatypes :: k).
    SingI metatypes =>
    String
    -> Maybe (Propellor Result) -> Property (MetaTypes metatypes)
    property'' String
    d Maybe (Propellor Result)
    a = MetaTypes metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property (MetaTypes metatypes)
    forall metatypes.
    metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property metatypes
    Property MetaTypes metatypes
    forall {k} (t :: k). SingI t => Sing t
    sing String
    d Maybe (Propellor Result)
    a Info
    forall a. Monoid a => a
    mempty [ChildProperty]
    forall a. Monoid a => a
    mempty

    -- | Changes the action that is performed to satisfy a property.
    adjustPropertySatisfy :: Property metatypes -> (Propellor Result -> Propellor Result) -> Property metatypes
    adjustPropertySatisfy :: forall metatypes.
    Property metatypes
    -> (Propellor Result -> Propellor Result) -> Property metatypes
    adjustPropertySatisfy (Property metatypes
    t String
    d Maybe (Propellor Result)
    s Info
    i [ChildProperty]
    c) Propellor Result -> Propellor Result
    f = metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property metatypes
    forall metatypes.
    metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property metatypes
    Property metatypes
    t String
    d (Propellor Result -> Propellor Result
    f (Propellor Result -> Propellor Result)
    -> Maybe (Propellor Result) -> Maybe (Propellor Result)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe (Propellor Result)
    s) Info
    i [ChildProperty]
    c

    -- | A property that can be reverted. The first Property is run
    -- normally and the second is run when it's reverted.
    --
    -- See `Propellor.Property.Versioned.Versioned`
    -- for a way to use RevertableProperty to define different
    -- versions of a host.
    data RevertableProperty setupmetatypes undometatypes = RevertableProperty
    	{ forall setupmetatypes undometatypes.
    RevertableProperty setupmetatypes undometatypes
    -> Property setupmetatypes
    setupRevertableProperty :: Property setupmetatypes
    	, forall setupmetatypes undometatypes.
    RevertableProperty setupmetatypes undometatypes
    -> Property undometatypes
    undoRevertableProperty :: Property undometatypes
    	}

    instance Show (RevertableProperty setupmetatypes undometatypes) where
    	show :: RevertableProperty setupmetatypes undometatypes -> String
    show (RevertableProperty Property setupmetatypes
    p Property undometatypes
    _) = Property setupmetatypes -> String
    forall a. Show a => a -> String
    show Property setupmetatypes
    p

    -- | Shorthand to construct a revertable property from any two Properties.
    (<!>)
    	:: Property setupmetatypes
    	-> Property undometatypes
    	-> RevertableProperty setupmetatypes undometatypes
    Property setupmetatypes
    setup <!> :: forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    <!> Property undometatypes
    undo = Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    RevertableProperty Property setupmetatypes
    setup Property undometatypes
    undo

    instance IsProp (Property metatypes) where
    	setDesc :: Property metatypes -> String -> Property metatypes
    setDesc (Property metatypes
    t String
    _ Maybe (Propellor Result)
    a Info
    i [ChildProperty]
    c) String
    d = metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property metatypes
    forall metatypes.
    metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property metatypes
    Property metatypes
    t String
    d Maybe (Propellor Result)
    a Info
    i [ChildProperty]
    c
    	getDesc :: Property metatypes -> String
    getDesc (Property metatypes
    _ String
    d Maybe (Propellor Result)
    _ Info
    _ [ChildProperty]
    _) = String
    d
    	getChildren :: Property metatypes -> [ChildProperty]
    getChildren (Property metatypes
    _ String
    _ Maybe (Propellor Result)
    _ Info
    _ [ChildProperty]
    c) = [ChildProperty]
    c
    	addChildren :: Property metatypes -> [ChildProperty] -> Property metatypes
    addChildren (Property metatypes
    t String
    d Maybe (Propellor Result)
    a Info
    i [ChildProperty]
    c) [ChildProperty]
    c' = metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property metatypes
    forall metatypes.
    metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property metatypes
    Property metatypes
    t String
    d Maybe (Propellor Result)
    a Info
    i ([ChildProperty]
    c [ChildProperty] -> [ChildProperty] -> [ChildProperty]
    forall a. [a] -> [a] -> [a]
    ++ [ChildProperty]
    c')
    	getInfoRecursive :: Property metatypes -> Info
    getInfoRecursive (Property metatypes
    _ String
    _ Maybe (Propellor Result)
    _ Info
    i [ChildProperty]
    c) =
    		Info
    i Info -> Info -> Info
    forall a. Semigroup a => a -> a -> a
    <> [Info] -> Info
    forall a. Monoid a => [a] -> a
    mconcat ((ChildProperty -> Info) -> [ChildProperty] -> [Info]
    forall a b. (a -> b) -> [a] -> [b]
    map ChildProperty -> Info
    forall p. IsProp p => p -> Info
    getInfoRecursive [ChildProperty]
    c)
    	getInfo :: Property metatypes -> Info
    getInfo (Property metatypes
    _ String
    _ Maybe (Propellor Result)
    _ Info
    i [ChildProperty]
    _) = Info
    i
    	toChildProperty :: Property metatypes -> ChildProperty
    toChildProperty (Property metatypes
    _ String
    d Maybe (Propellor Result)
    a Info
    i [ChildProperty]
    c) = String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> ChildProperty
    ChildProperty String
    d Maybe (Propellor Result)
    a Info
    i [ChildProperty]
    c
    	getSatisfy :: Property metatypes -> Maybe (Propellor Result)
    getSatisfy (Property metatypes
    _ String
    _ Maybe (Propellor Result)
    a Info
    _ [ChildProperty]
    _) = Maybe (Propellor Result)
    a

    instance IsProp (RevertableProperty setupmetatypes undometatypes) where
    	-- | Sets the description of both sides.
    	setDesc :: RevertableProperty setupmetatypes undometatypes
    -> String -> RevertableProperty setupmetatypes undometatypes
    setDesc (RevertableProperty Property setupmetatypes
    p1 Property undometatypes
    p2) String
    d =
    		Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    RevertableProperty (Property setupmetatypes -> String -> Property setupmetatypes
    forall p. IsProp p => p -> String -> p
    setDesc Property setupmetatypes
    p1 String
    d) (Property undometatypes -> String -> Property undometatypes
    forall p. IsProp p => p -> String -> p
    setDesc Property undometatypes
    p2 (String
    "not " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    d))
    	getDesc :: RevertableProperty setupmetatypes undometatypes -> String
    getDesc (RevertableProperty Property setupmetatypes
    p1 Property undometatypes
    _) = Property setupmetatypes -> String
    forall p. IsProp p => p -> String
    getDesc Property setupmetatypes
    p1
    	getChildren :: RevertableProperty setupmetatypes undometatypes -> [ChildProperty]
    getChildren (RevertableProperty Property setupmetatypes
    p1 Property undometatypes
    _) = Property setupmetatypes -> [ChildProperty]
    forall p. IsProp p => p -> [ChildProperty]
    getChildren Property setupmetatypes
    p1
    	-- | Only add children to the active side.
    	addChildren :: RevertableProperty setupmetatypes undometatypes
    -> [ChildProperty]
    -> RevertableProperty setupmetatypes undometatypes
    addChildren (RevertableProperty Property setupmetatypes
    p1 Property undometatypes
    p2) [ChildProperty]
    c = Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    RevertableProperty (Property setupmetatypes
    -> [ChildProperty] -> Property setupmetatypes
    forall p. IsProp p => p -> [ChildProperty] -> p
    addChildren Property setupmetatypes
    p1 [ChildProperty]
    c) Property undometatypes
    p2
    	-- | Return the Info of the currently active side.
    	getInfoRecursive :: RevertableProperty setupmetatypes undometatypes -> Info
    getInfoRecursive (RevertableProperty Property setupmetatypes
    p1 Property undometatypes
    _p2) = Property setupmetatypes -> Info
    forall p. IsProp p => p -> Info
    getInfoRecursive Property setupmetatypes
    p1
    	getInfo :: RevertableProperty setupmetatypes undometatypes -> Info
    getInfo (RevertableProperty Property setupmetatypes
    p1 Property undometatypes
    _p2) = Property setupmetatypes -> Info
    forall p. IsProp p => p -> Info
    getInfo Property setupmetatypes
    p1
    	toChildProperty :: RevertableProperty setupmetatypes undometatypes -> ChildProperty
    toChildProperty (RevertableProperty Property setupmetatypes
    p1 Property undometatypes
    _p2) = Property setupmetatypes -> ChildProperty
    forall p. IsProp p => p -> ChildProperty
    toChildProperty Property setupmetatypes
    p1
    	getSatisfy :: RevertableProperty setupmetatypes undometatypes
    -> Maybe (Propellor Result)
    getSatisfy (RevertableProperty Property setupmetatypes
    p1 Property undometatypes
    _) = Property setupmetatypes -> Maybe (Propellor Result)
    forall p. IsProp p => p -> Maybe (Propellor Result)
    getSatisfy Property setupmetatypes
    p1

    -- | Type level calculation of the type that results from combining two
    -- types of properties.
    type family CombinedType x y where
    	CombinedType (Property (MetaTypes x)) (Property (MetaTypes y)) =
    		Property (MetaTypes (Combine x y))
    	CombinedType
    		(RevertableProperty (MetaTypes x) (MetaTypes x'))
    		(RevertableProperty (MetaTypes y) (MetaTypes y')) =
    			RevertableProperty (MetaTypes (Combine x y)) (MetaTypes (Combine x' y'))
    	-- When only one of the properties is revertable, the combined
    	-- property is not fully revertable, so is not a RevertableProperty.
    	CombinedType (RevertableProperty (MetaTypes x) (MetaTypes x')) (Property (MetaTypes y)) =
    		Property (MetaTypes (Combine x y))
    	CombinedType (Property (MetaTypes x)) (RevertableProperty (MetaTypes y) (MetaTypes y')) =
    		Property (MetaTypes (Combine x y))

    type ResultCombiner = Maybe (Propellor Result) -> Maybe (Propellor Result) -> Maybe (Propellor Result)

    class Combines x y where
    	-- | Combines together two properties, yielding a property that
    	-- has the description and info of the first, and that has the
    	-- second property as a child property.
    	combineWith
    		:: ResultCombiner
    		-- ^ How to combine the actions to satisfy the properties.
    		-> ResultCombiner
    		-- ^ Used when combining revertable properties, to combine
    		-- their reversion actions.
    		-> x
    		-> y
    		-> CombinedType x y

    instance (CheckCombinable x y, SingI (Combine x y)) => Combines (Property (MetaTypes x)) (Property (MetaTypes y)) where
    	combineWith :: ResultCombiner
    -> ResultCombiner
    -> Property (MetaTypes x)
    -> Property (MetaTypes y)
    -> CombinedType (Property (MetaTypes x)) (Property (MetaTypes y))
    combineWith ResultCombiner
    f ResultCombiner
    _ (Property MetaTypes x
    _ String
    d1 Maybe (Propellor Result)
    a1 Info
    i1 [ChildProperty]
    c1) (Property MetaTypes y
    _ String
    d2 Maybe (Propellor Result)
    a2 Info
    i2 [ChildProperty]
    c2) =
    		Sing (Combine x y)
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property (Sing (Combine x y))
    forall metatypes.
    metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property metatypes
    Property Sing (Combine x y)
    forall {k} (t :: k). SingI t => Sing t
    sing String
    d1 (ResultCombiner
    f Maybe (Propellor Result)
    a1 Maybe (Propellor Result)
    a2) Info
    i1 (String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> ChildProperty
    ChildProperty String
    d2 Maybe (Propellor Result)
    a2 Info
    i2 [ChildProperty]
    c2 ChildProperty -> [ChildProperty] -> [ChildProperty]
    forall a. a -> [a] -> [a]
    : [ChildProperty]
    c1)
    instance (CheckCombinable x y, CheckCombinable x' y', SingI (Combine x y), SingI (Combine x' y')) => Combines (RevertableProperty (MetaTypes x) (MetaTypes x')) (RevertableProperty (MetaTypes y) (MetaTypes y')) where
    	combineWith :: ResultCombiner
    -> ResultCombiner
    -> RevertableProperty (MetaTypes x) (MetaTypes x')
    -> RevertableProperty (MetaTypes y) (MetaTypes y')
    -> CombinedType
         (RevertableProperty (MetaTypes x) (MetaTypes x'))
         (RevertableProperty (MetaTypes y) (MetaTypes y'))
    combineWith ResultCombiner
    sf ResultCombiner
    tf (RevertableProperty Property (MetaTypes x)
    s1 Property (MetaTypes x')
    t1) (RevertableProperty Property (MetaTypes y)
    s2 Property (MetaTypes y')
    t2) =
    		Property (MetaTypes (Combine x y))
    -> Property (MetaTypes (Combine x' y'))
    -> RevertableProperty
         (MetaTypes (Combine x y)) (MetaTypes (Combine x' y'))
    forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    RevertableProperty
    			(ResultCombiner
    -> ResultCombiner
    -> Property (MetaTypes x)
    -> Property (MetaTypes y)
    -> CombinedType (Property (MetaTypes x)) (Property (MetaTypes y))
    forall x y.
    Combines x y =>
    ResultCombiner -> ResultCombiner -> x -> y -> CombinedType x y
    combineWith ResultCombiner
    sf ResultCombiner
    tf Property (MetaTypes x)
    s1 Property (MetaTypes y)
    s2)
    			(ResultCombiner
    -> ResultCombiner
    -> Property (MetaTypes x')
    -> Property (MetaTypes y')
    -> CombinedType (Property (MetaTypes x')) (Property (MetaTypes y'))
    forall x y.
    Combines x y =>
    ResultCombiner -> ResultCombiner -> x -> y -> CombinedType x y
    combineWith ResultCombiner
    tf ResultCombiner
    sf Property (MetaTypes x')
    t1 Property (MetaTypes y')
    t2)
    instance (CheckCombinable x y, SingI (Combine x y)) => Combines (RevertableProperty (MetaTypes x) (MetaTypes x')) (Property (MetaTypes y)) where
    	combineWith :: ResultCombiner
    -> ResultCombiner
    -> RevertableProperty (MetaTypes x) (MetaTypes x')
    -> Property (MetaTypes y)
    -> CombinedType
         (RevertableProperty (MetaTypes x) (MetaTypes x'))
         (Property (MetaTypes y))
    combineWith ResultCombiner
    sf ResultCombiner
    tf (RevertableProperty Property (MetaTypes x)
    x Property (MetaTypes x')
    _) Property (MetaTypes y)
    y = ResultCombiner
    -> ResultCombiner
    -> Property (MetaTypes x)
    -> Property (MetaTypes y)
    -> CombinedType (Property (MetaTypes x)) (Property (MetaTypes y))
    forall x y.
    Combines x y =>
    ResultCombiner -> ResultCombiner -> x -> y -> CombinedType x y
    combineWith ResultCombiner
    sf ResultCombiner
    tf Property (MetaTypes x)
    x Property (MetaTypes y)
    y
    instance (CheckCombinable x y, SingI (Combine x y)) => Combines (Property (MetaTypes x)) (RevertableProperty (MetaTypes y) (MetaTypes y')) where
    	combineWith :: ResultCombiner
    -> ResultCombiner
    -> Property (MetaTypes x)
    -> RevertableProperty (MetaTypes y) (MetaTypes y')
    -> CombinedType
         (Property (MetaTypes x))
         (RevertableProperty (MetaTypes y) (MetaTypes y'))
    combineWith ResultCombiner
    sf ResultCombiner
    tf Property (MetaTypes x)
    x (RevertableProperty Property (MetaTypes y)
    y Property (MetaTypes y')
    _) = ResultCombiner
    -> ResultCombiner
    -> Property (MetaTypes x)
    -> Property (MetaTypes y)
    -> CombinedType (Property (MetaTypes x)) (Property (MetaTypes y))
    forall x y.
    Combines x y =>
    ResultCombiner -> ResultCombiner -> x -> y -> CombinedType x y
    combineWith ResultCombiner
    sf ResultCombiner
    tf Property (MetaTypes x)
    x Property (MetaTypes y)
    y

    class TightenTargets p where
    	-- | Tightens the MetaType list of a Property (or similar),
    	-- to contain fewer targets.
    	--
    	-- For example, to make a property that uses apt-get, which is only
    	-- available on DebianLike systems:
    	--
    	-- > upgraded :: Property DebianLike
    	-- > upgraded = tightenTargets $ cmdProperty "apt-get" ["upgrade"]
    	tightenTargets
    		::
    			( TightenTargetsAllowed untightened tightened
    			, SingI tightened
    			)
    		=> p (MetaTypes untightened)
    		-> p (MetaTypes tightened)

    -- Note that this uses PolyKinds
    type family TightenTargetsAllowed untightened tightened :: Constraint where
    	TightenTargetsAllowed untightened tightened =
    		If (Targets tightened `IsSubset` Targets untightened
    		    && NonTargets untightened `IsSubset` NonTargets tightened)
    			('True ~ 'True)
    			(IfStuck (Targets tightened)
    				(DelayError
    					('Text "Unable to infer desired Property type in this use of tightenTargets."
    					 ':$$: ('Text "Consider adding a type annotation.")
    					)
    				)
    				(DelayErrorFcf
    					('Text "This use of tightenTargets would widen, not narrow, adding: "
    					 ':$$: PrettyPrintMetaTypes (Difference (Targets tightened) (Targets untightened))
    					)
    				)
    			)

    instance TightenTargets Property where
    	tightenTargets :: forall (untightened :: [MetaType]) (tightened :: [MetaType]).
    (TightenTargetsAllowed untightened tightened, SingI tightened) =>
    Property (MetaTypes untightened) -> Property (MetaTypes tightened)
    tightenTargets (Property MetaTypes untightened
    _ String
    d Maybe (Propellor Result)
    a Info
    i [ChildProperty]
    c) = MetaTypes tightened
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property (MetaTypes tightened)
    forall metatypes.
    metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property metatypes
    Property MetaTypes tightened
    forall {k} (t :: k). SingI t => Sing t
    sing String
    d Maybe (Propellor Result)
    a Info
    i [ChildProperty]
    c

    -- | Any type of Property is a Semigroup. When properties x and y are
    -- appended together, the resulting property has a description like
    -- "x and y". Note that when x fails to be ensured, it will not
    -- try to ensure y.
    instance SingI metatypes => Sem.Semigroup (Property (MetaTypes metatypes))
      where
    	Property MetaTypes metatypes
    _ String
    d1 Maybe (Propellor Result)
    a1 Info
    i1 [ChildProperty]
    c1 <> :: Property (MetaTypes metatypes)
    -> Property (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    <> Property MetaTypes metatypes
    _ String
    d2 Maybe (Propellor Result)
    a2 Info
    i2 [ChildProperty]
    c2 =
    	  	MetaTypes metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property (MetaTypes metatypes)
    forall metatypes.
    metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property metatypes
    Property MetaTypes metatypes
    forall {k} (t :: k). SingI t => Sing t
    sing String
    d (Maybe (Propellor Result)
    a1 ResultCombiner
    forall a. Semigroup a => a -> a -> a
    <> Maybe (Propellor Result)
    a2) (Info
    i1 Info -> Info -> Info
    forall a. Semigroup a => a -> a -> a
    <> Info
    i2) ([ChildProperty]
    c1 [ChildProperty] -> [ChildProperty] -> [ChildProperty]
    forall a. Semigroup a => a -> a -> a
    <> [ChildProperty]
    c2)
    	  where
    		-- Avoid including "noop property" in description
    		-- when using eg mconcat.
    		d :: String
    d = case (Maybe (Propellor Result)
    a1, Maybe (Propellor Result)
    a2) of
    			(Just Propellor Result
    _, Just Propellor Result
    _) -> String
    d1 String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> String
    " and " String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> String
    d2
    			(Just Propellor Result
    _, Maybe (Propellor Result)
    Nothing) -> String
    d1
    			(Maybe (Propellor Result)
    Nothing, Just Propellor Result
    _) -> String
    d2
    			(Maybe (Propellor Result)
    Nothing, Maybe (Propellor Result)
    Nothing) -> String
    d1

    -- | Any type of Property is a Monoid.
    instance SingI metatypes => Monoid (Property (MetaTypes metatypes))
      where
    	-- | A property that does nothing.
    	mempty :: Property (MetaTypes metatypes)
    mempty = MetaTypes metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property (MetaTypes metatypes)
    forall metatypes.
    metatypes
    -> String
    -> Maybe (Propellor Result)
    -> Info
    -> [ChildProperty]
    -> Property metatypes
    Property MetaTypes metatypes
    forall {k} (t :: k). SingI t => Sing t
    sing String
    "noop property" Maybe (Propellor Result)
    forall a. Maybe a
    Nothing Info
    forall a. Monoid a => a
    mempty [ChildProperty]
    forall a. Monoid a => a
    mempty
    	mappend :: Property (MetaTypes metatypes)
    -> Property (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    mappend = Property (MetaTypes metatypes)
    -> Property (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    forall a. Semigroup a => a -> a -> a
    (Sem.<>)

    -- | Any type of RevertableProperty is a Semigroup. When revertable
    -- properties x and y are appended together, the resulting revertable
    -- property has a description like "x and y".
    -- Note that when x fails to be ensured, it will not try to ensure y.
    instance
    	( Sem.Semigroup (Property (MetaTypes setupmetatypes))
    	, Sem.Semigroup (Property (MetaTypes undometatypes))
    	, SingI setupmetatypes
    	, SingI undometatypes
    	)
    	=> Sem.Semigroup (RevertableProperty (MetaTypes setupmetatypes) (MetaTypes undometatypes))
      where
    	RevertableProperty Property (MetaTypes setupmetatypes)
    s1 Property (MetaTypes undometatypes)
    u1 <> :: RevertableProperty
      (MetaTypes setupmetatypes) (MetaTypes undometatypes)
    -> RevertableProperty
         (MetaTypes setupmetatypes) (MetaTypes undometatypes)
    -> RevertableProperty
         (MetaTypes setupmetatypes) (MetaTypes undometatypes)
    <> RevertableProperty Property (MetaTypes setupmetatypes)
    s2 Property (MetaTypes undometatypes)
    u2 =
    		Property (MetaTypes setupmetatypes)
    -> Property (MetaTypes undometatypes)
    -> RevertableProperty
         (MetaTypes setupmetatypes) (MetaTypes undometatypes)
    forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    RevertableProperty (Property (MetaTypes setupmetatypes)
    s1 Property (MetaTypes setupmetatypes)
    -> Property (MetaTypes setupmetatypes)
    -> Property (MetaTypes setupmetatypes)
    forall a. Semigroup a => a -> a -> a
    <> Property (MetaTypes setupmetatypes)
    s2) (Property (MetaTypes undometatypes)
    u2 Property (MetaTypes undometatypes)
    -> Property (MetaTypes undometatypes)
    -> Property (MetaTypes undometatypes)
    forall a. Semigroup a => a -> a -> a
    <> Property (MetaTypes undometatypes)
    u1)

    instance
    	( Monoid (Property (MetaTypes setupmetatypes))
    	, Monoid (Property (MetaTypes undometatypes))
    	, SingI setupmetatypes
    	, SingI undometatypes
    	)
    	=> Monoid (RevertableProperty (MetaTypes setupmetatypes) (MetaTypes undometatypes))
      where
    	mempty :: RevertableProperty
      (MetaTypes setupmetatypes) (MetaTypes undometatypes)
    mempty = Property (MetaTypes setupmetatypes)
    -> Property (MetaTypes undometatypes)
    -> RevertableProperty
         (MetaTypes setupmetatypes) (MetaTypes undometatypes)
    forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    RevertableProperty Property (MetaTypes setupmetatypes)
    forall a. Monoid a => a
    mempty Property (MetaTypes undometatypes)
    forall a. Monoid a => a
    mempty
    	mappend :: RevertableProperty
      (MetaTypes setupmetatypes) (MetaTypes undometatypes)
    -> RevertableProperty
         (MetaTypes setupmetatypes) (MetaTypes undometatypes)
    -> RevertableProperty
         (MetaTypes setupmetatypes) (MetaTypes undometatypes)
    mappend = RevertableProperty
      (MetaTypes setupmetatypes) (MetaTypes undometatypes)
    -> RevertableProperty
         (MetaTypes setupmetatypes) (MetaTypes undometatypes)
    -> RevertableProperty
         (MetaTypes setupmetatypes) (MetaTypes undometatypes)
    forall a. Semigroup a => a -> a -> a
    (Sem.<>)