aboutsummaryrefslogblamecommitdiffstats
path: root/lib/system.ml
blob: bad6c3b4706fd2ecb99360937681ee18d30baf5c (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11


                                                                                  
 
                                                      





                                                        
                                                                      


                                                                             
                                                       




                                                       
   
        
           












                                                                                  
               

                               




                                                                    


                                                                         


                                                                            

            

                                                   
                       
                                                               
    
                                         











                                                                             
                                           
                                                                     


                                                                      



                                                                     
                                                                

                 
(** 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