{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveGeneric #-} module Data.Docs.Docs ( Documentation(..) , Field(..) , Docs(..) , genDocs , genDocsWith , genDocsEnum , genValues , genFields , FieldModifier ) where import Data.Text (Text, pack) import qualified Data.Text as T import Data.Docs.TypeName (GTypeName) import qualified Data.Docs.TypeName as TypeName import Data.Docs.Selectors (Selectors(..)) import Data.Data (Proxy(..), TypeRep) import GHC.Generics (Generic, Rep) -- | Can generate documentation for type class Docs a where docs :: Proxy a -> Documentation default docs :: (Generic a, GTypeName (Rep a), Selectors (Rep a)) => Proxy a -> Documentation docs = genDocs "" type FieldName = Text type TypeName = Text -- | Documentation for a given type data Documentation = Documentation { typeName :: TypeName , fields :: [Field] , description :: Text , enumeratedValues :: [Text] } deriving (Show, Eq) -- | Documentation for a record field data Field = Field { fieldName :: FieldName , fieldType :: TypeName , isRequired :: Bool } deriving (Show, Eq) -- | Create documentation for a type with only a description genDocs :: forall a. (Generic a, GTypeName (Rep a), Selectors (Rep a)) => Text -> Proxy a -> Documentation genDocs d p = Documentation { typeName = pack $ TypeName.typeName p , description = d , fields = genFields id p , enumeratedValues = [] } genDocsWith :: forall a. (Generic a, GTypeName (Rep a), Selectors (Rep a)) => FieldModifier -> Text -> Proxy a -> Documentation genDocsWith fieldModifier d p = Documentation { typeName = pack $ TypeName.typeName p , description = d , fields = genFields fieldModifier p , enumeratedValues = [] } genDocsEnum :: forall a. (Generic a, GTypeName (Rep a), Selectors (Rep a), Bounded a, Enum a, Show a) => Text -> Proxy a -> Documentation genDocsEnum d p = (genDocs d p) { enumeratedValues = genValues p } type FieldModifier = String -> String genFields :: forall a. (Selectors (Rep a)) => FieldModifier -> Proxy a -> [Field] genFields fieldModifier _ = map (uncurry toField . selectorToText fieldModifier) $ selectors (Proxy :: Proxy (Rep a)) genValues :: forall a. (Enum a, Bounded a, Show a) => Proxy a -> [Text] genValues _ = map (pack . show) ([minBound..] :: [a]) selectorToText :: FieldModifier -> (String, TypeRep) -> (String, [String]) selectorToText fieldModifier (s, t) = (fieldModifier s, words $ show t) toField :: String -> [String] -> Field toField s ("Maybe":t) = field s (concat t) False toField s t = field s (concat t) True field :: String -> String -> Bool -> Field field n t o = Field (T.pack n) (T.pack t) o