servant-docs-simple-0.2.0.0: src/Servant/Docs/Simple/Render.hs
{- | Renders the intermediate structure into common documentation formats
__Example scripts__
[Generating plaintext/JSON documentation from api types](https://github.com/Holmusk/servant-docs-simple/blob/master/examples/generate.hs)
[Writing our own rendering format](https://github.com/Holmusk/servant-docs-simple/blob/master/examples/render.hs)
__Example of rendering the intermediate structure__
/Intermediate structure/
> ApiDocs ( fromList [( "/hello/world",
> , Details (fromList ([ ( "RequestBody"
> , Details (fromList ([ ( "Format"
> , Detail "': * () ('[] *)"
> )
> , ( "ContentType"
> , Detail "()"
> )
> ]))
> )
> , ( "RequestType"
> , Detail "'POST"
> )
> , ( "Response"
> , Details (fromList ([ ( "Format"
> , Detail "': * () ('[] *)"
> )
> , ( "ContentType"
> , Detail "()"
> )
> ]))
> )
> ]))
> )])
/JSON/
> {
> "/hello/world": {
> "Response": {
> "Format": "': * () ('[] *)",
> "ContentType": "()"
> },
> "RequestType": "'POST",
> "RequestBody": {
> "Format": "': * () ('[] *)",
> "ContentType": "()"
> }
> }
> }
/Text/
> /hello/world:
> RequestBody:
> Format: ': * () ('[] *)
> ContentType: ()
> RequestType: 'POST
> Response:
> Format: ': * () ('[] *)
> ContentType: ()
-}
module Servant.Docs.Simple.Render
( ApiDocs (..)
, Details (..)
, Renderable (..)
, Parameter
, Route
, Json (..)
, Pretty (..)
, PlainText (..)
) where
import Data.Aeson (ToJSON (..), Value (..))
import Data.HashMap.Strict (fromList)
import Data.List (intersperse)
import Data.Map.Ordered (OMap, assocs)
import Data.Text (Text, pack)
import Data.Text.Prettyprint.Doc (Doc, cat, line, nest, pretty, vcat, vsep)
-- | Intermediate documentation structure, a hashmap of endpoints
--
-- API type:
--
-- > type API = "users" :> ( "update" :> Response '[()] ()
-- > :<|> "get" :> Response '[()] ()
-- > )
--
-- Parsed into ApiDocs:
--
--
-- > ApiDocs ( fromList [ ( "/users/update",
-- > , Details (fromList ([ ( "Response"
-- > , Details (fromList ([ ( "Format"
-- > , Detail "': * () ('[] *)"
-- > )
-- > , ( "ContentType"
-- > , Detail "()"
-- > )
-- > ]))
-- > )
-- > ]))
-- > )
-- > , ( "/users/get",
-- > , Details (fromList ([ ( "Response"
-- > , Details (fromList ([ ( "Format"
-- > , Detail "': * () ('[] *)"
-- > )
-- > , ( "ContentType"
-- > , Detail "()"
-- > )
-- > ]))
-- > )
-- > ]))
-- > )
-- > ])
--
-- For more examples reference [Test.Servant.Docs.Simple.Samples](https://github.com/Holmusk/servant-docs-simple/blob/master/test/Test/Servant/Docs/Simple/Samples.hs)
--
newtype ApiDocs = ApiDocs (OMap Route Details) deriving stock (Eq, Show)
-- | Route representation
type Route = Text
-- | Details of the Api Route
--
-- __Examples__
--
-- > Authentication: true
--
-- Can be interpreted as a Parameter (Authentication) and a /Detail/ (true)
--
-- > Response:
-- > Format: ...
-- > ContentType: ...
--
-- Can be interpreted as a Parameter (Response) and /Details/ (Format (...), ContentType (...))
--
data Details = Details (OMap Parameter Details) -- ^ OMap of Parameter-Details
| Detail Text -- ^ Single Value
deriving stock (Eq, Show)
-- | Parameter names
type Parameter = Text
-- | Convert ApiDocs into different documentation formats
class Renderable a where
render :: ApiDocs -> a
-- | Conversion to JSON using Data.Aeson
newtype Json = Json { getJson :: Value } deriving stock (Eq, Show)
-- | Conversion to JSON using Data.Aeson
instance Renderable Json where
render = Json . toJSON
-- | Json instance for the endpoints hashmap
instance ToJSON ApiDocs where
toJSON (ApiDocs endpoints) = toJSON . fromList . assocs $ endpoints
-- | Json instance for the parameter hashmap of each endpoint
instance ToJSON Details where
toJSON (Detail t) = String t
toJSON (Details ls) = toJSON . fromList . assocs $ ls
-- | Conversion to prettyprint
newtype Pretty ann = Pretty { getPretty :: Doc ann }
-- | Conversion to prettyprint
instance Renderable (Pretty ann) where
render = Pretty . prettyPrint
-- | Helper function to prettyprint the ApiDocs
prettyPrint :: ApiDocs -> Doc ann
prettyPrint (ApiDocs endpoints) = vcat . intersperse line
$ uncurry (toDoc 0) <$> assocs endpoints
-- | Helper function
toDoc :: Int -> Text -> Details -> Doc ann
toDoc i t d = case d of
Detail a -> cat [pretty t, ": ", pretty a]
Details as -> nest i . vsep $ pretty t <> ":"
: (uncurry (toDoc (i + 4)) <$> assocs as)
-- | Conversion to plaintext
newtype PlainText = PlainText { getPlainText :: Text } deriving stock (Eq, Show)
-- | Conversion to plaintext
instance Renderable PlainText where
render = PlainText . pack . show . getPretty . render