Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / BasicTypes.hs @ cd67e337

History | View | Annotate | Download (6.7 kB)

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