Utils.hs 3.89 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1
2
{-| Utility functions -}

3
4
5
6
7
8
9
module Ganeti.HTools.Utils
    (
      debug
    , sepSplit
    , swapPairs
    , varianceCoeff
    , readData
10
    , commaJoin
11
12
13
14
15
    , readEitherString
    , loadJSArray
    , fromObj
    , getStringElement
    , getIntElement
Iustin Pop's avatar
Iustin Pop committed
16
    , getBoolElement
17
    , getListElement
Iustin Pop's avatar
Iustin Pop committed
18
19
20
    , getObjectElement
    , asJSObject
    , asObjectList
21
22
23
    , Result(Ok, Bad)
    , fromJResult
    , (|+)
24
    ) where
Iustin Pop's avatar
Iustin Pop committed
25

Iustin Pop's avatar
Iustin Pop committed
26
import Data.Either
27
import Data.List
28
import Control.Monad
Iustin Pop's avatar
Iustin Pop committed
29
30
import System
import System.IO
31
import qualified Text.JSON as J
32
import Text.Printf (printf)
Iustin Pop's avatar
Iustin Pop committed
33
34
35
36
37
38
39

import Debug.Trace

-- | To be used only for debugging, breaks referential integrity.
debug :: Show a => a -> a
debug x = trace (show x) x

Iustin Pop's avatar
Iustin Pop committed
40

41
{-
Iustin Pop's avatar
Iustin Pop committed
42

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
This is similar to the JSON library Result type - *very* similar, but
we want to use it in multiple places, so we abstract it into a
mini-library here

-}

data Result a
    = Bad String
    | Ok a
    deriving (Show)

instance Monad Result where
    (>>=) (Bad x) _ = Bad x
    (>>=) (Ok x) fn = fn x
    return = Ok
    fail = Bad

fromJResult :: J.Result a -> Result a
fromJResult (J.Error x) = Bad x
fromJResult (J.Ok x) = Ok x
Iustin Pop's avatar
Iustin Pop committed
63

Iustin Pop's avatar
Iustin Pop committed
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
-- | Comma-join a string list.
commaJoin :: [String] -> String
commaJoin = intercalate ","

-- | Split a string on a separator and return an array.
sepSplit :: Char -> String -> [String]
sepSplit sep s
    | x == "" && xs == [] = []
    | xs == []            = [x]
    | ys == []            = x:"":[]
    | otherwise           = x:(sepSplit sep ys)
    where (x, xs) = break (== sep) s
          ys = drop 1 xs

-- | Partial application of sepSplit to @'.'@
commaSplit :: String -> [String]
commaSplit = sepSplit ','

-- | Swap a list of @(a, b)@ into @(b, a)@
swapPairs :: [(a, b)] -> [(b, a)]
swapPairs = map (\ (a, b) -> (b, a))

-- Simple and slow statistical functions, please replace with better versions

-- | Mean value of a list.
meanValue :: Floating a => [a] -> a
meanValue lst = (sum lst) / (fromIntegral $ length lst)

-- | Standard deviation.
stdDev :: Floating a => [a] -> a
stdDev lst =
    let mv = meanValue lst
        square = (^ (2::Int)) -- silences "defaulting the constraint..."
        av = sum $ map square $ map (\e -> e - mv) lst
        bv = sqrt (av / (fromIntegral $ length lst))
    in bv

-- | Coefficient of variation.
varianceCoeff :: Floating a => [a] -> a
varianceCoeff lst = (stdDev lst) / (fromIntegral $ length lst)
Iustin Pop's avatar
Iustin Pop committed
104

105
106
107
108
109
110
-- | Get an Ok result or print the error and exit
readData :: Result a -> IO a
readData nd =
    (case nd of
       Bad x -> do
         putStrLn x
Iustin Pop's avatar
Iustin Pop committed
111
         exitWith $ ExitFailure 1
112
       Ok x -> return x)
113

114
readEitherString :: J.JSValue -> Result String
115
116
readEitherString v =
    case v of
117
118
      J.JSString s -> Ok $ J.fromJSString s
      _ -> Bad "Wrong JSON type"
119

120
121
loadJSArray :: String -> Result [J.JSObject J.JSValue]
loadJSArray s = fromJResult $ J.decodeStrict s
122

123
fromObj :: J.JSON a => String -> J.JSObject J.JSValue -> Result a
124
fromObj k o =
125
126
127
    case lookup k (J.fromJSObject o) of
      Nothing -> Bad $ printf "key '%s' not found" k
      Just val -> fromJResult $ J.readJSON val
128

129
getStringElement :: String -> J.JSObject J.JSValue -> Result String
130
131
getStringElement = fromObj

132
getIntElement :: String -> J.JSObject J.JSValue -> Result Int
133
134
getIntElement = fromObj

135
getBoolElement :: String -> J.JSObject J.JSValue -> Result Bool
Iustin Pop's avatar
Iustin Pop committed
136
137
getBoolElement = fromObj

138
getListElement :: String -> J.JSObject J.JSValue -> Result [J.JSValue]
139
140
getListElement = fromObj

141
142
getObjectElement :: String -> J.JSObject J.JSValue
                 -> Result (J.JSObject J.JSValue)
Iustin Pop's avatar
Iustin Pop committed
143
144
getObjectElement = fromObj

145
146
147
148
149
150
151
152
153
154
asJSObject :: J.JSValue -> Result (J.JSObject J.JSValue)
asJSObject (J.JSObject a) = Ok a
asJSObject _ = Bad "not an object"

asObjectList :: [J.JSValue] -> Result [J.JSObject J.JSValue]
asObjectList = sequence . map asJSObject

-- | Function to concat two strings with a separator under a monad
(|+) :: (Monad m) => m String -> m String -> m String
(|+) = liftM2 (\x y -> x ++ "|" ++ y)