Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / BasicTypes.hs @ e817723c

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