Revision c2e136e2 src/Ganeti/THH.hs

b/src/Ganeti/THH.hs
95 95

  
96 96
-- * Exported types
97 97

  
98
-- | Class of objects that can be converted to 'JSObject'
98
-- | Class of objects that can be converted from and to 'JSObject'
99 99
-- lists-format.
100 100
class DictObject a where
101 101
  toDict :: a -> [(String, JSON.JSValue)]
102
  fromDict :: [(String, JSON.JSValue)] -> JSON.Result a
102 103

  
103 104
-- | Optional field information.
104 105
data OptionalType
......
837 838
  let cname = mkName sname
838 839
  fnames <- mapM (newName . fieldVariable) fields
839 840
  let pat = conP cname (map varP fnames)
840
  let felems = map (uncurry saveObjectField) (zip fnames fields)
841
  let felems = zipWith saveObjectField fnames fields
841 842
      -- now build the OP_ID serialisation
842 843
      opid = [| [( $(stringE "OP_ID"),
843 844
                   JSON.showJSON $(stringE . deCamelCase $ sname) )] |]
......
875 876
         , ValD (VarP jvalname) (NormalB jvalclause) []]
876 877

  
877 878
-- | Generates load code for a single constructor of the opcode data type.
878
loadConstructor :: OpCodeConstructor -> Q Exp
879
loadConstructor (sname, _, _, fields, _) = do
880
  let name = mkName sname
881
  fbinds <- mapM (loadObjectField fields) fields
882
  let (fnames, fstmts) = unzip fbinds
883
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
884
      fstmts' = fstmts ++ [NoBindS (AppE (VarE 'return) cval)]
885
  return $ DoE fstmts'
879
loadConstructor :: Name -> (Field -> Q Exp) -> [Field] -> Q Exp
880
loadConstructor name loadfn fields = do
881
  fnames <- mapM (newName . ("r_" ++) . fieldName) fields
882
  fexps <- mapM loadfn fields
883
  let fstmts = zipWith (BindS . VarP) fnames fexps
884
      cexp = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
885
      retstmt = [NoBindS (AppE (VarE 'return) cexp)]
886
      -- FIXME: should we require an empty dict for an empty type?
887
      -- this allows any JSValue right now
888
  return $ DoE (fstmts ++ retstmt)
889

  
890
-- | Generates load code for a single constructor of the opcode data type.
891
loadOpConstructor :: OpCodeConstructor -> Q Exp
892
loadOpConstructor (sname, _, _, fields, _) =
893
  loadConstructor (mkName sname) (loadObjectField fields) fields
886 894

  
887 895
-- | Generates the loadOpCode function.
888 896
genLoadOpCode :: [OpCodeConstructor] -> Q (Dec, Dec)
889 897
genLoadOpCode opdefs = do
890 898
  let fname = mkName "loadOpCode"
891 899
      arg1 = mkName "v"
892
      objname = mkName "o"
900
      objname = objVarName
893 901
      opid = mkName "op_id"
894 902
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
895 903
                                 (JSON.readJSON $(varE arg1)) |]
896 904
  st2 <- bindS (varP opid) [| $fromObjE $(varE objname) $(stringE "OP_ID") |]
897 905
  -- the match results (per-constructor blocks)
898
  mexps <- mapM loadConstructor opdefs
906
  mexps <- mapM loadOpConstructor opdefs
899 907
  fails <- [| fail $ "Unknown opcode " ++ $(varE opid) |]
900 908
  let mpats = map (\(me, (consName, _, _, _, _)) ->
901 909
                       let mp = LitP . StringL . deCamelCase $ consName
......
953 961
  let cname = mkName sname
954 962
  fnames <- mapM (newName . fieldVariable) fields
955 963
  let pat = conP cname (map varP fnames)
956
  let felems = map (uncurry saveObjectField) (zip fnames fields)
964
  let felems = zipWith saveObjectField fnames fields
957 965
      flist = [| concat $(listE felems) |]
958 966
  clause [pat] (normalB flist) []
959 967

  
......
984 992
buildObjectSerialisation :: String -> [Field] -> Q [Dec]
985 993
buildObjectSerialisation sname fields = do
986 994
  let name = mkName sname
987
  savedecls <- genSaveObject saveObjectField sname fields
988
  (loadsig, loadfn) <- genLoadObject (loadObjectField fields) sname fields
995
  dictdecls <- genDictObject saveObjectField
996
                             (loadObjectField fields) sname fields
997
  savedecls <- genSaveObject sname
998
  (loadsig, loadfn) <- genLoadObject sname
989 999
  shjson <- objectShowJSON sname
990 1000
  rdjson <- objectReadJSON sname
991 1001
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
992 1002
                 [rdjson, shjson]
993
  return $ savedecls ++ [loadsig, loadfn, instdecl]
1003
  return $ dictdecls ++ savedecls ++ [loadsig, loadfn, instdecl]
1004

  
1005
-- | An internal name used for naming variables that hold the entire
1006
-- object of type @[(String,JSValue)]@.
1007
objVarName :: Name
1008
objVarName = mkName "_o"
994 1009

  
995 1010
-- | The toDict function name for a given type.
996 1011
toDictName :: String -> Name
997 1012
toDictName sname = mkName ("toDict" ++ sname)
998 1013

  
999
-- | Generates the save object functionality.
1000
genSaveObject :: (Name -> Field -> Q Exp)
1001
              -> String -> [Field] -> Q [Dec]
1002
genSaveObject save_fn sname fields = do
1014
-- | The fromDict function name for a given type.
1015
fromDictName :: String -> Name
1016
fromDictName sname = mkName ("fromDict" ++ sname)
1017

  
1018
-- | Generates 'DictObject' instance.
1019
genDictObject :: (Name -> Field -> Q Exp)  -- ^ a saving function
1020
              -> (Field -> Q Exp)          -- ^ a loading function
1021
              -> String                    -- ^ an object name
1022
              -> [Field]                   -- ^ a list of fields
1023
              -> Q [Dec]
1024
genDictObject save_fn load_fn sname fields = do
1003 1025
  let name = mkName sname
1026
  -- toDict
1004 1027
  fnames <- mapM (newName . fieldVariable) fields
1005 1028
  let pat = conP name (map varP fnames)
1029
      tdexp = [| concat $(listE $ zipWith save_fn fnames fields) |]
1030
  tdclause <- clause [pat] (normalB tdexp) []
1031
  -- fromDict
1032
  fdexp <- loadConstructor name load_fn fields
1033
  let fdclause = Clause [VarP objVarName] (NormalB fdexp) []
1034
  -- the toDict... function
1006 1035
  let tdname = toDictName sname
1007 1036
  tdsigt <- [t| $(conT name) -> [(String, JSON.JSValue)] |]
1037
  -- the final instance
1038
  return $ [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
1039
             [ ValD (VarP 'toDict) (NormalB (VarE tdname)) []
1040
             , FunD 'fromDict [fdclause]
1041
             ]] ++
1042
           [ SigD tdname tdsigt
1043
           , ValD (VarP tdname) (NormalB (VarE 'toDict)) [] ]
1008 1044

  
1009
  let felems = map (uncurry save_fn) (zip fnames fields)
1010
      flist = listE felems
1011
      -- and finally convert all this to a json object
1012
      tdlist = [| concat $flist |]
1013
      iname = mkName "i"
1014
  tclause <- clause [pat] (normalB tdlist) []
1015
  cclause <- [| $makeObjE . $(varE tdname) |]
1045
-- | Generates the save object functionality.
1046
genSaveObject :: String -> Q [Dec]
1047
genSaveObject sname = do
1016 1048
  let fname = mkName ("save" ++ sname)
1017
  sigt <- [t| $(conT name) -> JSON.JSValue |]
1018
  return [SigD tdname tdsigt, FunD tdname [tclause],
1019
          SigD fname sigt, ValD (VarP fname) (NormalB cclause) []]
1049
  sigt <- [t| $(conT $ mkName sname) -> JSON.JSValue |]
1050
  cclause <- [| $makeObjE . $(varE $ 'toDict) |]
1051
  return [SigD fname sigt, ValD (VarP fname) (NormalB cclause) []]
1020 1052

  
1021 1053
-- | Generates the code for saving an object's field, handling the
1022 1054
-- various types of fields that we have.
......
1047 1079
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
1048 1080

  
1049 1081
-- | Generates the load object functionality.
1050
genLoadObject :: (Field -> Q (Name, Stmt))
1051
              -> String -> [Field] -> Q (Dec, Dec)
1052
genLoadObject load_fn sname fields = do
1053
  let name = mkName sname
1054
      funname = mkName $ "load" ++ sname
1055
      arg1 = mkName $ if null fields then "_" else "v"
1056
      objname = mkName "o"
1057
      opid = mkName "op_id"
1058
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
1059
                                 (JSON.readJSON $(varE arg1)) |]
1060
  fbinds <- mapM load_fn fields
1061
  let (fnames, fstmts) = unzip fbinds
1062
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
1063
      retstmt = [NoBindS (AppE (VarE 'return) cval)]
1064
      -- FIXME: should we require an empty dict for an empty type?
1065
      -- this allows any JSValue right now
1066
      fstmts' = if null fields
1067
                  then retstmt
1068
                  else st1:fstmts ++ retstmt
1069
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT name) |]
1070
  return $ (SigD funname sigt,
1071
            FunD funname [Clause [VarP arg1] (NormalB (DoE fstmts')) []])
1082
genLoadObject :: String -> Q (Dec, Dec)
1083
genLoadObject sname = do
1084
  let fname = mkName $ "load" ++ sname
1085
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT $ mkName sname) |]
1086
  cclause <- [| fromDict <=< liftM JSON.fromJSObject . JSON.readJSON |]
1087
  return $ (SigD fname sigt,
1088
            FunD fname [Clause [] (NormalB cclause) []])
1072 1089

  
1073 1090
-- | Generates code for loading an object's field.
1074
loadObjectField :: [Field] -> Field -> Q (Name, Stmt)
1091
loadObjectField :: [Field] -> Field -> Q Exp
1075 1092
loadObjectField allFields field = do
1076 1093
  let name = fieldVariable field
1077 1094
      names = map fieldVariable allFields
1078 1095
      otherNames = listE . map stringE $ names \\ [name]
1079
  fvar <- newName name
1080 1096
  -- these are used in all patterns below
1081
  let objvar = varNameE "o"
1097
  let objvar = varE objVarName
1082 1098
      objfield = stringE (fieldName field)
1083
  bexp <- case (fieldDefault field, fieldIsOptional field) of
1099
  case (fieldDefault field, fieldIsOptional field) of
1084 1100
            -- Only non-optional fields without defaults must have a value;
1085 1101
            -- we treat both optional types the same, since
1086 1102
            -- 'maybeFromObj' can deal with both missing and null values
......
1094 1110
                     $ filter (not . (`elem` $otherNames) . fst) $objvar |]
1095 1111
            _ ->  loadFnOpt field [| maybeFromObj $objvar $objfield |] objvar
1096 1112

  
1097
  return (fvar, BindS (VarP fvar) bexp)
1098

  
1099 1113
-- | Builds the readJSON instance for a given object name.
1100 1114
objectReadJSON :: String -> Q Dec
1101 1115
objectReadJSON name = do
......
1141 1155
  ser_decls_p <- buildPParamSerialisation sname_p fields
1142 1156
  fill_decls <- fillParam sname field_pfx fields
1143 1157
  return $ [declF, declP] ++ ser_decls_f ++ ser_decls_p ++ fill_decls ++
1144
           buildParamAllFields sname fields ++
1145
           buildDictObjectInst name_f sname_f
1158
           buildParamAllFields sname fields
1146 1159

  
1147 1160
-- | Builds a list of all fields of a parameter.
1148 1161
buildParamAllFields :: String -> [Field] -> [Dec]
......
1156 1169
buildDictObjectInst :: Name -> String -> [Dec]
1157 1170
buildDictObjectInst name sname =
1158 1171
  [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
1159
   [ValD (VarP 'toDict) (NormalB (VarE (toDictName sname))) []]]
1172
   [ ValD (VarP 'toDict) (NormalB (VarE (toDictName sname))) []
1173
   , ValD (VarP 'fromDict) (NormalB (VarE (fromDictName sname))) []
1174
   ]]
1160 1175

  
1161 1176
-- | Generates the serialisation for a partial parameter.
1162 1177
buildPParamSerialisation :: String -> [Field] -> Q [Dec]
1163 1178
buildPParamSerialisation sname fields = do
1164 1179
  let name = mkName sname
1165
  savedecls <- genSaveObject savePParamField sname fields
1166
  (loadsig, loadfn) <- genLoadObject loadPParamField sname fields
1180
  dictdecls <- genDictObject savePParamField loadPParamField sname fields
1181
  savedecls <- genSaveObject sname
1182
  (loadsig, loadfn) <- genLoadObject sname
1167 1183
  shjson <- objectShowJSON sname
1168 1184
  rdjson <- objectReadJSON sname
1169 1185
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
1170 1186
                 [rdjson, shjson]
1171
  return $ savedecls ++ [loadsig, loadfn, instdecl]
1187
  return $ dictdecls ++ savedecls ++ [loadsig, loadfn, instdecl]
1172 1188

  
1173 1189
-- | Generates code to save an optional parameter field.
1174 1190
savePParamField :: Name -> Field -> Q Exp
......
1185 1201
                             ]
1186 1202

  
1187 1203
-- | Generates code to load an optional parameter field.
1188
loadPParamField :: Field -> Q (Name, Stmt)
1204
loadPParamField :: Field -> Q Exp
1189 1205
loadPParamField field = do
1190 1206
  checkNonOptDef field
1191 1207
  let name = fieldName field
1192
  fvar <- newName name
1193 1208
  -- these are used in all patterns below
1194
  let objvar = varNameE "o"
1209
  let objvar = varE objVarName
1195 1210
      objfield = stringE name
1196 1211
      loadexp = [| $(varE 'maybeFromObj) $objvar $objfield |]
1197
  bexp <- loadFnOpt field loadexp objvar
1198
  return (fvar, BindS (VarP fvar) bexp)
1212
  loadFnOpt field loadexp objvar
1199 1213

  
1200 1214
-- | Builds a simple declaration of type @n_x = fromMaybe f_x p_x@.
1201 1215
buildFromMaybe :: String -> Q Dec

Also available in: Unified diff