acolyte-server-0.1.0.0: src/Acolyte/Server/Combine.hs
-- | Sub-API composition: combine multiple smaller APIs into one server.
--
-- Each sub-API uses flat tuple instances (O(1) compile time). The
-- combined server merges all routes and preserves compile-time
-- completeness checking across all sub-APIs.
--
-- @
-- type UsersAPI = '[Get UsersPath ..., Post UsersPath ...]
-- type ArticlesAPI = '[Get ArticlesPath ..., Delete ArticlePath ...]
--
-- -- Combined type for OpenAPI / client generation
-- type FullAPI = UsersAPI ++ ArticlesAPI
--
-- -- Build server with completeness check for BOTH sub-APIs
-- server = combineServer2 @UsersAPI @ArticlesAPI
-- usersHandlers
-- articleHandlers
-- @
module Acolyte.Server.Combine
( -- * Combining 2 sub-APIs
combineServer2
-- * Combining 3 sub-APIs
, combineServer3
-- * Combining 4 sub-APIs
, combineServer4
-- * Combining 5 sub-APIs
, combineServer5
-- * Combining 6 sub-APIs
, combineServer6
-- * Combining 7 sub-APIs
, combineServer7
-- * Combining 8 sub-APIs
, combineServer8
-- * Low-level: build sub-API router and merge
, subRouter
) where
import Data.ByteString (ByteString)
import Data.Kind (Type)
import Spire.Service (Service)
import Http.Core (Request, Response)
import Acolyte.Core.API (Serves, type (++))
import Acolyte.Server.Handler (HasEndpointInfo)
import Acolyte.Server.Router (Router, emptyRouter, serve)
import Acolyte.Server.Wiring (BuildServer, buildRouter)
-- | Build a router for a sub-API and merge into an existing router.
--
-- This is the building block. Each call is O(1) (flat tuple instance).
subRouter
:: forall api handlers
. (Serves api handlers, BuildServer api handlers)
=> handlers
-> Router
-> Router
subRouter handlers = buildRouter @api handlers
-- | Combine 2 sub-APIs with compile-time completeness for both.
combineServer2
:: forall api1 api2 h1 h2
. ( Serves api1 h1, BuildServer api1 h1
, Serves api2 h2, BuildServer api2 h2
)
=> h1 -> h2
-> Service IO (Request ByteString) (Response ByteString)
combineServer2 h1 h2 = serve
$ subRouter @api2 h2
$ subRouter @api1 h1
$ emptyRouter
-- | Combine 3 sub-APIs.
combineServer3
:: forall api1 api2 api3 h1 h2 h3
. ( Serves api1 h1, BuildServer api1 h1
, Serves api2 h2, BuildServer api2 h2
, Serves api3 h3, BuildServer api3 h3
)
=> h1 -> h2 -> h3
-> Service IO (Request ByteString) (Response ByteString)
combineServer3 h1 h2 h3 = serve
$ subRouter @api3 h3
$ subRouter @api2 h2
$ subRouter @api1 h1
$ emptyRouter
-- | Combine 4 sub-APIs.
combineServer4
:: forall api1 api2 api3 api4 h1 h2 h3 h4
. ( Serves api1 h1, BuildServer api1 h1
, Serves api2 h2, BuildServer api2 h2
, Serves api3 h3, BuildServer api3 h3
, Serves api4 h4, BuildServer api4 h4
)
=> h1 -> h2 -> h3 -> h4
-> Service IO (Request ByteString) (Response ByteString)
combineServer4 h1 h2 h3 h4 = serve
$ subRouter @api4 h4
$ subRouter @api3 h3
$ subRouter @api2 h2
$ subRouter @api1 h1
$ emptyRouter
-- | Combine 5 sub-APIs.
combineServer5
:: forall api1 api2 api3 api4 api5 h1 h2 h3 h4 h5
. ( Serves api1 h1, BuildServer api1 h1
, Serves api2 h2, BuildServer api2 h2
, Serves api3 h3, BuildServer api3 h3
, Serves api4 h4, BuildServer api4 h4
, Serves api5 h5, BuildServer api5 h5
)
=> h1 -> h2 -> h3 -> h4 -> h5
-> Service IO (Request ByteString) (Response ByteString)
combineServer5 h1 h2 h3 h4 h5 = serve
$ subRouter @api5 h5
$ subRouter @api4 h4
$ subRouter @api3 h3
$ subRouter @api2 h2
$ subRouter @api1 h1
$ emptyRouter
-- | Combine 6 sub-APIs.
combineServer6
:: forall api1 api2 api3 api4 api5 api6 h1 h2 h3 h4 h5 h6
. ( Serves api1 h1, BuildServer api1 h1
, Serves api2 h2, BuildServer api2 h2
, Serves api3 h3, BuildServer api3 h3
, Serves api4 h4, BuildServer api4 h4
, Serves api5 h5, BuildServer api5 h5
, Serves api6 h6, BuildServer api6 h6
)
=> h1 -> h2 -> h3 -> h4 -> h5 -> h6
-> Service IO (Request ByteString) (Response ByteString)
combineServer6 h1 h2 h3 h4 h5 h6 = serve
$ subRouter @api6 h6
$ subRouter @api5 h5
$ subRouter @api4 h4
$ subRouter @api3 h3
$ subRouter @api2 h2
$ subRouter @api1 h1
$ emptyRouter
-- | Combine 7 sub-APIs.
combineServer7
:: forall api1 api2 api3 api4 api5 api6 api7 h1 h2 h3 h4 h5 h6 h7
. ( Serves api1 h1, BuildServer api1 h1
, Serves api2 h2, BuildServer api2 h2
, Serves api3 h3, BuildServer api3 h3
, Serves api4 h4, BuildServer api4 h4
, Serves api5 h5, BuildServer api5 h5
, Serves api6 h6, BuildServer api6 h6
, Serves api7 h7, BuildServer api7 h7
)
=> h1 -> h2 -> h3 -> h4 -> h5 -> h6 -> h7
-> Service IO (Request ByteString) (Response ByteString)
combineServer7 h1 h2 h3 h4 h5 h6 h7 = serve
$ subRouter @api7 h7
$ subRouter @api6 h6
$ subRouter @api5 h5
$ subRouter @api4 h4
$ subRouter @api3 h3
$ subRouter @api2 h2
$ subRouter @api1 h1
$ emptyRouter
-- | Combine 8 sub-APIs.
combineServer8
:: forall api1 api2 api3 api4 api5 api6 api7 api8 h1 h2 h3 h4 h5 h6 h7 h8
. ( Serves api1 h1, BuildServer api1 h1
, Serves api2 h2, BuildServer api2 h2
, Serves api3 h3, BuildServer api3 h3
, Serves api4 h4, BuildServer api4 h4
, Serves api5 h5, BuildServer api5 h5
, Serves api6 h6, BuildServer api6 h6
, Serves api7 h7, BuildServer api7 h7
, Serves api8 h8, BuildServer api8 h8
)
=> h1 -> h2 -> h3 -> h4 -> h5 -> h6 -> h7 -> h8
-> Service IO (Request ByteString) (Response ByteString)
combineServer8 h1 h2 h3 h4 h5 h6 h7 h8 = serve
$ subRouter @api8 h8
$ subRouter @api7 h7
$ subRouter @api6 h6
$ subRouter @api5 h5
$ subRouter @api4 h4
$ subRouter @api3 h3
$ subRouter @api2 h2
$ subRouter @api1 h1
$ emptyRouter