Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / BasicTypes.hs @ d250bc5d

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