module Ganeti.BasicTypes
( GenericResult(..)
, genericResult
, Result
, ResultT(..)
, resultT
, FromString(..)
, isOk
, isBad
, eitherToResult
, annotateResult
, iterateOk
, select
, LookupResult(..)
, MatchPriority(..)
, lookupName
, goodLookupResult
, goodMatchPriority
, prefixMatch
, compareNameComponent
) where
import Control.Applicative
import Control.Monad
import Control.Monad.Trans
import Data.Function
import Data.List
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
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