dojang-0.1.0: src/Dojang/Types/EnvironmentPredicate/Specificity.hs
-- | This module contains the 'Specificity' type and the 'specificity' function,
-- which are used to determine how specific an 'EnvironmentPredicate' is, i.e.,
-- when there are multiple predicates that could match, which one should be
-- chosen.
module Dojang.Types.EnvironmentPredicate.Specificity
( Specificity (..)
, specificity
) where
import Dojang.Types.EnvironmentPredicate (EnvironmentPredicate (..))
import Dojang.Types.MonikerName (MonikerName)
-- $setup
-- >>> :seti -XOverloadedStrings
-- >>> :seti -XOverloadedLists
-- | The specificity of an 'EnvironmentPredicate'. It guarantees that
-- the more specific predicate will always be greater than the less specific
-- predicate (e.g. @And [OperatingSystem "linux", Architecture "x86_64"]@ is
-- more specific than @OperatingSystem "linux"@).
data Specificity = Specificity
{ maxPredicates :: Int
-- ^ The number of predicates that must be evaluated at a maximum.
, totalPredicates :: Int
-- ^ The total number of predicates regardless of whether they would be
-- evaluated or not (e.g. if the first predicate in an 'Or' matches,
-- then the second predicate would not be evaluated, but it would still be
-- counted).
}
deriving (Eq, Show, Read)
instance Ord Specificity where
compare (Specificity maxA totalA) (Specificity maxB totalB) =
case compare maxA maxB of
EQ -> compare totalB totalA
x -> x
-- | Calculates the 'Specificity' of an 'EnvironmentPredicate'. For details
-- on how the 'Specificity' is calculated, see the documentation for
-- 'Specificity'.
--
-- >>> import Data.List (sortOn)
-- >>> import Dojang.Types.EnvironmentPredicate (EnvironmentPredicate (..))
-- >>> :{
-- let predicates =
-- [ OperatingSystem "linux"
-- , Architecture "x86_64"
-- , And [OperatingSystem "linux", Architecture "x86_64"]
-- , Or [OperatingSystem "linux", Architecture "x86_64"]
-- ] :: [EnvironmentPredicate]
-- :}
--
-- >>> sortOn (specificity (const Nothing)) predicates
-- [Or (...),OperatingSystem Linux,Architecture X86_64,And (...)]
specificity
:: (MonikerName -> Maybe EnvironmentPredicate)
-> EnvironmentPredicate
-> Specificity
specificity _ Always = Specificity 0 0
specificity monikerResolver (Not predicate) =
specificity monikerResolver predicate
specificity monikerResolver (And predicates) =
let qs = specificity monikerResolver <$> predicates
in Specificity (sum $ maxPredicates <$> qs) (sum $ totalPredicates <$> qs)
specificity monikerResolver (Or predicates) =
let qs = specificity monikerResolver <$> predicates
in Specificity (maximum $ maxPredicates <$> qs) (sum $ totalPredicates <$> qs)
specificity monikerResolver (Moniker monikerName) =
case monikerResolver monikerName of
Nothing -> Specificity 0 0
Just predicate -> specificity monikerResolver predicate
specificity _ (OperatingSystem _) = Specificity 1 1
specificity _ (Architecture _) = Specificity 1 1
specificity _ (KernelName _) = Specificity 1 1
specificity _ (KernelRelease _) = Specificity 1 1
specificity _ (KernelReleasePrefix _) = Specificity 1 1
specificity _ (KernelReleaseSuffix _) = Specificity 1 1