, defaultField
, optionalField
, renameField
- , containerField
, customField
, timeStampFields
, uuidFields
, buildObject
, buildObjectSerialisation
, buildParam
- , Container
) where
-import Control.Arrow
import Control.Monad (liftM, liftM2)
import Data.Char
import Data.List
-import qualified Data.Map as M
import qualified Data.Set as Set
import Language.Haskell.TH
import qualified Text.JSON as JSON
-import Ganeti.HTools.JSON
-
-- * Exported types
-type Container = M.Map String
-
-- | Serialised field data type.
data Field = Field { fieldName :: String
, fieldType :: Q Type
, fieldShow :: Maybe (Q Exp)
, fieldDefault :: Maybe (Q Exp)
, fieldConstr :: Maybe String
- , fieldIsContainer :: Bool
, fieldIsOptional :: Bool
}
, fieldShow = Nothing
, fieldDefault = Nothing
, fieldConstr = Nothing
- , fieldIsContainer = False
, fieldIsOptional = False
}
optionalField :: Field -> Field
optionalField field = field { fieldIsOptional = True }
--- | Marks a field as a container.
-containerField :: Field -> Field
-containerField field = field { fieldIsContainer = True }
-
-- | Sets custom functions on a field.
customField :: Name -- ^ The name of the read function
-> Name -- ^ The name of the show function
_ -> map (\c -> if c == '-' then '_' else c) $ fieldName f
actualFieldType :: Field -> Q Type
-actualFieldType f | fieldIsContainer f = [t| Container $t |]
- | fieldIsOptional f = [t| Maybe $t |]
+actualFieldType f | fieldIsOptional f = [t| Maybe $t |]
| otherwise = t
where t = fieldType f
-> Q Exp -- ^ The value of the field as existing in the JSON message
-> Q Exp -- ^ The entire object in JSON object format
-> Q Exp -- ^ Resulting expression
-loadFn (Field { fieldIsContainer = True }) expr _ =
- [| $expr >>= readContainer |]
loadFn (Field { fieldRead = Just readfn }) expr o = [| $expr >>= $readfn $o |]
loadFn _ expr _ = expr
appFn f x | f == VarE 'id = x
| otherwise = AppE f x
--- | Container loader
-readContainer :: (Monad m, JSON.JSON a) =>
- JSON.JSObject JSON.JSValue -> m (Container a)
-readContainer obj = do
- let kjvlist = JSON.fromJSObject obj
- kalist <- mapM (\(k, v) -> fromKeyValue k v >>= \a -> return (k, a)) kjvlist
- return $ M.fromList kalist
-
--- | Container dumper
-showContainer :: (JSON.JSON a) => Container a -> JSON.JSValue
-showContainer = JSON.makeObj . map (second JSON.showJSON) . M.toList
-
-- * Template code for simple raw type-equivalent ADTs
-- | Generates a data type declaration.
saveObjectField :: Name -> Field -> Q Exp
saveObjectField fvar field
- | isContainer = [| [( $nameE , JSON.showJSON . showContainer $ $fvarE)] |]
| fisOptional = [| case $(varE fvar) of
Nothing -> []
Just v -> [( $nameE, JSON.showJSON v)]
Just fn -> [| let (actual, extra) = $fn $fvarE
in extra ++ [( $nameE, JSON.showJSON actual)]
|]
- where isContainer = fieldIsContainer field
- fisOptional = fieldIsOptional field
+ where fisOptional = fieldIsOptional field
nameE = stringE (fieldName field)
fvarE = varE fvar