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