Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / BasicTypes.hs @ 82b948e4

History | View | Annotate | Download (7.2 kB)

1 93be1ced Iustin Pop
{-# LANGUAGE FlexibleInstances #-}
2 93be1ced Iustin Pop
3 0c37d1e4 Iustin Pop
{-
4 0c37d1e4 Iustin Pop
5 1091021c Iustin Pop
Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
6 0c37d1e4 Iustin Pop
7 0c37d1e4 Iustin Pop
This program is free software; you can redistribute it and/or modify
8 0c37d1e4 Iustin Pop
it under the terms of the GNU General Public License as published by
9 0c37d1e4 Iustin Pop
the Free Software Foundation; either version 2 of the License, or
10 0c37d1e4 Iustin Pop
(at your option) any later version.
11 0c37d1e4 Iustin Pop
12 0c37d1e4 Iustin Pop
This program is distributed in the hope that it will be useful, but
13 0c37d1e4 Iustin Pop
WITHOUT ANY WARRANTY; without even the implied warranty of
14 0c37d1e4 Iustin Pop
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 0c37d1e4 Iustin Pop
General Public License for more details.
16 0c37d1e4 Iustin Pop
17 0c37d1e4 Iustin Pop
You should have received a copy of the GNU General Public License
18 0c37d1e4 Iustin Pop
along with this program; if not, write to the Free Software
19 0c37d1e4 Iustin Pop
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 0c37d1e4 Iustin Pop
02110-1301, USA.
21 0c37d1e4 Iustin Pop
22 0c37d1e4 Iustin Pop
-}
23 0c37d1e4 Iustin Pop
24 0c37d1e4 Iustin Pop
module Ganeti.BasicTypes
25 93be1ced Iustin Pop
  ( GenericResult(..)
26 b74ebe44 Klaus Aehlig
  , genericResult
27 93be1ced Iustin Pop
  , Result
28 d71fbcc5 Agata Murawska
  , ResultT(..)
29 d71fbcc5 Agata Murawska
  , resultT
30 93be1ced Iustin Pop
  , FromString(..)
31 0c37d1e4 Iustin Pop
  , isOk
32 0c37d1e4 Iustin Pop
  , isBad
33 0c37d1e4 Iustin Pop
  , eitherToResult
34 f3f76ccc Iustin Pop
  , annotateResult
35 b1c772fd Klaus Aehlig
  , iterateOk
36 2fc5653f Iustin Pop
  , select
37 2fc5653f Iustin Pop
  , LookupResult(..)
38 2fc5653f Iustin Pop
  , MatchPriority(..)
39 2fc5653f Iustin Pop
  , lookupName
40 2fc5653f Iustin Pop
  , goodLookupResult
41 2fc5653f Iustin Pop
  , goodMatchPriority
42 2fc5653f Iustin Pop
  , prefixMatch
43 2fc5653f Iustin Pop
  , compareNameComponent
44 0c37d1e4 Iustin Pop
  ) where
45 0c37d1e4 Iustin Pop
46 25779212 Iustin Pop
import Control.Applicative
47 0c37d1e4 Iustin Pop
import Control.Monad
48 d71fbcc5 Agata Murawska
import Control.Monad.Trans
49 2fc5653f Iustin Pop
import Data.Function
50 2fc5653f Iustin Pop
import Data.List
51 0c37d1e4 Iustin Pop
52 93be1ced Iustin Pop
-- | Generic monad for our error handling mechanisms.
53 93be1ced Iustin Pop
data GenericResult a b
54 93be1ced Iustin Pop
  = Bad a
55 93be1ced Iustin Pop
  | Ok b
56 139c0683 Iustin Pop
    deriving (Show, Eq)
57 0c37d1e4 Iustin Pop
58 b74ebe44 Klaus Aehlig
-- | Sum type structure of GenericResult.
59 b74ebe44 Klaus Aehlig
genericResult :: (a -> c) -> (b -> c) -> GenericResult a b -> c
60 b74ebe44 Klaus Aehlig
genericResult f _ (Bad a) = f a
61 b74ebe44 Klaus Aehlig
genericResult _ g (Ok b) = g b
62 b74ebe44 Klaus Aehlig
63 93be1ced Iustin Pop
-- | Type alias for a string Result.
64 93be1ced Iustin Pop
type Result = GenericResult String
65 93be1ced Iustin Pop
66 93be1ced Iustin Pop
-- | Type class for things that can be built from strings.
67 93be1ced Iustin Pop
class FromString a where
68 93be1ced Iustin Pop
  mkFromString :: String -> a
69 93be1ced Iustin Pop
70 93be1ced Iustin Pop
-- | Trivial 'String' instance; requires FlexibleInstances extension
71 93be1ced Iustin Pop
-- though.
72 93be1ced Iustin Pop
instance FromString [Char] where
73 93be1ced Iustin Pop
  mkFromString = id
74 93be1ced Iustin Pop
75 93be1ced Iustin Pop
-- | 'Monad' instance for 'GenericResult'.
76 93be1ced Iustin Pop
instance (FromString a) => Monad (GenericResult a) where
77 0c37d1e4 Iustin Pop
  (>>=) (Bad x) _ = Bad x
78 0c37d1e4 Iustin Pop
  (>>=) (Ok x) fn = fn x
79 0c37d1e4 Iustin Pop
  return = Ok
80 93be1ced Iustin Pop
  fail   = Bad . mkFromString
81 0c37d1e4 Iustin Pop
82 93be1ced Iustin Pop
instance Functor (GenericResult a) where
83 a9ccc950 Iustin Pop
  fmap _ (Bad msg) = Bad msg
84 a9ccc950 Iustin Pop
  fmap fn (Ok val) = Ok (fn val)
85 a9ccc950 Iustin Pop
86 93be1ced Iustin Pop
instance MonadPlus (GenericResult String) where
87 0c37d1e4 Iustin Pop
  mzero = Bad "zero Result when used as MonadPlus"
88 0c37d1e4 Iustin Pop
  -- for mplus, when we 'add' two Bad values, we concatenate their
89 0c37d1e4 Iustin Pop
  -- error descriptions
90 0c37d1e4 Iustin Pop
  (Bad x) `mplus` (Bad y) = Bad (x ++ "; " ++ y)
91 0c37d1e4 Iustin Pop
  (Bad _) `mplus` x = x
92 0c37d1e4 Iustin Pop
  x@(Ok _) `mplus` _ = x
93 0c37d1e4 Iustin Pop
94 93be1ced Iustin Pop
instance Applicative (GenericResult a) where
95 25779212 Iustin Pop
  pure = Ok
96 25779212 Iustin Pop
  (Bad f) <*> _       = Bad f
97 25779212 Iustin Pop
  _       <*> (Bad x) = Bad x
98 25779212 Iustin Pop
  (Ok f)  <*> (Ok x)  = Ok $ f x
99 25779212 Iustin Pop
100 d71fbcc5 Agata Murawska
-- | This is a monad transformation for Result. It's implementation is
101 d71fbcc5 Agata Murawska
-- based on the implementations of MaybeT and ErrorT.
102 93be1ced Iustin Pop
newtype ResultT a m b = ResultT {runResultT :: m (GenericResult a b)}
103 d71fbcc5 Agata Murawska
104 93be1ced Iustin Pop
instance (Monad m, FromString a) => Monad (ResultT a m) where
105 93be1ced Iustin Pop
  fail err = ResultT (return . Bad $ mkFromString err)
106 274366e5 Agata Murawska
  return   = lift . return
107 274366e5 Agata Murawska
  x >>= f  = ResultT $ do
108 274366e5 Agata Murawska
               a <- runResultT x
109 274366e5 Agata Murawska
               case a of
110 274366e5 Agata Murawska
                 Ok val -> runResultT $ f val
111 274366e5 Agata Murawska
                 Bad err -> return $ Bad err
112 d71fbcc5 Agata Murawska
113 93be1ced Iustin Pop
instance MonadTrans (ResultT a) where
114 d71fbcc5 Agata Murawska
  lift x = ResultT (liftM Ok x)
115 d71fbcc5 Agata Murawska
116 93be1ced Iustin Pop
instance (MonadIO m, FromString a) => MonadIO (ResultT a m) where
117 d71fbcc5 Agata Murawska
  liftIO = lift . liftIO
118 d71fbcc5 Agata Murawska
119 d71fbcc5 Agata Murawska
-- | Lift a `Result` value to a `ResultT`.
120 93be1ced Iustin Pop
resultT :: Monad m => GenericResult a b -> ResultT a m b
121 d71fbcc5 Agata Murawska
resultT = ResultT . return
122 d71fbcc5 Agata Murawska
123 93be1ced Iustin Pop
-- | Simple checker for whether a 'GenericResult' is OK.
124 93be1ced Iustin Pop
isOk :: GenericResult a b -> Bool
125 0c37d1e4 Iustin Pop
isOk (Ok _) = True
126 93be1ced Iustin Pop
isOk _      = False
127 0c37d1e4 Iustin Pop
128 93be1ced Iustin Pop
-- | Simple checker for whether a 'GenericResult' is a failure.
129 93be1ced Iustin Pop
isBad :: GenericResult a b -> Bool
130 0c37d1e4 Iustin Pop
isBad = not . isOk
131 0c37d1e4 Iustin Pop
132 98508e7f Dato Simó
-- | Converter from Either to 'GenericResult'.
133 93be1ced Iustin Pop
eitherToResult :: Either a b -> GenericResult a b
134 93be1ced Iustin Pop
eitherToResult (Left  s) = Bad s
135 93be1ced Iustin Pop
eitherToResult (Right v) = Ok  v
136 f3f76ccc Iustin Pop
137 f3f76ccc Iustin Pop
-- | Annotate a Result with an ownership information.
138 f3f76ccc Iustin Pop
annotateResult :: String -> Result a -> Result a
139 f3f76ccc Iustin Pop
annotateResult owner (Bad s) = Bad $ owner ++ ": " ++ s
140 f3f76ccc Iustin Pop
annotateResult _ v = v
141 1091021c Iustin Pop
142 b1c772fd Klaus Aehlig
-- | Iterate while Ok.
143 b1c772fd Klaus Aehlig
iterateOk :: (a -> GenericResult b a) -> a -> [a]
144 b1c772fd Klaus Aehlig
iterateOk f a = genericResult (const []) ((:) a . iterateOk f) (f a)
145 b1c772fd Klaus Aehlig
146 2fc5653f Iustin Pop
-- * Misc functionality
147 2fc5653f Iustin Pop
148 2fc5653f Iustin Pop
-- | Return the first result with a True condition, or the default otherwise.
149 2fc5653f Iustin Pop
select :: a            -- ^ default result
150 2fc5653f Iustin Pop
       -> [(Bool, a)]  -- ^ list of \"condition, result\"
151 2fc5653f Iustin Pop
       -> a            -- ^ first result which has a True condition, or default
152 2fc5653f Iustin Pop
select def = maybe def snd . find fst
153 2fc5653f Iustin Pop
154 2fc5653f Iustin Pop
-- * Lookup of partial names functionality
155 2fc5653f Iustin Pop
156 2fc5653f Iustin Pop
-- | The priority of a match in a lookup result.
157 2fc5653f Iustin Pop
data MatchPriority = ExactMatch
158 2fc5653f Iustin Pop
                   | MultipleMatch
159 2fc5653f Iustin Pop
                   | PartialMatch
160 2fc5653f Iustin Pop
                   | FailMatch
161 139c0683 Iustin Pop
                   deriving (Show, Enum, Eq, Ord)
162 2fc5653f Iustin Pop
163 2fc5653f Iustin Pop
-- | The result of a name lookup in a list.
164 2fc5653f Iustin Pop
data LookupResult = LookupResult
165 2fc5653f Iustin Pop
  { lrMatchPriority :: MatchPriority -- ^ The result type
166 2fc5653f Iustin Pop
  -- | Matching value (for ExactMatch, PartialMatch), Lookup string otherwise
167 2fc5653f Iustin Pop
  , lrContent :: String
168 139c0683 Iustin Pop
  } deriving (Show)
169 2fc5653f Iustin Pop
170 2fc5653f Iustin Pop
-- | Lookup results have an absolute preference ordering.
171 2fc5653f Iustin Pop
instance Eq LookupResult where
172 2fc5653f Iustin Pop
  (==) = (==) `on` lrMatchPriority
173 2fc5653f Iustin Pop
174 2fc5653f Iustin Pop
instance Ord LookupResult where
175 2fc5653f Iustin Pop
  compare = compare `on` lrMatchPriority
176 2fc5653f Iustin Pop
177 2fc5653f Iustin Pop
-- | Check for prefix matches in names.
178 2fc5653f Iustin Pop
-- Implemented in Ganeti core utils.text.MatchNameComponent
179 2fc5653f Iustin Pop
-- as the regexp r"^%s(\..*)?$" % re.escape(key)
180 2fc5653f Iustin Pop
prefixMatch :: String  -- ^ Lookup
181 2fc5653f Iustin Pop
            -> String  -- ^ Full name
182 2fc5653f Iustin Pop
            -> Bool    -- ^ Whether there is a prefix match
183 2fc5653f Iustin Pop
prefixMatch = isPrefixOf . (++ ".")
184 2fc5653f Iustin Pop
185 2fc5653f Iustin Pop
-- | Is the lookup priority a "good" one?
186 2fc5653f Iustin Pop
goodMatchPriority :: MatchPriority -> Bool
187 2fc5653f Iustin Pop
goodMatchPriority ExactMatch = True
188 2fc5653f Iustin Pop
goodMatchPriority PartialMatch = True
189 2fc5653f Iustin Pop
goodMatchPriority _ = False
190 2fc5653f Iustin Pop
191 2fc5653f Iustin Pop
-- | Is the lookup result an actual match?
192 2fc5653f Iustin Pop
goodLookupResult :: LookupResult -> Bool
193 2fc5653f Iustin Pop
goodLookupResult = goodMatchPriority . lrMatchPriority
194 2fc5653f Iustin Pop
195 2fc5653f Iustin Pop
-- | Compares a canonical name and a lookup string.
196 2fc5653f Iustin Pop
compareNameComponent :: String        -- ^ Canonical (target) name
197 2fc5653f Iustin Pop
                     -> String        -- ^ Partial (lookup) name
198 2fc5653f Iustin Pop
                     -> LookupResult  -- ^ Result of the lookup
199 2fc5653f Iustin Pop
compareNameComponent cnl lkp =
200 2fc5653f Iustin Pop
  select (LookupResult FailMatch lkp)
201 2fc5653f Iustin Pop
  [ (cnl == lkp          , LookupResult ExactMatch cnl)
202 2fc5653f Iustin Pop
  , (prefixMatch lkp cnl , LookupResult PartialMatch cnl)
203 2fc5653f Iustin Pop
  ]
204 2fc5653f Iustin Pop
205 2fc5653f Iustin Pop
-- | Lookup a string and choose the best result.
206 2fc5653f Iustin Pop
chooseLookupResult :: String       -- ^ Lookup key
207 2fc5653f Iustin Pop
                   -> String       -- ^ String to compare to the lookup key
208 2fc5653f Iustin Pop
                   -> LookupResult -- ^ Previous result
209 2fc5653f Iustin Pop
                   -> LookupResult -- ^ New result
210 2fc5653f Iustin Pop
chooseLookupResult lkp cstr old =
211 2fc5653f Iustin Pop
  -- default: use class order to pick the minimum result
212 2fc5653f Iustin Pop
  select (min new old)
213 2fc5653f Iustin Pop
  -- special cases:
214 2fc5653f Iustin Pop
  -- short circuit if the new result is an exact match
215 2fc5653f Iustin Pop
  [ (lrMatchPriority new == ExactMatch, new)
216 2fc5653f Iustin Pop
  -- if both are partial matches generate a multiple match
217 2fc5653f Iustin Pop
  , (partial2, LookupResult MultipleMatch lkp)
218 2fc5653f Iustin Pop
  ] where new = compareNameComponent cstr lkp
219 2fc5653f Iustin Pop
          partial2 = all ((PartialMatch==) . lrMatchPriority) [old, new]
220 2fc5653f Iustin Pop
221 2fc5653f Iustin Pop
-- | Find the canonical name for a lookup string in a list of names.
222 2fc5653f Iustin Pop
lookupName :: [String]      -- ^ List of keys
223 2fc5653f Iustin Pop
           -> String        -- ^ Lookup string
224 2fc5653f Iustin Pop
           -> LookupResult  -- ^ Result of the lookup
225 2fc5653f Iustin Pop
lookupName l s = foldr (chooseLookupResult s)
226 2fc5653f Iustin Pop
                       (LookupResult FailMatch s) l