{-# language DataKinds #-} {-# language GADTs #-} {-# language PolyKinds #-} {-# language TypeFamilies #-} {-# language TypeOperators #-} {-# language UndecidableInstances #-} {-| Description : Protocol-defined annotations. Libraries can define custom annotations to indicate additional information not found in the 'Schema' itself. For example, Protocol Buffers requires a numerical identifier for each field in a record. -} module Mu.Schema.Annotations ( -- * Annotate a schema Annotation(..) , AnnotatedSchema , AnnotationDomain -- * Find annotations for an element , GetSchemaAnnotation , GetTypeAnnotation , GetFieldAnnotation ) where import Data.Kind import GHC.TypeLits import Mu.Schema.Definition -- | Each annotation belongs to a domain. type AnnotationDomain = Type -- | Annotations proper. data Annotation domain typeName fieldName where -- | Annotation over the whole schema. AnnSchema :: domain -> Annotation domain typeName fieldName -- | Annotation over a type in the schema. AnnType :: typeName -> domain -> Annotation domain typeName fieldName -- | Annotation over a field in a record -- or a choice in an enumeration. AnnField :: typeName -> fieldName -> domain -> Annotation domain typeName fieldName -- | This type family links each schema to -- its corresponding annotations from one domain. type family AnnotatedSchema domain (sch :: Schema typeName fieldName) :: [Annotation domain typeName fieldName] -- | Find the annotation over the schema in the given set. -- If the annotation cannot be found, raise a 'TypeError'. type family GetSchemaAnnotation (anns :: [Annotation domain t f]) :: domain where GetSchemaAnnotation '[] = TypeError ('Text "cannot find schema annotation") GetSchemaAnnotation ('AnnSchema d ': rs) = d GetSchemaAnnotation (r ': rs) = GetSchemaAnnotation rs -- | Find the annotation over the given type in the given set. -- If the annotation cannot be found, raise a 'TypeError'. type family GetTypeAnnotation (anns :: [Annotation domain t f]) (ty :: t) :: domain where GetTypeAnnotation '[] ty = TypeError ('Text "cannot find annotation for " ':<>: 'ShowType ty) GetTypeAnnotation ('AnnType ty d ': rs) ty = d GetTypeAnnotation (r ': rs) ty = GetTypeAnnotation rs ty -- | Find the annotation over the given field or choice in the given type. -- If the annotation cannot be found, raise a 'TypeError'. type family GetFieldAnnotation (anns :: [Annotation domain t f]) (ty :: t) (fl :: f) :: domain where GetFieldAnnotation '[] ty fl = TypeError ('Text "cannot find annotation for " ':<>: 'ShowType ty ':<>: 'Text "/" ':<>: 'ShowType fl) GetFieldAnnotation ('AnnField ty fl d ': rs) ty fl = d GetFieldAnnotation (r ': rs) ty fl = GetFieldAnnotation rs ty fl