module Data.API.Tools.Datatypes
( datatypesTool
, datatypesTool'
, defaultDerivedClasses
, type_nm
, rep_type_nm
, nodeT
, nodeRepT
, nodeConE
, nodeNewtypeConE
, nodeFieldE
, nodeFieldP
, nodeAltConE
, nodeAltConP
, newtypeProjectionE
) where
import Data.API.TH
import Data.API.TH.Compat
import Data.API.Tools.Combinators
import Data.API.Types
import Control.Applicative
import Data.Aeson
import qualified Data.CaseInsensitive as CI
import Data.Char
import Data.Maybe
import Data.String
import qualified Data.Text as T
import Data.Time
import Data.Typeable
import Language.Haskell.TH
import Text.Regex
import Prelude
datatypesTool :: APITool
datatypesTool = datatypesTool' defaultDerivedClasses
datatypesTool' :: (APINode -> [Name]) -> APITool
datatypesTool' deriv = apiNodeTool $ apiSpecTool (mkTool (gen_sn_dt deriv))
(simpleTool (gen_sr_dt deriv))
(simpleTool (gen_su_dt deriv))
(simpleTool (gen_se_dt deriv))
(simpleTool gen_sy)
gen_sy :: (APINode, APIType) -> Q [Dec]
gen_sy (as, ty) = return [TySynD (type_nm as) [] $ mk_type ty]
gen_sn_dt :: (APINode -> [Name]) -> ToolSettings -> (APINode, SpecNewtype) -> Q [Dec]
gen_sn_dt deriv ts (as, sn) = (nd :) <$> if smart then sc else return []
where
nd = mkNewtypeD [] nm [] c (deriv as)
c = RecC (newtype_con_nm smart as) [(newtype_prj_nm as,annNotStrict,wrapped_ty)]
wrapped_ty = mk_type $ TyBasic (snType sn)
nm = rep_type_nm as
smart = newtypeSmartConstructors ts && isJust (snFilter sn)
sc = simpleSigD (newtype_smart_con_nm as) [t| $(return wrapped_ty) -> Maybe $(nodeRepT as) |] $
case snFilter sn of
Just (FtrStrg re) -> [| \ s -> if isJust (matchRegex (re_regex re) (T.unpack s))
then Just ($nt_con s) else Nothing |]
Just (FtrIntg ir) -> [| \ i -> if i `inIntRange` ir then Just ($nt_con i) else Nothing |]
Just (FtrUTC ur) -> [| \ u -> if u `inUTCRange` ur then Just ($nt_con u) else Nothing |]
Nothing -> [| Just . $nt_con |]
nt_con = nodeNewtypeConE ts as sn
gen_sr_dt :: (APINode -> [Name]) -> (APINode, SpecRecord) -> Q [Dec]
gen_sr_dt deriv (as, sr) = return [mkDataD [] nm [] cs (deriv as)]
where
cs = [RecC nm [(pref_field_nm as fnm,annIsStrict,mk_type (ftType fty)) |
(fnm,fty)<-srFields sr]]
nm = rep_type_nm as
gen_su_dt :: (APINode -> [Name]) -> (APINode, SpecUnion) -> Q [Dec]
gen_su_dt deriv (as, su) = return [mkDataD [] nm [] cs (deriv as)]
where
cs = [NormalC (pref_con_nm as fnm) [(annIsStrict,mk_type ty)] |
(fnm,(ty,_))<-suFields su]
nm = rep_type_nm as
gen_se_dt :: (APINode -> [Name]) -> (APINode, SpecEnum) -> Q [Dec]
gen_se_dt deriv (as, se) = return [mkDataD [] nm [] cs (deriv as)]
where
cs = [NormalC (pref_con_nm as fnm) [] | (fnm,_) <- seAlts se ]
nm = rep_type_nm as
mk_type :: APIType -> Type
mk_type ty =
case ty of
TyList ty' -> AppT ListT $ mk_type ty'
TyMaybe ty' -> AppT (ConT ''Maybe) $ mk_type ty'
TyName nm -> ConT $ mkNameText $ _TypeName nm
TyBasic bt -> basic_type bt
TyJSON -> ConT ''Value
basic_type :: BasicType -> Type
basic_type bt =
case bt of
BTstring -> ConT ''T.Text
BTbinary -> ConT ''Binary
BTbool -> ConT ''Bool
BTint -> ConT ''Int
BTutc -> ConT ''UTCTime
defaultDerivedClasses :: APINode -> [Name]
defaultDerivedClasses an = case anSpec an of
SpNewtype sn -> case snType sn of
BTstring -> ''IsString : derive_leaf_nms
BTbinary -> derive_leaf_nms
BTbool -> derive_leaf_nms
BTint -> derive_leaf_nms
BTutc -> derive_leaf_nms
SpRecord _ -> derive_node_nms
SpUnion _ -> derive_node_nms
SpEnum _ -> derive_leaf_nms ++ [''Bounded, ''Enum]
SpSynonym _ -> []
derive_leaf_nms :: [Name]
derive_leaf_nms = [''Show,''Eq,''Ord,''Typeable]
derive_node_nms :: [Name]
derive_node_nms = [''Show,''Eq,''Typeable]
type_nm :: APINode -> Name
type_nm an = mkName $ T.unpack $ _TypeName $ anName an
rep_type_nm :: APINode -> Name
rep_type_nm an = mkName $ rep_type_s an
newtype_prj_nm :: APINode -> Name
newtype_prj_nm an = mkName $ "_" ++ rep_type_s an
newtype_con_nm :: Bool -> APINode -> Name
newtype_con_nm smart an | smart = mkName $ "UnsafeMk" ++ rep_type_s an
| otherwise = mkName $ rep_type_s an
newtype_smart_con_nm :: APINode -> Name
newtype_smart_con_nm an = mkName $ "mk" ++ rep_type_s an
rep_type_s :: APINode -> String
rep_type_s an = f $ T.unpack $ _TypeName $ anName an
where
f s = maybe s (const ("REP__"++s)) $ anConvert an
pref_field_nm :: APINode -> FieldName -> Name
pref_field_nm as fnm = mkName $ pre ++ T.unpack (_FieldName fnm)
where
pre = "_" ++ map toLower (CI.original $ anPrefix as) ++ "_"
pref_con_nm :: APINode -> FieldName -> Name
pref_con_nm as fnm = mkName $ pre ++ T.unpack (_FieldName fnm)
where
pre = map toUpper (CI.original $ anPrefix as) ++ "_"
nodeT :: APINode -> TypeQ
nodeT = conT . type_nm
nodeRepT :: APINode -> TypeQ
nodeRepT = conT . rep_type_nm
nodeConE :: APINode -> ExpQ
nodeConE = conE . rep_type_nm
nodeNewtypeConE :: ToolSettings -> APINode -> SpecNewtype -> ExpQ
nodeNewtypeConE ts an sn = conE $ newtype_con_nm (newtypeSmartConstructors ts && isJust (snFilter sn)) an
nodeFieldE :: APINode -> FieldName -> ExpQ
nodeFieldE an fnm = varE $ pref_field_nm an fnm
nodeFieldP :: APINode -> FieldName -> PatQ
nodeFieldP an fnm = varP $ pref_field_nm an fnm
nodeAltConE :: APINode -> FieldName -> ExpQ
nodeAltConE an fn = conE $ pref_con_nm an fn
nodeAltConP :: APINode -> FieldName -> [PatQ] -> PatQ
nodeAltConP an fn = conP (pref_con_nm an fn)
newtypeProjectionE :: APINode -> ExpQ
newtypeProjectionE = varE . newtype_prj_nm