thrift-compiler-0.1.0.1: Thrift/Compiler/Types.hs
{-
Copyright (c) Meta Platforms, Inc. and affiliates.
All rights reserved.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree.
-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -Wno-star-is-type #-}
module Thrift.Compiler.Types
( Status(..), IfResolved, Parsed
, Program(..), Header(..), ParsedStatement(..), IncludeType(..), Decl(..), SpliceFile
, Loc(..), noLoc, nlc, Comment(..), Located(..)
, Separator(..), getSepLoc
, Annotations(..), Annotation(..), AnnValue(..), getAnns, annLoc
, StructuredAnnotation(..), StructuredAnnotationElems(..)
, ThriftPriority(..)
, Env(..), emptyEnv, Context(..), emptyContext
, TypeMap, SchemaMap, UnionMap, EnumMap, ConstMap, ServiceMap, ImportMap
, EnumValues, EnumInt
, Typedef(..), TypedefTag(..), TypedefLoc(..)
, Const(..), ConstLoc(..)
, UntypedConst(..), ConstVal(..), ListElem(..), MapPair(..), QuoteType(..)
, StructPair (..)
, TypedConst(..)
, List(..), Set(..), HashSet(..), Map(..), HashMap(..), EnumVal(..), New(..)
, StructVal(..), ExceptionVal(..), UnionVal(..), MembershipProof(..)
, Struct(..), StructType(..), StructLoc(..), ErrorClassification(..)
, ErrorClassificationType(..)
, Field(..), FieldLoc(..), FieldType(..), FieldTag(..), FieldId
, Requiredness(..), Laziness(..)
, Union(..), UnionAlt(..), PossiblyEmpty(..), EmptyName
, Enum(..), EnumFlavour(..), EnumValue(..), EnumValLoc(..)
, EnumValueType, enumValueType
, Service(..), Super(..), Function(..), FunLoc(..), getServiceFunctions
, ServiceStmt(..), Performs(..)
, funThrows, ThrowsLoc(..), Throws(..), FunctionType(..)
, Interaction(..)
, RpcIdempotency(..), RpcIdempotencyType(..)
, Stream(..), ResponseAndStreamReturn(..), rsTypeLoc, rsLoc
, Type, AnnotatedType(..), TType(..), SomeAnnTy(..)
, TypeLoc(..), GetArity, getTypeLoc
, SCHEMA(..), Schema, USchema
, SpecialType
, Name(..), ThriftName, Name_(..), localName, mapName, mkName
, filterHiddenFields
) where
import Prelude hiding (Enum)
import Data.ByteString (ByteString)
import Data.Int
import Data.Proxy
import Data.Some
import Data.Text (Text)
import GHC.TypeLits
import qualified Data.Map as Map
import qualified Data.Set as Set
import Language.Haskell.Exts.SrcLoc (SrcSpanInfo)
import Language.Haskell.Exts.Syntax (Module)
import Thrift.Compiler.Options
data Status = Resolved | Unresolved
type Parsed t = t 'Unresolved () Loc
type family IfResolved (s :: Status) (t :: *) :: * where
IfResolved 'Unresolved t = ()
IfResolved 'Resolved t = t
data ThriftPriority
= HighImportant
| High
| Important
| NormalPriority
| BestEffort
| NPriorities
deriving (Show, Eq)
-- Thrift Program --------------------------------------------------------------
data Program (l :: * {- Language -}) a = Program
{ progName :: Text -- ^ Thrift name
, progHSName :: Text -- ^ Haskell module prefix (with namespace)
, progPath :: FilePath
, progOutPath :: FilePath
, progInstances :: SpliceFile
, progIncludes :: [Program l a]
, progHeaders :: [Header 'Resolved l a]
, progDecls :: [Decl 'Resolved l a]
, progComments :: [Comment a]
, progEnv :: Env l
}
data ParsedStatement
= StatementHeader (Parsed Header)
| StatementDecl (Parsed Decl)
data Header s l a
= HInclude
{ incPath :: FilePath
, incType :: IncludeType
, incKeywordLoc :: Located a
, incPathLoc :: Located a
, incQuoteType :: QuoteType
}
| HNamespace
{ nmLang :: Text
, nmName :: Text
, nmKeywordLoc :: Located a
, nmLangLoc :: Located a
, nmNameLoc :: Located a
, nmQuoteType :: Maybe QuoteType
}
| HPackage
{ pkgUri :: Text
, pkgKeywordLoc :: Located a
, pkgUriLoc :: Located a
, pkgQuoteType :: QuoteType
, pkgSAnns :: [StructuredAnnotation s l a]
}
data IncludeType = Include | HsInclude | CppInclude
data Decl s l a
= D_Struct (Struct s l a)
| D_Union (Union s l a)
| D_Enum (Enum s l a)
| D_Typedef (Typedef s l a)
| D_Const (Const s l a)
| D_Service (Service s l a)
| D_Interaction (Interaction s l a)
type SpliceFile = Maybe (Module SrcSpanInfo)
data Annotations a = Annotations
{ annList :: [Annotation a]
, annOpenParen :: Located a
, annCloseParen :: Located a
}
data Annotation a
= SimpleAnn
{ saTag :: Text
, saLoc :: Located a
, saSep :: Separator a
}
| ValueAnn
{ vaTag :: Text
, vaVal :: AnnValue
, vaTagLoc :: Located a
, vaEqual :: Located a
, vaValLoc :: Located a
, vaSep :: Separator a
}
data AnnValue = IntAnn Int Text | TextAnn Text QuoteType
getAnns :: Maybe (Annotations a) -> [Annotation a]
getAnns = maybe [] annList
annLoc :: Annotation a -> a
annLoc SimpleAnn{..} = lLocation saLoc
annLoc ValueAnn{..} = lLocation vaTagLoc
data StructuredAnnotation (s :: Status) (l :: *) a = forall t. StructuredAnn
{ saAt :: Located a
, saType :: Text
, saTypeLoc :: TypeLoc 0 a
, saMaybeElems :: Maybe (StructuredAnnotationElems a)
, saResolvedType :: IfResolved s (Type l t)
, saResolvedVal :: IfResolved s (Some (StructVal l))
}
data StructuredAnnotationElems a = StructuredAnnElems
{ saOpenBrace :: Located a
, saElems :: [ListElem StructPair a]
, saCloseBrace :: Located a
}
data Loc = Loc
{ locFile :: FilePath
, locStartLine :: Int
, locStartCol :: Int
, locEndLine :: Int
, locEndCol :: Int
} deriving (Show, Eq)
instance Ord Loc where
l1 <= l2
| locFile l1 == locFile l2 =
if locStartLine l1 == locStartLine l2
then locStartCol l1 <= locStartCol l2
else locStartLine l1 <= locStartLine l2
| otherwise = locFile l1 <= locFile l2
noLoc :: Loc
noLoc = Loc "" 0 0 0 0
nlc :: Located Loc
nlc = Located [] noLoc
data Comment a = Comment a Text
deriving (Show, Eq)
data Located a = Located
{ lComments :: [Comment a]
, lLocation :: a
} deriving (Show, Eq)
data Separator a
= Semicolon (Located a)
| Comma (Located a)
| NoSep
getSepLoc :: Separator a -> Maybe (Located a)
getSepLoc (Semicolon loc) = Just loc
getSepLoc (Comma loc) = Just loc
getSepLoc NoSep = Nothing
-- Environment -----------------------------------------------------------------
-- | The type checking environment keeps track of
data Env (l :: * {- Language -}) = Env
{ typeMap :: TypeMap l -- ^ Local Named Types
, schemaMap :: SchemaMap l -- ^ Local Schemas
, unionMap :: UnionMap l -- ^ Local Union Schemas
, enumMap :: EnumMap -- ^ Local Enums
, enumInt :: EnumInt
-- ^ Local Enums as Int for default values, weird mode
, constMap :: ConstMap l -- ^ Local Constants
, serviceMap :: ServiceMap -- ^ Local Services
, importMap :: ImportMap l -- ^ all the modules that are in scope
, options :: Options l -- ^ Program options
, envName :: Text -- ^ The 'thriftName' of this 'Env'
}
-- | Help build a specialized 'Env' for 'runTypechecker' while
-- 'typecheckModule' is constructing the final 'Env' and 'Program'
emptyEnv :: (Text, Options l) -> Env l
emptyEnv (thriftName, opts) = Env
{ typeMap = emptyContext
, schemaMap = Map.empty
, unionMap = Map.empty
, enumMap = Map.empty
, enumInt = Map.empty
, constMap = emptyContext
, serviceMap = Map.empty
, importMap = Map.empty
, options = opts
, envName = thriftName
}
data Context a = Context
{ cMap :: Map.Map Text a -- ^ Map to lookup value of a symbol
, cScope :: Set.Set Text -- ^ Additional set of symbols that are in scope
-- ie hsNames for types and constants
}
emptyContext :: Context a
emptyContext = Context
{ cMap = Map.empty
, cScope = Set.empty
}
type TypeMap l = Context (Some (Type l))
type SchemaMap l = Map.Map Text (Some (Schema l))
type UnionMap l = Map.Map Text (Some (USchema l))
type EnumMap = Map.Map Text EnumValues
-- For Constants, we store the renamed version in addition to the type
type ConstMap l = Context (Some (Type l), Name, Loc)
-- For Service, we store the Set Text of renamed functions for scoping
type ServiceMap = Map.Map Text (Name, Set.Set Text, Loc)
type ImportMap l = Map.Map Text (Env l)
-- We need to be able to look up the constants keyed by their numeric value
-- or themselves
type EnumValues = (Map.Map EnumValueType (Name, Loc), Map.Map Text (Name, Loc))
-- | There are weird thrift file using enum constants as I32 and I64 defaults,
-- (as unqualified names). If there is a name collision with different value
-- this holds Nothing to detect the ambiguity. Active when weird mode is on.
type EnumInt = Map.Map Text (Maybe EnumValueType)
-- Thrift Typedef --------------------------------------------------------------
data Typedef (s :: Status) (l :: * {- Language -}) a = forall v t. Typedef
{ tdName :: Text
, tdTag :: TypedefTag s
, tdResolvedName :: IfResolved s Text
, tdType :: AnnotatedType a v
, tdResolvedType :: IfResolved s (Type l t)
, tdLoc :: TypedefLoc a
, tdAnns :: Maybe (Annotations a)
, tdSAnns :: [StructuredAnnotation s l a]
}
data TypedefTag (s :: Status) where
IsTypedef :: TypedefTag s
IsNewtype :: TypedefTag 'Resolved
data TypedefLoc a = TypedefLoc
{ tdlKeyword :: Located a
, tdlName :: Located a
}
-- Thrift Constant--------------------------------------------------------------
data Const (s :: Status) (l :: * {- Language -}) a = forall v t. Const
{ constName :: Text
, constResolvedName :: IfResolved s Text
, constType :: AnnotatedType a v
, constVal :: UntypedConst a
, constResolvedType :: IfResolved s (Type l t)
, constResolvedVal :: IfResolved s (TypedConst l t)
, constLoc :: ConstLoc a
, constSAnns :: [StructuredAnnotation s l a]
}
data ConstLoc a = ConstLoc
{ clKeyword :: Located a
, clName :: Located a
, clEqual :: Located a
, clSeparator :: Separator a
}
data UntypedConst a = UntypedConst
{ ucLoc :: Located a
, ucConst :: ConstVal a
}
data ConstVal a
= IntConst Int Text
-- ^ Includes are numeric types, enums, and booleans
| DoubleConst Double Text
-- ^ Doubles and Floats
| StringConst Text QuoteType
| IdConst Text
| ListConst
{ lvElems :: [ListElem UntypedConst a]
, lvCloseBrace :: Located a
}
-- ^ Lists and Sets
| MapConst
{ mvElems :: [ListElem MapPair a]
, mvCloseBrace :: Located a
}
-- ^ Maps and Structs
| StructConst
{ svType :: Text
, svOpenBrace :: Located a
, svElems :: [ListElem StructPair a]
, svCloseBrace :: Located a
}
| BoolConst Bool
data ListElem t a = ListElem
{ leElem :: t a
, leSeparator :: Separator a
}
data MapPair a = MapPair
{ mpKey :: UntypedConst a
, mpColon :: Located a
, mpVal :: UntypedConst a
}
data StructPair a = StructPair
{ spKey :: Text
, spKeyLoc :: Located a
, spEquals:: Located a
, spVal:: UntypedConst a
}
data QuoteType = SingleQuote | DoubleQuote
deriving (Show, Eq)
data TypedConst l t
= Literal t
| Identifier Name (Type l t) Loc -- ^ Loc for definition of Name
| WeirdEnumToInt (Type l t) Name (Type l EnumVal) Loc
newtype List l t = List [TypedConst l t]
newtype Set l t = Set [TypedConst l t]
newtype HashSet l t = HashSet [TypedConst l t]
newtype Map l k v = Map [(TypedConst l k, TypedConst l v)]
newtype HashMap l k v = HashMap [(TypedConst l k, TypedConst l v)]
-- | Include Loc of definition (from EnumValLoc)
data EnumVal = EnumVal Name Loc
newtype New t = New t
-- The fields of a struct are encoded in the type parameter of the StructVal
-- s has kind [(Symbol, *)]. The Symbol is a type-level string, which is the
-- name of the field and the * is the actual type of the field. A list of these
-- pairs makes a StructVal
data StructVal (l :: * {- Language -}) (s :: [(Symbol, *)]) where
Empty :: StructVal l '[]
ConsVal
:: forall (name :: Symbol) l t s. KnownSymbol name
=> Proxy name
-> Type l t
-> TypedConst l t
-> StructVal l s
-> StructVal l ('(name, t) ': s)
ConsDefault
:: forall (name :: Symbol) l t s. KnownSymbol name
=> Proxy name
-> Type l t
-> StructVal l s
-> StructVal l ('(name, t) ': s)
ConsJust
:: forall (name :: Symbol) l t s. KnownSymbol name
=> Proxy name
-> Type l t
-> TypedConst l t
-> StructVal l s
-> StructVal l ('(name, Maybe t) ': s)
ConsNothing
:: forall (name :: Symbol) l t s. KnownSymbol name
=> Proxy name
-> StructVal l s
-> StructVal l ('(name, Maybe t) ': s)
newtype ExceptionVal l s = EV (StructVal l s)
-- Unions have exactly one value out of a list of possible values. However, they
-- also need to include a MembershipProof which proves that the present field is
-- a member of the union's schema
data UnionVal (l :: * {- Language -}) (s :: [(Symbol, *)]) where
UnionVal
:: forall (name :: Symbol) l s t. KnownSymbol name
=> Proxy name
-> Type l t
-> TypedConst l t
-> MembershipProof '(name, t) s
-> UnionVal l s
-- Proof that a field is a memeber of a schema
data MembershipProof x xs where
-- The field is the first field in the type list
PHere :: MembershipProof x (x ': xs)
-- The field is buried somewhere deeper in the type list
PThere :: MembershipProof x xs -> MembershipProof x (y ': xs)
-- Thrift Structs and Exceptions -----------------------------------------------
data Struct s l a = Struct
{ structName :: Text
, structResolvedName :: IfResolved s Text
, structType :: StructType
, structMembers :: [Field 'StructField s l a]
, structLoc :: StructLoc a
, structAnns :: Maybe (Annotations a)
, structSAnns :: [StructuredAnnotation s l a]
, errorClassifications :: [ErrorClassification a]
}
data StructType = StructTy | ExceptionTy
data StructLoc a = StructLoc
{ slKeyword :: Located a
, slName :: Located a
, slOpenBrace :: Located a
, slCloseBrace :: Located a
}
data ErrorClassification a = ErrorClassification
{ ecType :: ErrorClassificationType
, ecKeywordLoc :: Located a
}
data ErrorClassificationType =
EcSafe | EcTransient | EcStateful | EcPermanent | EcServer | EcClient
-- Thrift Fields ---------------------------------------------------------------
data FieldType = StructField | Argument | ThrowsField
data FieldTag u s l t where
STRUCT_FIELD :: FieldTag 'StructField s l t
ARGUMENT :: FieldTag 'Argument s l t
THROWS_UNRESOLVED :: FieldTag 'ThrowsField 'Unresolved l t
THROWS_RESOLVED :: FieldTag 'ThrowsField 'Resolved l (Some (ExceptionVal l))
data Field u s l a = forall v t. Field
{ fieldId :: FieldId
, fieldName :: Text
, fieldResolvedName :: IfResolved s Text
, fieldType :: AnnotatedType a v
, fieldResolvedType :: IfResolved s (Type l t)
, fieldVal :: Maybe (UntypedConst a)
, fieldResolvedVal :: IfResolved s (Maybe (TypedConst l t))
, fieldRequiredness :: Requiredness u a
, fieldLaziness :: Laziness
, fieldTag :: FieldTag u s l t
, fieldLoc :: FieldLoc a
, fieldAnns :: Maybe (Annotations a)
, fieldSAnns :: [StructuredAnnotation s l a]
}
type FieldId = Int32
data Requiredness u a where
Default :: Requiredness u a
-- Optional and Required are only for Struct Fields (not arguments)
Optional :: Located a -> Requiredness 'StructField a
Required :: Located a -> Requiredness 'StructField a
data Laziness = Lazy | Strict
data FieldLoc a = FieldLoc
{ flId :: Located a
, flIdRep :: Text
, flColon :: Located a
, flName :: Located a
, flEqual :: Maybe (Located a)
, flSeparator :: Separator a
}
-- Thrift Unions ---------------------------------------------------------------
data Union s l a = forall u. Union
{ unionName :: Text
, unionResolvedName :: IfResolved s Text
, unionAlts :: [UnionAlt s l a]
, unionEmptyName :: EmptyName s u
, unionHasEmpty :: PossiblyEmpty u
, unionLoc :: StructLoc a
, unionAnns :: Maybe (Annotations a)
, unionSAnns :: [StructuredAnnotation s l a]
}
data UnionAlt (s :: Status) (l :: * {- Language -}) a = forall v t. UnionAlt
{ altId :: FieldId
, altName :: Text
, altResolvedName :: IfResolved s Text
, altType :: AnnotatedType a v
, altResolvedType :: IfResolved s (Type l t)
, altLoc :: FieldLoc a
, altAnns :: Maybe (Annotations a)
, altSAnns :: [StructuredAnnotation s l a]
}
data PossiblyEmpty (u :: Bool) where
HasEmpty :: PossiblyEmpty 'True
NonEmpty :: PossiblyEmpty 'False
type family EmptyName s u where
EmptyName 'Unresolved u = ()
EmptyName 'Resolved 'True = Text
EmptyName 'Resolved 'False = ()
-- Thrift Enums ----------------------------------------------------------------
data Enum (s :: Status) (l :: * {- Language -}) a = Enum
{ enumName :: Text
, enumResolvedName :: IfResolved s Text
, enumFlavour :: IfResolved s EnumFlavour
, enumConstants :: [EnumValue s l a]
, enumLoc :: StructLoc a
, enumAnns :: Maybe (Annotations a)
, enumSAnns :: [StructuredAnnotation s l a]
}
data EnumFlavour
= SumTypeEnum
{ enumNoUnknown :: Bool
}
| PseudoEnum
{ peThriftEnum :: Bool
}
data EnumValue (s :: Status) (l :: * {- Language -}) a = EnumValue
{ evName :: Text
, evResolvedName :: IfResolved s Text
, evValue :: EnumValueType
, evLoc :: EnumValLoc a
, evAnns :: Maybe (Annotations a)
, evSAnns :: [StructuredAnnotation s l a]
}
type EnumValueType = Int32
enumValueType :: TType s l a EnumValueType
enumValueType = I32
data EnumValLoc a = EnumValLoc
{ evlName :: Located a
, evlEqual :: Located a
, evlValue :: Located a
, evlRep :: Text
, evlSeparator :: Separator a
}
-- Thrift Service --------------------------------------------------------------
data Service (s :: Status) (l :: * {- Language -}) a = Service
{ serviceName :: Text
, serviceResolvedName :: IfResolved s Text
, serviceSuper :: Maybe (Super s a)
-- , serviceFunctions :: [Function s l a]
, serviceStmts :: [ServiceStmt s l a]
, serviceLoc :: StructLoc a
, serviceAnns :: Maybe (Annotations a)
, serviceSAnns :: [StructuredAnnotation s l a]
}
getServiceFunctions :: Service s l a -> [Function s l a]
getServiceFunctions Service {..} = concatMap getFn serviceStmts
where
getFn (FunctionStmt f ) = [f]
getFn _ = []
data Super s a = Super
{ supName :: Text
, supResolvedName :: IfResolved s (Name, Loc)
, supExtends :: Located a -- ^ location of "extends" keyword
, supLoc :: Located a -- ^ location of "supName" after "extends"
}
data ServiceStmt s l a
= FunctionStmt (Function s l a)
| PerformsStmt (Performs s l a)
newtype Performs (s :: Status) (l :: * {- Language -}) a = Performs
{ performsName :: Text
}
data Function (s :: Status) (l :: * {- Language -}) a = Function
{ funName :: Text
, funResolvedName :: IfResolved s Text
, funType :: FunctionType s l a
, funResolvedType :: IfResolved s (Maybe (Some (Type l)))
, funArgs :: [Field 'Argument s l a]
, funExceptions :: [Field 'ThrowsField s l a]
, funIsOneWay :: Bool
, funPriority :: ThriftPriority
, funLoc :: FunLoc a
, funAnns :: Maybe (Annotations a)
, funSAnns :: [StructuredAnnotation s l a]
, funIdempotency :: Maybe RpcIdempotency
}
data FunctionType s l a
= FunType (Some (AnnotatedType a))
| FunTypeVoid (Located a)
| FunTypeResponseAndStreamReturn (ResponseAndStreamReturn s l a)
data ResponseAndStreamReturn s l a = forall v. ResponseAndStreamReturn
{ rsReturn :: Maybe (AnnotatedType a v)
, rsComma :: Maybe (Located a)
, rsStream :: Stream s l a
}
rsTypeLoc :: ResponseAndStreamReturn s l a -> SomeTypeLoc a
rsTypeLoc ResponseAndStreamReturn{rsStream = Stream{..}, ..} = case rsReturn of
Just AnnotatedType{..} -> ThisTypeLoc atLoc
Nothing -> ThisTypeLoc streamLoc
rsLoc :: ResponseAndStreamReturn s l a -> a
rsLoc = loc . rsTypeLoc
where loc (ThisTypeLoc l) = getTypeLoc l
data Stream s l a = forall v. Stream
{ streamType :: AnnotatedType a v
, streamThrows :: Maybe (Throws s l a)
, streamLoc :: TypeLoc 1 a
}
data FunLoc a = FunLoc
{ fnlOneway :: Maybe (Located a)
, fnlIdempotency :: Maybe (Located a)
, fnlName :: Located a
, fnlOpenParen :: Located a
, fnlCloseParen :: Located a
, fnlThrows :: Maybe (ThrowsLoc a)
, fnlSeparator :: Separator a
}
data ThrowsLoc a = ThrowsLoc
{ tlThrows :: Located a
, tlOpenParen :: Located a
, tlCloseParen :: Located a
}
data Throws s l a = Throws
{ throwsLoc :: ThrowsLoc a
, throwsFields :: [Field 'ThrowsField s l a]
}
funThrows
:: Function s l a
-> Maybe (Throws s l a)
funThrows Function{funLoc=FunLoc{..}, ..} = fmap f fnlThrows
where f throws = Throws throws funExceptions
newtype RpcIdempotency = RpcIdempotency { riType :: RpcIdempotencyType }
data RpcIdempotencyType = RiReadonly | RiIdempotent
-- Thrift Interaction ----------------------------------------------------------
data Interaction (s :: Status) (l :: * {- Language -}) a = Interaction
{ interactionName :: Text
, interactionResolvedName :: IfResolved s Text
, interactionSuper :: Maybe (Super s a)
, interactionFunctions :: [Function s l a]
, interactionLoc :: StructLoc a
, interactionAnns :: Maybe (Annotations a)
, interactionSAnns :: [StructuredAnnotation s l a]
}
-- Thrift Value Types ----------------------------------------------------------
-- When a thrift file is parsed, all of the types are marked as 'Unresolved
-- meaning that we don't know have any information about the named types.
-- During type checking, these types become 'Resolved, a process by which all
-- of the named types become either structs, enums, or typedefs
type Type l = TType 'Resolved l Loc
-- Annotations are resolved during typechecking, so we don't need them once
-- types are resolved
type family TypeOf (s :: Status) (l :: * {- Language -}) a :: * -> * where
TypeOf 'Resolved l a = Type l
TypeOf 'Unresolved l a = AnnotatedType a
data AnnotatedType a t = AnnotatedType
{ atType :: TType 'Unresolved () a t
, atAnnotations :: Maybe (Annotations a)
, atLoc :: TypeLoc (GetArity t) a
}
type family GetArity t :: Nat where
GetArity (List l a) = 1
GetArity (Set l a) = 1
GetArity (HashSet l a) = 1
GetArity (Map l a b) = 2
GetArity (HashMap l a b) = 2
GetArity a = 0
data TypeLoc (n :: Nat) a where
Arity0Loc :: { a0Ty :: Located a } -> TypeLoc 0 a
Arity1Loc
:: { a1Ty :: Located a
, a1OpenBrace :: Located a
, a1CloseBrace :: Located a
}
-> TypeLoc 1 a
Arity2Loc
:: { a2Ty :: Located a
, a2OpenBrace :: Located a
, a2Comma :: Located a
, a2CloseBrace :: Located a
}
-> TypeLoc 2 a
getTypeLoc :: TypeLoc n a -> a
getTypeLoc Arity0Loc{..} = lLocation a0Ty
getTypeLoc Arity1Loc{..} = lLocation a1Ty
getTypeLoc Arity2Loc{..} = lLocation a2Ty
data SomeTypeLoc a =
forall n. ThisTypeLoc (TypeLoc n a)
data SomeAnnTy s l =
forall t. ThisAnnTy (TType s l Loc t) (TypeLoc (GetArity t) Loc)
data TType (s :: Status) (l :: * {- Language -}) a (t :: *) where
-- Integral TTypes
I8 :: TType s l a Int8
I16 :: TType s l a Int16
I32 :: TType s l a Int32
I64 :: TType s l a Int64
-- Floating Point TTypes
TFloat :: TType s l a Float
TDouble :: TType s l a Double
-- Other Base TTypes
TBool :: TType s l a Bool
TText :: TType s l a Text
TBytes :: TType s l a ByteString
-- Collections
TSet :: TypeOf s l a t -> TType s l a (Set l t)
THashSet :: TypeOf s l a t -> TType s l a (HashSet l t)
TList :: TypeOf s l a t -> TType s l a (List l t)
TMap :: TypeOf s l a k -> TypeOf s l a v -> TType s l a (Map l k v)
THashMap :: TypeOf s l a k -> TypeOf s l a v -> TType s l a (HashMap l k v)
-- TTypedefs
TTypedef :: Name -> Type l t -> Loc -> TType 'Resolved l a t
TNewtype :: Name -> Type l t -> Loc -> TType 'Resolved l a (New t)
-- Structs
TStruct :: Name -> Loc -> TType 'Resolved l a (Some (StructVal l))
TException :: Name -> Loc -> TType 'Resolved l a (Some (ExceptionVal l))
TUnion :: Name -> Loc -> TType 'Resolved l a (Some (UnionVal l))
-- Enums
TEnum :: Name -> Loc -> Bool -> TType 'Resolved l a EnumVal
-- Unresolved named types
TNamed :: Text -> TType 'Unresolved l a ()
-- Special types that we get by resolving annotations
TSpecial :: SpecialType l t -> TType 'Resolved l a t
data family SpecialType (l :: * {- Language -}) (t :: *)
data SchemaType = StructSchema | UnionSchema
type Schema l = SCHEMA l 'StructSchema
type USchema l = SCHEMA l 'UnionSchema
-- Schema for a struct, defines exactly what must be present
data SCHEMA (l :: * {- Language -}) (t :: SchemaType) (s :: [(Symbol, *)]) where
-- | Empty struct has type empty list
SEmpty :: SCHEMA l u '[]
SField
:: forall (name :: Symbol) l t s. KnownSymbol name
=> Proxy name
-> Text
-> Type l t
-> SCHEMA l 'StructSchema s
-> SCHEMA l 'StructSchema ('(name, t) ': s)
-- | For required fields, the value must be present
SReqField
:: forall (name :: Symbol) l u t s. KnownSymbol name
=> Proxy name
-> Text
-> Type l t
-> SCHEMA l u s
-> SCHEMA l u ('(name, t) ': s)
-- | For optional fields, the value is a Maybe
-- Only structs can have optional fields
SOptField
:: forall (name :: Symbol) l t s. KnownSymbol name
=> Proxy name
-> Text
-> Type l t
-> SCHEMA l 'StructSchema s
-> SCHEMA l 'StructSchema ('(name, Maybe t) ': s)
-- | Renamed for target langauge
data Name = Name
{ sourceName :: ThriftName
, resolvedName :: Name_ 'Resolved
} deriving (Eq)
-- | Name in the original thrift source file
type ThriftName = Name_ 'Unresolved
data Name_ (s :: Status)
= UName Text
| QName Text Text
deriving (Eq,Show)
localName :: Name_ s -> Text
localName (UName n) = n
localName (QName _ n) = n
mapName :: (Text -> Text) -> Name_ s -> Name_ s
mapName f (UName n) = UName $ f n
mapName f (QName m n) = QName m $ f n
mkName :: Text -> Text -> Name
mkName tname rname = Name
{ sourceName = UName tname
, resolvedName = UName rname
}
filterHiddenFields :: [Field u s l a] -> [Field u s l a]
filterHiddenFields = filter (not . isHiddenField)
where
isHiddenField Field{..} =
any (\annot -> case annot of
SimpleAnn{saTag = "hs.hidden"} -> True
_ -> False
) $ getAnns fieldAnns