mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-11 05:45:48 +00:00
167 lines
7.0 KiB
Haskell
167 lines
7.0 KiB
Haskell
{-# LANGUAGE DuplicateRecordFields #-}
|
|
{-# LANGUAGE LambdaCase #-}
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
module API.Docs.Generate where
|
|
|
|
import API.Docs.Commands
|
|
import API.Docs.Events
|
|
import API.Docs.Responses
|
|
import API.Docs.Syntax
|
|
import API.Docs.Syntax.Types
|
|
import API.Docs.Types
|
|
import API.TypeInfo
|
|
import Data.Char (isSpace, isUpper, toLower, toUpper)
|
|
import qualified Data.List.NonEmpty as L
|
|
import Data.Text (Text)
|
|
import qualified Data.Text as T
|
|
|
|
commandsDocFile :: FilePath
|
|
commandsDocFile = "./bots/api/COMMANDS.md"
|
|
|
|
eventsDocFile :: FilePath
|
|
eventsDocFile = "./bots/api/EVENTS.md"
|
|
|
|
typesDocFile :: FilePath
|
|
typesDocFile = "./bots/api/TYPES.md"
|
|
|
|
commandsDocText :: Text
|
|
commandsDocText =
|
|
("# API Commands and Responses\n\n" <> autoGenerated <> "\n")
|
|
<> foldMap commandCatTOC chatCommandsDocs
|
|
<> "\n---\n"
|
|
<> foldMap commandCatText chatCommandsDocs
|
|
where
|
|
commandCatTOC CCCategory {categoryName, commands} =
|
|
(T.pack $ "\n" <> withLink "" categoryName <> "\n")
|
|
<> foldMap commandTOC commands
|
|
where
|
|
commandTOC CCDoc {commandType = ATUnionMember tag _} = T.pack $ "- " <> withLink "" (fstToUpper tag) <> "\n"
|
|
commandCatText CCCategory {categoryName, categoryDescr, commands} =
|
|
(T.pack $ "\n\n## " <> categoryName <> "\n\n" <> categoryDescr <> "\n")
|
|
<> foldMap commandDocText commands
|
|
where
|
|
commandDocText CCDoc {commandType = ATUnionMember tag params, commandDescr, network, syntax, responses, errors} =
|
|
("\n\n### " <> T.pack (fstToUpper tag) <> "\n\n" <> commandDescr <> "\n\n*Network usage*: " <> networkUsage network <> ".\n")
|
|
<> (if null params then "" else paramsText)
|
|
<> (if syntax == "" then "" else syntaxText (tag, params) syntax)
|
|
<> (if length responses > 1 then "\n**Responses**:\n" else "\n**Response**:\n")
|
|
<> foldMap responseText responses
|
|
<> (if null errors then "" else "\n**Errors**:\n")
|
|
<> foldMap errorText errors
|
|
<> "\n---\n"
|
|
where
|
|
paramsText = "\n**Parameters**:\n" <> fieldsText "./TYPES.md" params
|
|
responseText CRDoc {responseType = ATUnionMember tag fields, responseDescr} =
|
|
(T.pack $ "\n" <> fstToUpper tag <> ": " <> respDescr <> ".\n")
|
|
<> ("- type: \"" <> T.pack tag <> "\"\n")
|
|
<> fieldsText "./TYPES.md" fields
|
|
where
|
|
respDescr = if null responseDescr then camelToSpace tag else responseDescr
|
|
errorText (TD err descr) =
|
|
let descr' = if null descr then camelToSpace err else descr
|
|
in T.pack $ "- " <> fstToUpper err <> ": " <> descr' <> ".\n"
|
|
|
|
networkUsage :: Maybe UsesNetwork -> Text
|
|
networkUsage = \case
|
|
Nothing -> "no"
|
|
Just UNInteractive -> "interactive"
|
|
Just UNBackground -> "background"
|
|
|
|
syntaxText :: TypeAndFields -> Expr -> Text
|
|
syntaxText r syntax =
|
|
"\n**Syntax**:\n"
|
|
<> "\n```\n" <> docSyntaxText r syntax <> "\n```\n"
|
|
<> (if isConst syntax then "" else "\n```javascript\n" <> jsSyntaxText False "" r syntax <> " // JavaScript\n```\n")
|
|
<> (if isConst syntax then "" else "\n```python\n" <> pySyntaxText r syntax <> " # Python\n```\n")
|
|
|
|
camelToSpace :: String -> String
|
|
camelToSpace [] = []
|
|
camelToSpace (x : xs) = toUpper x : go xs
|
|
where
|
|
go [] = []
|
|
go (y : ys)
|
|
| isUpper y = ' ' : toLower y : go ys
|
|
| otherwise = y : go ys
|
|
|
|
eventsDocText :: Text
|
|
eventsDocText =
|
|
("# API Events\n\n" <> autoGenerated <> "\n")
|
|
<> foldMap eventCatTOC chatEventsDocs
|
|
<> "\n---\n"
|
|
<> foldMap eventCatText chatEventsDocs
|
|
where
|
|
eventCatTOC CECategory {categoryName, mainEvents, otherEvents} =
|
|
(T.pack $ "\n" <> withLink "" categoryName <> "\n")
|
|
<> (if hasOthers then "- Main event" <> plural mainEvents <> "\n" else "")
|
|
<> foldMap eventTOC mainEvents
|
|
<> (if hasOthers then "- Other event" <> plural otherEvents <> "\n" <> foldMap eventTOC otherEvents else "")
|
|
where
|
|
eventTOC CEDoc {eventType = ATUnionMember tag _} = T.pack $ indent <> "- " <> withLink "" (fstToUpper tag) <> "\n"
|
|
hasOthers = not $ null otherEvents
|
|
indent = if hasOthers then " " else ""
|
|
eventCatText CECategory {categoryName, categoryDescr, mainEvents, otherEvents} =
|
|
(T.pack $ "\n\n## " <> categoryName <> "\n\n" <> categoryDescr <> "\n")
|
|
<> foldMap eventDocText (mainEvents ++ otherEvents)
|
|
where
|
|
eventDocText CEDoc {eventType = ATUnionMember tag fields, eventDescr} =
|
|
(T.pack $ "\n\n### " <> fstToUpper tag <> "\n\n" <> evtDescr)
|
|
<> "\n\n**Record type**:\n"
|
|
<> ("- type: \"" <> T.pack tag <> "\"\n")
|
|
<> fieldsText "./TYPES.md" fields
|
|
<> "\n---\n"
|
|
where
|
|
evtDescr = if null eventDescr then camelToSpace tag <> "." else eventDescr
|
|
plural evts = if length evts == 1 then "" else "s"
|
|
|
|
typesDocText :: Text
|
|
typesDocText =
|
|
("# API Types\n\n" <> autoGenerated <> "\n")
|
|
<> (foldMap (\t -> T.pack $ "\n- " <> withLink "" (docTypeName t)) chatTypesDocs <> "\n")
|
|
<> foldMap typeDocText chatTypesDocs
|
|
where
|
|
typeDocText CTDoc {typeDef = td@APITypeDef {typeName' = name, typeDef}, typeDescr, typeSyntax} =
|
|
("\n\n---\n\n## " <> T.pack name <> "\n")
|
|
<> (if T.null typeDescr then "" else "\n" <> typeDescr <> "\n")
|
|
<> typeDefText typeDef
|
|
<> (if typeSyntax == "" then "" else syntaxText (name, self : typeFields) typeSyntax)
|
|
where
|
|
self = APIRecordField "self" (ATDef td)
|
|
typeFields = case typeDef of
|
|
ATDRecord fs -> fs
|
|
ATDUnion ms -> APIRecordField "type" tagType : concatMap (\(ATUnionMember _ fs) -> fs) ms
|
|
where
|
|
tagType = ATDef $ APITypeDef (name <> ".type") $ ATDEnum tags
|
|
tags = L.map (\(ATUnionMember tag _) -> tag) ms
|
|
ATDEnum _ -> []
|
|
typeDefText = \case
|
|
ATDRecord fields -> "\n**Record type**:\n" <> fieldsText "" fields
|
|
ATDEnum cs -> "\n**Enum type**:\n" <> foldMap (\m -> "- \"" <> T.pack m <> "\"\n") cs
|
|
ATDUnion cs -> "\n**Discriminated union type**:\n" <> foldMap constrText cs
|
|
where
|
|
constrText (ATUnionMember tag fields) =
|
|
("\n" <> T.pack (fstToUpper tag) <> ":\n")
|
|
<> ("- type: \"" <> T.pack tag <> "\"\n")
|
|
<> fieldsText "" fields
|
|
|
|
fieldsText :: FilePath -> [APIRecordField] -> Text
|
|
fieldsText docPath = foldMap $ T.pack . fieldText
|
|
where
|
|
fieldText (APIRecordField name t) = "- " <> name <> ": " <> typeText t <> "\n"
|
|
typeText = \case
|
|
ATPrim (PT t) -> t
|
|
ATDef (APITypeDef t _) -> withLink docPath t
|
|
ATRef t -> withLink docPath t
|
|
ATOptional t -> typeText t <> "?"
|
|
ATArray {elemType} -> "[" <> typeText elemType <> "]"
|
|
ATMap (PT t) valueType -> "{" <> t <> " : " <> typeText valueType <> "}"
|
|
|
|
autoGenerated :: Text
|
|
autoGenerated = "This file is generated automatically."
|
|
|
|
withLink :: FilePath -> String -> String
|
|
withLink docPath s = "[" <> s <> "](" <> docPath <> "#" <> headerAnchor s <> ")"
|
|
where
|
|
headerAnchor = map $ \c -> if isSpace c then '-' else toLower c
|