blob: bad6c3b4706fd2ecb99360937681ee18d30baf5c (
plain) (
tree)
|
|
(** System assembly: registry-based configuration dispatch.
This module is the composition root. It owns the mapping from configuration
tags to concrete implementations and assembles a server from a config file. *)
type nt_parcel = NtParcel : (module Nt.S) -> nt_parcel
type transport_parcel =
| TransportParcel :
(module Transport.TRANSPORT with type t = 't) * 't
-> transport_parcel
type nt_provider = Sexplib.Sexp.t -> (nt_parcel, string) result
type transport_provider = Sexplib.Sexp.t -> (transport_parcel, string) result
type registry = {
nt : nt_provider Utilities.StringMap.t;
transport : transport_provider Utilities.StringMap.t;
}
let registry : registry =
let open Utilities.StringMap in
{
nt =
empty
|> add "memory" (fun _sexp ->
Ok (NtParcel (module Nt.Memory : Nt.S)))
|> add "sqlite" (fun sexp ->
(* Accept (sqlite) for :memory: or (sqlite "/path/to/db") for a file. *)
let path = match sexp with
| Sexplib.Sexp.(List [ Atom p ]) -> p
| _ -> ":memory:"
in
let module M = Nt.Make (struct
let driver = "sqlite"
let init_arg = path
end) in
Ok (NtParcel (module M : Nt.S)));
transport =
empty
|> add "tcp" (fun sexp ->
let ( let* ) = Result.bind in
let* config = Transport.TCP.parse sexp in
let transport = Transport.TCP.create config in
Ok (TransportParcel ((module Transport.TCP), transport)));
}
let assemble (config : Configuration.t) : (unit -> unit, string) result =
let open Utilities.Result in
let* nt_tag, nt_body =
Configuration.require_section ~name:"nt"
~valid_tags:(Utilities.StringMap.bindings registry.nt |> List.map fst)
config
in
let* nt_provider =
Utilities.StringMap.find_opt nt_tag registry.nt
|> Option.to_result
~none:(Printf.sprintf "Unknown NT backend: %s" nt_tag)
in
let* packed_nt = nt_provider nt_body in
let* transport_tag, transport_body =
Configuration.require_section ~name:"transport"
~valid_tags:
(Utilities.StringMap.bindings registry.transport |> List.map fst)
config
in
let* transport_provider =
Utilities.StringMap.find_opt transport_tag registry.transport
|> Option.to_result
~none:(Printf.sprintf "Unknown transport backend: %s" transport_tag)
in
let* packed_transport = transport_provider transport_body in
let (NtParcel (module NT)) = packed_nt in
let (TransportParcel ((module T), transport)) = packed_transport in
let* () = NT.initialize () |> Result.map_error Nt.string_of_error in
let module L = Listener.Make (T) (NT) in
Ok (fun () -> L.run transport)
let run_from_config (path : string) : (unit -> unit, string) result =
let ( let* ) = Result.bind in
let* config =
Configuration.load ~expected_keys:[ "nt"; "transport" ] path
in
assemble config
|