module Ganeti.BasicTypes
( GenericResult(..)
, genericResult
, Result
, ResultT(..)
, resultT
, FromString(..)
, isOk
, isBad
, eitherToResult
, annotateResult
, iterateOk
, select
, runListHead
, LookupResult(..)
, MatchPriority(..)
, lookupName
, goodLookupResult
, goodMatchPriority
, prefixMatch
, compareNameComponent
, ListSet(..)
, emptyListSet
) where
import Control.Applicative
import Control.Monad
import Control.Monad.Trans
import Data.Function
import Data.List
import Data.Maybe (listToMaybe)
import Data.Set (Set)
import qualified Data.Set as Set (empty)
import Text.JSON (JSON)
import qualified Text.JSON as JSON (readJSON, showJSON)
data GenericResult a b
= Bad a
| Ok b
deriving (Show, Eq)
genericResult :: (a -> c) -> (b -> c) -> GenericResult a b -> c
genericResult f _ (Bad a) = f a
genericResult _ g (Ok b) = g b
type Result = GenericResult String
class FromString a where
mkFromString :: String -> a
instance FromString [Char] where
mkFromString = id
instance (FromString a) => Monad (GenericResult a) where
(>>=) (Bad x) _ = Bad x
(>>=) (Ok x) fn = fn x
return = Ok
fail = Bad . mkFromString
instance Functor (GenericResult a) where
fmap _ (Bad msg) = Bad msg
fmap fn (Ok val) = Ok (fn val)
instance MonadPlus (GenericResult String) where
mzero = Bad "zero Result when used as MonadPlus"
(Bad x) `mplus` (Bad y) = Bad (x ++ "; " ++ y)
(Bad _) `mplus` x = x
x@(Ok _) `mplus` _ = x
instance Applicative (GenericResult a) where
pure = Ok
(Bad f) <*> _ = Bad f
_ <*> (Bad x) = Bad x
(Ok f) <*> (Ok x) = Ok $ f x
newtype ResultT a m b = ResultT {runResultT :: m (GenericResult a b)}
instance (Monad m, FromString a) => Monad (ResultT a m) where
fail err = ResultT (return . Bad $ mkFromString err)
return = lift . return
x >>= f = ResultT $ do
a <- runResultT x
case a of
Ok val -> runResultT $ f val
Bad err -> return $ Bad err
instance MonadTrans (ResultT a) where
lift x = ResultT (liftM Ok x)
instance (MonadIO m, FromString a) => MonadIO (ResultT a m) where
liftIO = lift . liftIO
resultT :: Monad m => GenericResult a b -> ResultT a m b
resultT = ResultT . return
isOk :: GenericResult a b -> Bool
isOk (Ok _) = True
isOk _ = False
isBad :: GenericResult a b -> Bool
isBad = not . isOk
eitherToResult :: Either a b -> GenericResult a b
eitherToResult (Left s) = Bad s
eitherToResult (Right v) = Ok v
annotateResult :: String -> Result a -> Result a
annotateResult owner (Bad s) = Bad $ owner ++ ": " ++ s
annotateResult _ v = v
iterateOk :: (a -> GenericResult b a) -> a -> [a]
iterateOk f a = genericResult (const []) ((:) a . iterateOk f) (f a)
select :: a
-> [(Bool, a)]
-> a
select def = maybe def snd . find fst
runListHead :: a -> (b -> a) -> [b] -> a
runListHead a f = maybe a f . listToMaybe
data MatchPriority = ExactMatch
| MultipleMatch
| PartialMatch
| FailMatch
deriving (Show, Enum, Eq, Ord)
data LookupResult = LookupResult
{ lrMatchPriority :: MatchPriority
, lrContent :: String
} deriving (Show)
instance Eq LookupResult where
(==) = (==) `on` lrMatchPriority
instance Ord LookupResult where
compare = compare `on` lrMatchPriority
prefixMatch :: String
-> String
-> Bool
prefixMatch = isPrefixOf . (++ ".")
goodMatchPriority :: MatchPriority -> Bool
goodMatchPriority ExactMatch = True
goodMatchPriority PartialMatch = True
goodMatchPriority _ = False
goodLookupResult :: LookupResult -> Bool
goodLookupResult = goodMatchPriority . lrMatchPriority
compareNameComponent :: String
-> String
-> LookupResult
compareNameComponent cnl lkp =
select (LookupResult FailMatch lkp)
[ (cnl == lkp , LookupResult ExactMatch cnl)
, (prefixMatch lkp cnl , LookupResult PartialMatch cnl)
]
chooseLookupResult :: String
-> String
-> LookupResult
-> LookupResult
chooseLookupResult lkp cstr old =
select (min new old)
[ (lrMatchPriority new == ExactMatch, new)
, (partial2, LookupResult MultipleMatch lkp)
] where new = compareNameComponent cstr lkp
partial2 = all ((PartialMatch==) . lrMatchPriority) [old, new]
lookupName :: [String]
-> String
-> LookupResult
lookupName l s = foldr (chooseLookupResult s)
(LookupResult FailMatch s) l
newtype ListSet a = ListSet { unListSet :: Set a }
deriving (Eq, Show)
instance (Ord a, JSON a) => JSON (ListSet a) where
showJSON = JSON.showJSON . unListSet
readJSON = liftM ListSet . JSON.readJSON
emptyListSet :: ListSet a
emptyListSet = ListSet Set.empty