Revision 942403e6 Ganeti/HTools/Utils.hs
b/Ganeti/HTools/Utils.hs | ||
---|---|---|
3 | 3 |
module Ganeti.HTools.Utils |
4 | 4 |
( |
5 | 5 |
debug |
6 |
, isLeft |
|
7 |
, fromLeft |
|
8 |
, fromRight |
|
9 | 6 |
, sepSplit |
10 | 7 |
, swapPairs |
11 | 8 |
, varianceCoeff |
12 | 9 |
, readData |
13 | 10 |
, commaJoin |
14 |
, combineEithers |
|
15 |
, ensureEitherList |
|
16 |
, eitherListHead |
|
17 | 11 |
, readEitherString |
18 |
, parseEitherList |
|
19 | 12 |
, loadJSArray |
20 | 13 |
, fromObj |
21 | 14 |
, getStringElement |
... | ... | |
25 | 18 |
, getObjectElement |
26 | 19 |
, asJSObject |
27 | 20 |
, asObjectList |
28 |
, concatEitherElems
|
|
29 |
, applyEither1
|
|
30 |
, applyEither2
|
|
21 |
, Result(Ok, Bad)
|
|
22 |
, fromJResult
|
|
23 |
, (|+)
|
|
31 | 24 |
) where |
32 | 25 |
|
33 | 26 |
import Data.Either |
34 | 27 |
import Data.List |
35 |
import Monad |
|
28 |
import Control.Monad
|
|
36 | 29 |
import System |
37 | 30 |
import System.IO |
38 |
import Text.JSON
|
|
31 |
import qualified Text.JSON as J
|
|
39 | 32 |
import Text.Printf (printf) |
40 | 33 |
|
41 | 34 |
import Debug.Trace |
... | ... | |
44 | 37 |
debug :: Show a => a -> a |
45 | 38 |
debug x = trace (show x) x |
46 | 39 |
|
47 |
-- | Check if the given argument is Left something |
|
48 |
isLeft :: Either a b -> Bool |
|
49 |
isLeft val = |
|
50 |
case val of |
|
51 |
Left _ -> True |
|
52 |
_ -> False |
|
53 | 40 |
|
54 |
fromLeft :: Either a b -> a |
|
55 |
fromLeft = either (\x -> x) (\_ -> undefined) |
|
41 |
{- |
|
56 | 42 |
|
57 |
fromRight :: Either a b -> b |
|
58 |
fromRight = either (\_ -> undefined) id |
|
43 |
This is similar to the JSON library Result type - *very* similar, but |
|
44 |
we want to use it in multiple places, so we abstract it into a |
|
45 |
mini-library here |
|
46 |
|
|
47 |
-} |
|
48 |
|
|
49 |
data Result a |
|
50 |
= Bad String |
|
51 |
| Ok a |
|
52 |
deriving (Show) |
|
53 |
|
|
54 |
instance Monad Result where |
|
55 |
(>>=) (Bad x) _ = Bad x |
|
56 |
(>>=) (Ok x) fn = fn x |
|
57 |
return = Ok |
|
58 |
fail = Bad |
|
59 |
|
|
60 |
fromJResult :: J.Result a -> Result a |
|
61 |
fromJResult (J.Error x) = Bad x |
|
62 |
fromJResult (J.Ok x) = Ok x |
|
59 | 63 |
|
60 | 64 |
-- | Comma-join a string list. |
61 | 65 |
commaJoin :: [String] -> String |
... | ... | |
98 | 102 |
varianceCoeff :: Floating a => [a] -> a |
99 | 103 |
varianceCoeff lst = (stdDev lst) / (fromIntegral $ length lst) |
100 | 104 |
|
101 |
-- | Get a Right result or print the error and exit |
|
102 |
readData :: (String -> IO (Either String String)) -> String -> IO String |
|
103 |
readData fn host = do |
|
104 |
nd <- fn host |
|
105 |
when (isLeft nd) $ |
|
106 |
do |
|
107 |
putStrLn $ fromLeft nd |
|
105 |
-- | Get an Ok result or print the error and exit |
|
106 |
readData :: Result a -> IO a |
|
107 |
readData nd = |
|
108 |
(case nd of |
|
109 |
Bad x -> do |
|
110 |
putStrLn x |
|
108 | 111 |
exitWith $ ExitFailure 1 |
109 |
return $ fromRight nd |
|
110 |
|
|
111 |
{-- Our cheap monad-like stuff. |
|
112 |
Ok x -> return x) |
|
112 | 113 |
|
113 |
Thi is needed since Either e a is already a monad instance somewhere |
|
114 |
in the standard libraries (Control.Monad.Error) and we don't need that |
|
115 |
entire thing. |
|
116 |
|
|
117 |
-} |
|
118 |
combineEithers :: (Either String a) |
|
119 |
-> (a -> Either String b) |
|
120 |
-> (Either String b) |
|
121 |
combineEithers (Left s) _ = Left s |
|
122 |
combineEithers (Right s) f = f s |
|
123 |
|
|
124 |
ensureEitherList :: [Either String a] -> Either String [a] |
|
125 |
ensureEitherList lst = |
|
126 |
foldr (\elem accu -> |
|
127 |
case (elem, accu) of |
|
128 |
(Left x, _) -> Left x |
|
129 |
(_, Left x) -> Left x -- should never happen |
|
130 |
(Right e, Right a) -> Right (e:a) |
|
131 |
) |
|
132 |
(Right []) lst |
|
133 |
|
|
134 |
eitherListHead :: Either String [a] -> Either String a |
|
135 |
eitherListHead lst = |
|
136 |
case lst of |
|
137 |
Left x -> Left x |
|
138 |
Right (x:_) -> Right x |
|
139 |
Right [] -> Left "List empty" |
|
140 |
|
|
141 |
readEitherString :: JSValue -> Either String String |
|
114 |
readEitherString :: J.JSValue -> Result String |
|
142 | 115 |
readEitherString v = |
143 | 116 |
case v of |
144 |
JSString s -> Right $ fromJSString s |
|
145 |
_ -> Left "Wrong JSON type" |
|
146 |
|
|
147 |
parseEitherList :: (JSObject JSValue -> Either String String) |
|
148 |
-> [JSObject JSValue] |
|
149 |
-> Either String String |
|
150 |
parseEitherList fn idata = |
|
151 |
let ml = ensureEitherList $ map fn idata |
|
152 |
in ml `combineEithers` (Right . unlines) |
|
117 |
J.JSString s -> Ok $ J.fromJSString s |
|
118 |
_ -> Bad "Wrong JSON type" |
|
153 | 119 |
|
154 |
loadJSArray :: String -> Either String [JSObject JSValue]
|
|
155 |
loadJSArray s = resultToEither $ decodeStrict s
|
|
120 |
loadJSArray :: String -> Result [J.JSObject J.JSValue]
|
|
121 |
loadJSArray s = fromJResult $ J.decodeStrict s
|
|
156 | 122 |
|
157 |
fromObj :: JSON a => String -> JSObject JSValue -> Either String a
|
|
123 |
fromObj :: J.JSON a => String -> J.JSObject J.JSValue -> Result a
|
|
158 | 124 |
fromObj k o = |
159 |
case lookup k (fromJSObject o) of |
|
160 |
Nothing -> Left $ printf "key '%s' not found" k
|
|
161 |
Just val -> resultToEither $ readJSON val
|
|
125 |
case lookup k (J.fromJSObject o) of
|
|
126 |
Nothing -> Bad $ printf "key '%s' not found" k
|
|
127 |
Just val -> fromJResult $ J.readJSON val
|
|
162 | 128 |
|
163 |
getStringElement :: String -> JSObject JSValue -> Either String String
|
|
129 |
getStringElement :: String -> J.JSObject J.JSValue -> Result String
|
|
164 | 130 |
getStringElement = fromObj |
165 | 131 |
|
166 |
getIntElement :: String -> JSObject JSValue -> Either String Int
|
|
132 |
getIntElement :: String -> J.JSObject J.JSValue -> Result Int
|
|
167 | 133 |
getIntElement = fromObj |
168 | 134 |
|
169 |
getBoolElement :: String -> JSObject JSValue -> Either String Bool
|
|
135 |
getBoolElement :: String -> J.JSObject J.JSValue -> Result Bool
|
|
170 | 136 |
getBoolElement = fromObj |
171 | 137 |
|
172 |
getListElement :: String -> JSObject JSValue |
|
173 |
-> Either String [JSValue] |
|
138 |
getListElement :: String -> J.JSObject J.JSValue -> Result [J.JSValue] |
|
174 | 139 |
getListElement = fromObj |
175 | 140 |
|
176 |
getObjectElement :: String -> JSObject JSValue
|
|
177 |
-> Either String (JSObject JSValue)
|
|
141 |
getObjectElement :: String -> J.JSObject J.JSValue
|
|
142 |
-> Result (J.JSObject J.JSValue)
|
|
178 | 143 |
getObjectElement = fromObj |
179 | 144 |
|
180 |
asJSObject :: JSValue -> Either String (JSObject JSValue) |
|
181 |
asJSObject (JSObject a) = Right a |
|
182 |
asJSObject _ = Left "not an object" |
|
183 |
|
|
184 |
asObjectList :: [JSValue] -> Either String [JSObject JSValue] |
|
185 |
asObjectList = |
|
186 |
ensureEitherList . map asJSObject |
|
187 |
|
|
188 |
concatEitherElems :: Either String String |
|
189 |
-> Either String String |
|
190 |
-> Either String String |
|
191 |
concatEitherElems = applyEither2 (\x y -> x ++ "|" ++ y) |
|
192 |
|
|
193 |
applyEither1 :: (a -> b) -> Either String a -> Either String b |
|
194 |
applyEither1 fn a = |
|
195 |
case a of |
|
196 |
Left x -> Left x |
|
197 |
Right y -> Right $ fn y |
|
198 |
|
|
199 |
applyEither2 :: (a -> b -> c) |
|
200 |
-> Either String a |
|
201 |
-> Either String b |
|
202 |
-> Either String c |
|
203 |
applyEither2 fn a b = |
|
204 |
case (a, b) of |
|
205 |
(Right x, Right y) -> Right $ fn x y |
|
206 |
(Left x, _) -> Left x |
|
207 |
(_, Left y) -> Left y |
|
145 |
asJSObject :: J.JSValue -> Result (J.JSObject J.JSValue) |
|
146 |
asJSObject (J.JSObject a) = Ok a |
|
147 |
asJSObject _ = Bad "not an object" |
|
148 |
|
|
149 |
asObjectList :: [J.JSValue] -> Result [J.JSObject J.JSValue] |
|
150 |
asObjectList = sequence . map asJSObject |
|
151 |
|
|
152 |
-- | Function to concat two strings with a separator under a monad |
|
153 |
(|+) :: (Monad m) => m String -> m String -> m String |
|
154 |
(|+) = liftM2 (\x y -> x ++ "|" ++ y) |
Also available in: Unified diff