Revision 948f6540 src/Ganeti/THH.hs

b/src/Ganeti/THH.hs
67 67
                  , buildObjectSerialisation
68 68
                  , buildParam
69 69
                  , DictObject(..)
70
                  , ArrayObject(..)
70 71
                  , genException
71 72
                  , excErrMsg
72 73
                  ) where
......
83 84
import qualified Data.Map as M
84 85
import qualified Data.Set as Set
85 86
import Language.Haskell.TH
87
import Language.Haskell.TH.Syntax (lift)
86 88
import System.Time (ClockTime(..))
87 89

  
88 90
import qualified Text.JSON as JSON
......
101 103
  toDict :: a -> [(String, JSON.JSValue)]
102 104
  fromDict :: [(String, JSON.JSValue)] -> JSON.Result a
103 105

  
106
-- | Class of objects that can be converted from and to @[JSValue]@ with
107
-- a fixed length and order.
108
class ArrayObject a where
109
  toJSArray :: a -> [JSON.JSValue]
110
  fromJSArray :: [JSON.JSValue] -> JSON.Result a
111

  
104 112
-- | Optional field information.
105 113
data OptionalType
106 114
  = NotOptional           -- ^ Field is not optional
......
1008 1016
objVarName :: Name
1009 1017
objVarName = mkName "_o"
1010 1018

  
1019
-- | Provides a default 'toJSArray' for 'ArrayObject' instance using its
1020
-- existing 'DictObject' instance. The keys are serialized in the order
1021
-- they're declared. The list must contain all keys possibly generated by
1022
-- 'toDict'.
1023
defaultToJSArray :: (DictObject a) => [String] -> a -> [JSON.JSValue]
1024
defaultToJSArray keys o =
1025
  let m = M.fromList $ toDict o
1026
  in map (fromMaybe JSON.JSNull . flip M.lookup m) keys
1027

  
1028
-- | Provides a default 'fromJSArray' for 'ArrayObject' instance using its
1029
-- existing 'DictObject' instance. The fields are deserialized in the order
1030
-- they're declared.
1031
defaultFromJSArray :: (DictObject a)
1032
                   => [String] -> [JSON.JSValue] -> JSON.Result a
1033
defaultFromJSArray keys xs = do
1034
  let xslen = length xs
1035
      explen = length keys
1036
  unless (xslen == explen) (fail $ "Expected " ++ show explen
1037
                                   ++ " arguments, got " ++ show xslen)
1038
  fromDict $ zip keys xs
1039

  
1040
-- | Generates an additional 'ArrayObject' instance using its
1041
-- existing 'DictObject' instance.
1042
--
1043
-- See 'defaultToJSArray' and 'defaultFromJSArray'.
1044
genArrayObjectInstance :: Name -> [Field] -> Q Dec
1045
genArrayObjectInstance name fields = do
1046
  let fnames = concatMap (liftA2 (:) fieldName fieldExtraKeys) fields
1047
  instanceD (return []) (appT (conT ''ArrayObject) (conT name))
1048
    [ valD (varP 'toJSArray) (normalB [| defaultToJSArray $(lift fnames) |]) []
1049
    , valD (varP 'fromJSArray) (normalB [| defaultFromJSArray fnames |]) []
1050
    ]
1051

  
1011 1052
-- | Generates 'DictObject' instance.
1012 1053
genDictObject :: (Name -> Field -> Q Exp)  -- ^ a saving function
1013 1054
              -> (Field -> Q Exp)          -- ^ a loading function
......
1024 1065
  -- fromDict
1025 1066
  fdexp <- loadConstructor name load_fn fields
1026 1067
  let fdclause = Clause [VarP objVarName] (NormalB fdexp) []
1068
  -- the ArrayObject instance generated from DictObject
1069
  arrdec <- genArrayObjectInstance name fields
1027 1070
  -- the final instance
1028
  return [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
1029
           [ FunD 'toDict [tdclause]
1030
           , FunD 'fromDict [fdclause]
1031
           ]]
1071
  return $ [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
1072
             [ FunD 'toDict [tdclause]
1073
             , FunD 'fromDict [fdclause]
1074
             ]]
1075
         ++ [arrdec]
1032 1076

  
1033 1077
-- | Generates the save object functionality.
1034 1078
genSaveObject :: String -> Q [Dec]

Also available in: Unified diff