Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / BasicTypes.hs @ ea128e20

History | View | Annotate | Download (8.1 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 9491766c Hrvoje Ribicic
  , justOk
34 ea128e20 Klaus Aehlig
  , justBad
35 0c37d1e4 Iustin Pop
  , eitherToResult
36 f3f76ccc Iustin Pop
  , annotateResult
37 b1c772fd Klaus Aehlig
  , iterateOk
38 2fc5653f Iustin Pop
  , select
39 2fc5653f Iustin Pop
  , LookupResult(..)
40 2fc5653f Iustin Pop
  , MatchPriority(..)
41 2fc5653f Iustin Pop
  , lookupName
42 2fc5653f Iustin Pop
  , goodLookupResult
43 2fc5653f Iustin Pop
  , goodMatchPriority
44 2fc5653f Iustin Pop
  , prefixMatch
45 2fc5653f Iustin Pop
  , compareNameComponent
46 4651c69f Jose A. Lopes
  , ListSet(..)
47 4651c69f Jose A. Lopes
  , emptyListSet
48 0c37d1e4 Iustin Pop
  ) where
49 0c37d1e4 Iustin Pop
50 25779212 Iustin Pop
import Control.Applicative
51 0c37d1e4 Iustin Pop
import Control.Monad
52 d71fbcc5 Agata Murawska
import Control.Monad.Trans
53 2fc5653f Iustin Pop
import Data.Function
54 2fc5653f Iustin Pop
import Data.List
55 ea128e20 Klaus Aehlig
import Data.Maybe
56 4651c69f Jose A. Lopes
import Data.Set (Set)
57 4651c69f Jose A. Lopes
import qualified Data.Set as Set (empty)
58 4651c69f Jose A. Lopes
import Text.JSON (JSON)
59 4651c69f Jose A. Lopes
import qualified Text.JSON as JSON (readJSON, showJSON)
60 0c37d1e4 Iustin Pop
61 93be1ced Iustin Pop
-- | Generic monad for our error handling mechanisms.
62 93be1ced Iustin Pop
data GenericResult a b
63 93be1ced Iustin Pop
  = Bad a
64 93be1ced Iustin Pop
  | Ok b
65 139c0683 Iustin Pop
    deriving (Show, Eq)
66 0c37d1e4 Iustin Pop
67 b74ebe44 Klaus Aehlig
-- | Sum type structure of GenericResult.
68 b74ebe44 Klaus Aehlig
genericResult :: (a -> c) -> (b -> c) -> GenericResult a b -> c
69 b74ebe44 Klaus Aehlig
genericResult f _ (Bad a) = f a
70 b74ebe44 Klaus Aehlig
genericResult _ g (Ok b) = g b
71 b74ebe44 Klaus Aehlig
72 93be1ced Iustin Pop
-- | Type alias for a string Result.
73 93be1ced Iustin Pop
type Result = GenericResult String
74 93be1ced Iustin Pop
75 93be1ced Iustin Pop
-- | Type class for things that can be built from strings.
76 93be1ced Iustin Pop
class FromString a where
77 93be1ced Iustin Pop
  mkFromString :: String -> a
78 93be1ced Iustin Pop
79 93be1ced Iustin Pop
-- | Trivial 'String' instance; requires FlexibleInstances extension
80 93be1ced Iustin Pop
-- though.
81 93be1ced Iustin Pop
instance FromString [Char] where
82 93be1ced Iustin Pop
  mkFromString = id
83 93be1ced Iustin Pop
84 93be1ced Iustin Pop
-- | 'Monad' instance for 'GenericResult'.
85 93be1ced Iustin Pop
instance (FromString a) => Monad (GenericResult a) where
86 0c37d1e4 Iustin Pop
  (>>=) (Bad x) _ = Bad x
87 0c37d1e4 Iustin Pop
  (>>=) (Ok x) fn = fn x
88 0c37d1e4 Iustin Pop
  return = Ok
89 93be1ced Iustin Pop
  fail   = Bad . mkFromString
90 0c37d1e4 Iustin Pop
91 93be1ced Iustin Pop
instance Functor (GenericResult a) where
92 a9ccc950 Iustin Pop
  fmap _ (Bad msg) = Bad msg
93 a9ccc950 Iustin Pop
  fmap fn (Ok val) = Ok (fn val)
94 a9ccc950 Iustin Pop
95 93be1ced Iustin Pop
instance MonadPlus (GenericResult String) where
96 0c37d1e4 Iustin Pop
  mzero = Bad "zero Result when used as MonadPlus"
97 0c37d1e4 Iustin Pop
  -- for mplus, when we 'add' two Bad values, we concatenate their
98 0c37d1e4 Iustin Pop
  -- error descriptions
99 0c37d1e4 Iustin Pop
  (Bad x) `mplus` (Bad y) = Bad (x ++ "; " ++ y)
100 0c37d1e4 Iustin Pop
  (Bad _) `mplus` x = x
101 0c37d1e4 Iustin Pop
  x@(Ok _) `mplus` _ = x
102 0c37d1e4 Iustin Pop
103 93be1ced Iustin Pop
instance Applicative (GenericResult a) where
104 25779212 Iustin Pop
  pure = Ok
105 25779212 Iustin Pop
  (Bad f) <*> _       = Bad f
106 25779212 Iustin Pop
  _       <*> (Bad x) = Bad x
107 25779212 Iustin Pop
  (Ok f)  <*> (Ok x)  = Ok $ f x
108 25779212 Iustin Pop
109 d71fbcc5 Agata Murawska
-- | This is a monad transformation for Result. It's implementation is
110 d71fbcc5 Agata Murawska
-- based on the implementations of MaybeT and ErrorT.
111 93be1ced Iustin Pop
newtype ResultT a m b = ResultT {runResultT :: m (GenericResult a b)}
112 d71fbcc5 Agata Murawska
113 93be1ced Iustin Pop
instance (Monad m, FromString a) => Monad (ResultT a m) where
114 93be1ced Iustin Pop
  fail err = ResultT (return . Bad $ mkFromString err)
115 274366e5 Agata Murawska
  return   = lift . return
116 274366e5 Agata Murawska
  x >>= f  = ResultT $ do
117 274366e5 Agata Murawska
               a <- runResultT x
118 274366e5 Agata Murawska
               case a of
119 274366e5 Agata Murawska
                 Ok val -> runResultT $ f val
120 274366e5 Agata Murawska
                 Bad err -> return $ Bad err
121 d71fbcc5 Agata Murawska
122 93be1ced Iustin Pop
instance MonadTrans (ResultT a) where
123 d71fbcc5 Agata Murawska
  lift x = ResultT (liftM Ok x)
124 d71fbcc5 Agata Murawska
125 93be1ced Iustin Pop
instance (MonadIO m, FromString a) => MonadIO (ResultT a m) where
126 d71fbcc5 Agata Murawska
  liftIO = lift . liftIO
127 d71fbcc5 Agata Murawska
128 d71fbcc5 Agata Murawska
-- | Lift a `Result` value to a `ResultT`.
129 93be1ced Iustin Pop
resultT :: Monad m => GenericResult a b -> ResultT a m b
130 d71fbcc5 Agata Murawska
resultT = ResultT . return
131 d71fbcc5 Agata Murawska
132 93be1ced Iustin Pop
-- | Simple checker for whether a 'GenericResult' is OK.
133 93be1ced Iustin Pop
isOk :: GenericResult a b -> Bool
134 0c37d1e4 Iustin Pop
isOk (Ok _) = True
135 93be1ced Iustin Pop
isOk _      = False
136 0c37d1e4 Iustin Pop
137 93be1ced Iustin Pop
-- | Simple checker for whether a 'GenericResult' is a failure.
138 93be1ced Iustin Pop
isBad :: GenericResult a b -> Bool
139 0c37d1e4 Iustin Pop
isBad = not . isOk
140 0c37d1e4 Iustin Pop
141 9491766c Hrvoje Ribicic
-- | Simple filter returning only OK values of GenericResult
142 9491766c Hrvoje Ribicic
justOk :: [GenericResult a b] -> [b]
143 ea128e20 Klaus Aehlig
justOk = mapMaybe (genericResult (const Nothing) Just)
144 ea128e20 Klaus Aehlig
145 ea128e20 Klaus Aehlig
-- | Simple filter returning only Bad values of GenericResult
146 ea128e20 Klaus Aehlig
justBad :: [GenericResult a b] -> [a]
147 ea128e20 Klaus Aehlig
justBad = mapMaybe (genericResult Just (const Nothing))
148 9491766c Hrvoje Ribicic
149 98508e7f Dato Simó
-- | Converter from Either to 'GenericResult'.
150 93be1ced Iustin Pop
eitherToResult :: Either a b -> GenericResult a b
151 93be1ced Iustin Pop
eitherToResult (Left  s) = Bad s
152 93be1ced Iustin Pop
eitherToResult (Right v) = Ok  v
153 f3f76ccc Iustin Pop
154 f3f76ccc Iustin Pop
-- | Annotate a Result with an ownership information.
155 f3f76ccc Iustin Pop
annotateResult :: String -> Result a -> Result a
156 f3f76ccc Iustin Pop
annotateResult owner (Bad s) = Bad $ owner ++ ": " ++ s
157 f3f76ccc Iustin Pop
annotateResult _ v = v
158 1091021c Iustin Pop
159 b1c772fd Klaus Aehlig
-- | Iterate while Ok.
160 b1c772fd Klaus Aehlig
iterateOk :: (a -> GenericResult b a) -> a -> [a]
161 b1c772fd Klaus Aehlig
iterateOk f a = genericResult (const []) ((:) a . iterateOk f) (f a)
162 b1c772fd Klaus Aehlig
163 2fc5653f Iustin Pop
-- * Misc functionality
164 2fc5653f Iustin Pop
165 2fc5653f Iustin Pop
-- | Return the first result with a True condition, or the default otherwise.
166 2fc5653f Iustin Pop
select :: a            -- ^ default result
167 2fc5653f Iustin Pop
       -> [(Bool, a)]  -- ^ list of \"condition, result\"
168 2fc5653f Iustin Pop
       -> a            -- ^ first result which has a True condition, or default
169 2fc5653f Iustin Pop
select def = maybe def snd . find fst
170 2fc5653f Iustin Pop
171 2fc5653f Iustin Pop
-- * Lookup of partial names functionality
172 2fc5653f Iustin Pop
173 2fc5653f Iustin Pop
-- | The priority of a match in a lookup result.
174 2fc5653f Iustin Pop
data MatchPriority = ExactMatch
175 2fc5653f Iustin Pop
                   | MultipleMatch
176 2fc5653f Iustin Pop
                   | PartialMatch
177 2fc5653f Iustin Pop
                   | FailMatch
178 139c0683 Iustin Pop
                   deriving (Show, Enum, Eq, Ord)
179 2fc5653f Iustin Pop
180 2fc5653f Iustin Pop
-- | The result of a name lookup in a list.
181 2fc5653f Iustin Pop
data LookupResult = LookupResult
182 2fc5653f Iustin Pop
  { lrMatchPriority :: MatchPriority -- ^ The result type
183 2fc5653f Iustin Pop
  -- | Matching value (for ExactMatch, PartialMatch), Lookup string otherwise
184 2fc5653f Iustin Pop
  , lrContent :: String
185 139c0683 Iustin Pop
  } deriving (Show)
186 2fc5653f Iustin Pop
187 2fc5653f Iustin Pop
-- | Lookup results have an absolute preference ordering.
188 2fc5653f Iustin Pop
instance Eq LookupResult where
189 2fc5653f Iustin Pop
  (==) = (==) `on` lrMatchPriority
190 2fc5653f Iustin Pop
191 2fc5653f Iustin Pop
instance Ord LookupResult where
192 2fc5653f Iustin Pop
  compare = compare `on` lrMatchPriority
193 2fc5653f Iustin Pop
194 2fc5653f Iustin Pop
-- | Check for prefix matches in names.
195 2fc5653f Iustin Pop
-- Implemented in Ganeti core utils.text.MatchNameComponent
196 2fc5653f Iustin Pop
-- as the regexp r"^%s(\..*)?$" % re.escape(key)
197 2fc5653f Iustin Pop
prefixMatch :: String  -- ^ Lookup
198 2fc5653f Iustin Pop
            -> String  -- ^ Full name
199 2fc5653f Iustin Pop
            -> Bool    -- ^ Whether there is a prefix match
200 2fc5653f Iustin Pop
prefixMatch = isPrefixOf . (++ ".")
201 2fc5653f Iustin Pop
202 2fc5653f Iustin Pop
-- | Is the lookup priority a "good" one?
203 2fc5653f Iustin Pop
goodMatchPriority :: MatchPriority -> Bool
204 2fc5653f Iustin Pop
goodMatchPriority ExactMatch = True
205 2fc5653f Iustin Pop
goodMatchPriority PartialMatch = True
206 2fc5653f Iustin Pop
goodMatchPriority _ = False
207 2fc5653f Iustin Pop
208 2fc5653f Iustin Pop
-- | Is the lookup result an actual match?
209 2fc5653f Iustin Pop
goodLookupResult :: LookupResult -> Bool
210 2fc5653f Iustin Pop
goodLookupResult = goodMatchPriority . lrMatchPriority
211 2fc5653f Iustin Pop
212 2fc5653f Iustin Pop
-- | Compares a canonical name and a lookup string.
213 2fc5653f Iustin Pop
compareNameComponent :: String        -- ^ Canonical (target) name
214 2fc5653f Iustin Pop
                     -> String        -- ^ Partial (lookup) name
215 2fc5653f Iustin Pop
                     -> LookupResult  -- ^ Result of the lookup
216 2fc5653f Iustin Pop
compareNameComponent cnl lkp =
217 2fc5653f Iustin Pop
  select (LookupResult FailMatch lkp)
218 2fc5653f Iustin Pop
  [ (cnl == lkp          , LookupResult ExactMatch cnl)
219 2fc5653f Iustin Pop
  , (prefixMatch lkp cnl , LookupResult PartialMatch cnl)
220 2fc5653f Iustin Pop
  ]
221 2fc5653f Iustin Pop
222 2fc5653f Iustin Pop
-- | Lookup a string and choose the best result.
223 2fc5653f Iustin Pop
chooseLookupResult :: String       -- ^ Lookup key
224 2fc5653f Iustin Pop
                   -> String       -- ^ String to compare to the lookup key
225 2fc5653f Iustin Pop
                   -> LookupResult -- ^ Previous result
226 2fc5653f Iustin Pop
                   -> LookupResult -- ^ New result
227 2fc5653f Iustin Pop
chooseLookupResult lkp cstr old =
228 2fc5653f Iustin Pop
  -- default: use class order to pick the minimum result
229 2fc5653f Iustin Pop
  select (min new old)
230 2fc5653f Iustin Pop
  -- special cases:
231 2fc5653f Iustin Pop
  -- short circuit if the new result is an exact match
232 2fc5653f Iustin Pop
  [ (lrMatchPriority new == ExactMatch, new)
233 2fc5653f Iustin Pop
  -- if both are partial matches generate a multiple match
234 2fc5653f Iustin Pop
  , (partial2, LookupResult MultipleMatch lkp)
235 2fc5653f Iustin Pop
  ] where new = compareNameComponent cstr lkp
236 2fc5653f Iustin Pop
          partial2 = all ((PartialMatch==) . lrMatchPriority) [old, new]
237 2fc5653f Iustin Pop
238 2fc5653f Iustin Pop
-- | Find the canonical name for a lookup string in a list of names.
239 2fc5653f Iustin Pop
lookupName :: [String]      -- ^ List of keys
240 2fc5653f Iustin Pop
           -> String        -- ^ Lookup string
241 2fc5653f Iustin Pop
           -> LookupResult  -- ^ Result of the lookup
242 2fc5653f Iustin Pop
lookupName l s = foldr (chooseLookupResult s)
243 2fc5653f Iustin Pop
                       (LookupResult FailMatch s) l
244 4651c69f Jose A. Lopes
245 4651c69f Jose A. Lopes
-- | Wrapper for a Haskell 'Set'
246 4651c69f Jose A. Lopes
--
247 4651c69f Jose A. Lopes
-- This type wraps a 'Set' and it is used in the Haskell to Python
248 4651c69f Jose A. Lopes
-- opcode generation to transform a Haskell 'Set' into a Python 'list'
249 4651c69f Jose A. Lopes
-- without duplicate elements.
250 4651c69f Jose A. Lopes
newtype ListSet a = ListSet { unListSet :: Set a }
251 4651c69f Jose A. Lopes
  deriving (Eq, Show)
252 4651c69f Jose A. Lopes
253 4651c69f Jose A. Lopes
instance (Ord a, JSON a) => JSON (ListSet a) where
254 4651c69f Jose A. Lopes
  showJSON = JSON.showJSON . unListSet
255 4651c69f Jose A. Lopes
  readJSON = liftM ListSet . JSON.readJSON
256 4651c69f Jose A. Lopes
257 4651c69f Jose A. Lopes
emptyListSet :: ListSet a
258 4651c69f Jose A. Lopes
emptyListSet = ListSet Set.empty