mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 11:15:31 -05:00
208 lines
6.9 KiB
OCaml
208 lines
6.9 KiB
OCaml
|
|
(*
|
|
copyright (c) 2013, simon cruanes
|
|
all rights reserved.
|
|
|
|
redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer. redistributions in binary
|
|
form must reproduce the above copyright notice, this list of conditions and the
|
|
following disclaimer in the documentation and/or other materials provided with
|
|
the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*)
|
|
|
|
(** {1 Expose the State of a Program to the Web}
|
|
|
|
We export some values (and associated functions for converting them to
|
|
html, and update them) as a FastCGI interface.
|
|
|
|
This module depends on CamlGI.
|
|
*)
|
|
|
|
(** {2 Some combinators to build HTML documents} *)
|
|
|
|
module HTML : sig
|
|
type t
|
|
(** A html document. Encoding is assumed to be UTF8 for now. *)
|
|
|
|
val str : string -> t
|
|
(** Simple string *)
|
|
|
|
val bprintf : ('a, Buffer.t, unit, unit) format4 -> t
|
|
(** Use a buffer printer to render a string. Shortcut for {!str} *)
|
|
|
|
val sprintf : ('a, unit, string, unit) format4 -> t
|
|
(** Use a string printer to render into a string. Shortcut for {!str} *)
|
|
|
|
val list : t list -> t
|
|
(** Build a list of items *)
|
|
|
|
val url : ?alt:string -> url:string -> descr:string -> t
|
|
(** build an URL tag. *)
|
|
|
|
val img : ?alt:string -> string -> t
|
|
(** Link to an image *)
|
|
|
|
val append : t -> t -> t
|
|
(** Concatenation of two documents *)
|
|
|
|
val concat : t list -> t
|
|
(** Concatenation of html documents *)
|
|
|
|
val h1 : t -> t
|
|
|
|
val h2 : t -> t
|
|
|
|
val h3 : t -> t
|
|
|
|
val h : int -> t -> t
|
|
(** Title of level parametrized by the integer *)
|
|
|
|
val p : t -> t
|
|
(** Paragraph *)
|
|
|
|
val div : ?id:string -> ?class_:string -> t -> t
|
|
(** Div tag, to specify a block *)
|
|
|
|
val span : ?id:string -> ?class_:string -> t -> t
|
|
(** Non semantic tag, mostly useful for CSS *)
|
|
|
|
val link : rel:string -> url:string -> t
|
|
(** Link (for head) *)
|
|
|
|
val head : t -> t
|
|
(** Head part of a document *)
|
|
|
|
val body : t -> t
|
|
(** Body part of a document *)
|
|
|
|
val html : t -> t
|
|
(** The whole document *)
|
|
|
|
val render : t -> string
|
|
(** Print into a string *)
|
|
|
|
val to_buf : Buffer.t -> t -> unit
|
|
(** Print in the buffer *)
|
|
|
|
val to_chan : out_channel -> t -> unit
|
|
(** Print on the channel *)
|
|
end
|
|
|
|
(** {2 Stateful Object on the Web} *)
|
|
|
|
(** This module defines how to bundle an OCaml value (possibly
|
|
stateful) with functions that export it to HTML,
|
|
and possibly update it from a CGI request.
|
|
*)
|
|
|
|
module State : sig
|
|
type 'a t
|
|
(** A value that can be exposed to the web. *)
|
|
|
|
type wrap = Wrap : 'a t -> wrap
|
|
(** Hides the type parameter in a GADT. Useful for {!Router}. *)
|
|
|
|
val create : ?update:((string*string) list -> 'a -> 'a) ->
|
|
?id:('a -> string) ->
|
|
export:('a -> HTML.t) ->
|
|
'a ->
|
|
'a t
|
|
(** Create a value that can be exposed to the Web.
|
|
@param export function used to print the current state of
|
|
the object into HTML (when requested).
|
|
@param update optional function that can be used to update
|
|
the value, given a query with parameters.
|
|
@param id optional function that maps a value to a (unique)
|
|
string. Can be used to obtain a unique URL for this value. *)
|
|
|
|
val on_change : 'a t -> ('a -> unit) -> unit
|
|
(** Register a callback that will be called everytime the value
|
|
is updated. Physical equality is used to determine whether
|
|
the value changed if no [id] function was provided;
|
|
otherwise, [id] is used to check whether the old and the new
|
|
strings are equal. *)
|
|
|
|
val handle_request : 'a t -> CamlGI.Cgi.Request.t -> unit
|
|
(** Handle the incoming request. It replies to the request by
|
|
possibly updating the local state, and
|
|
object. *)
|
|
end
|
|
|
|
|
|
(** {2 Routing} *)
|
|
|
|
module Router : sig
|
|
type t
|
|
(** An URL router. It dispatches incoming requests to registered
|
|
{!State.t} values depending on the request's URL. *)
|
|
|
|
val create : ?default:State.wrap -> ?log:out_channel -> unit -> t
|
|
(** New router.
|
|
@param log a channel on which to log events (incoming requests)
|
|
@param default a default object to expose, for incorrect routes
|
|
*)
|
|
|
|
val default : t -> State.wrap -> unit
|
|
(** Set the default handler, for incorrect routes (for which no
|
|
object is registered) or for routing the root url *)
|
|
|
|
val register : ?weak:bool -> t -> string -> State.wrap -> unit
|
|
(** Register a state object (see {!State}) under a given path.
|
|
Right now routing only dispatches at one level, there is no
|
|
tree-like structure, only a flat "directory" of objects.
|
|
|
|
@param weak (default false) if true, the object will unregister itself
|
|
when it's garbage collected. Only works if the type of the wrapped
|
|
object is heap allocated.
|
|
|
|
@raise Failure if the name is already taken
|
|
@raise Invalid_argument if [weak] is true and no finalizer can be
|
|
registered. *)
|
|
|
|
val unregister : t -> string -> unit
|
|
(** Remove a stateful value *)
|
|
|
|
val add_list : t -> (string * State.wrap) list -> unit
|
|
(** Register several handlers.
|
|
@raise Failure if it meets an already registered handler *)
|
|
|
|
val to_list : t -> (string * State.wrap) list
|
|
(** Currently registered objects *)
|
|
|
|
val random_id : unit -> string
|
|
(** Fresh, random ID that can be used for registering temporary objects *)
|
|
|
|
val handle_request : t -> CamlGI.Cgi.Request.t -> unit
|
|
(** Handle the incoming request, by routing to an appropriate
|
|
object. *)
|
|
end
|
|
|
|
(** {2 Main interface} *)
|
|
|
|
(* TODO: interface with {! LazyGraph}. A (string, html.t, string) graph
|
|
maps naturally to URLs and simplifies routing. *)
|
|
|
|
val serve_state : ?sockfile:string -> ?sockaddr:Unix.sockaddr ->
|
|
'a State.t -> unit
|
|
(** Serve incoming requests using a single object.
|
|
@param sockfile the unix file to use as a socket *)
|
|
|
|
val serve_router : ?sockfile:string -> ?sockaddr:Unix.sockaddr ->
|
|
Router.t -> unit
|
|
(** Shortcut. It calls {!CamlGI.Cgi.register_script} with a callback
|
|
that forwards requests to the given Router.
|
|
@param sockfile the unix file to use as a socket *)
|