diff --git a/src/plugins/server/data.ml b/src/plugins/server/data.ml index 10a7eef47743d582d6e0075dc82de2759b6a995b..1a0f56ec23f196813d20c0d2c49e9c815731e17e 100644 --- a/src/plugins/server/data.ml +++ b/src/plugins/server/data.ml @@ -38,6 +38,14 @@ sig val to_json : t -> json end +module type Info = +sig + val name : string + val descr : Markdown.text +end + +type 'a data = (module S with type t = 'a) + let d_tuple ts = Markdown.(tt "[" <+> glue ~sep:(raw " `,` ") ts <+> tt "]") let d_record txt = Markdown.(tt "{" <+> txt <+> tt "}") let d_array txt = Markdown.(tt "[" <+> txt <+> tt ",…]") @@ -45,210 +53,75 @@ let d_option txt = Markdown.(txt <@> tt "?") let failure msg js = raise (Jutil.Type_error(msg,js)) -(* -------------------------------------------------------------------------- *) -(* --- Field --- *) -(* -------------------------------------------------------------------------- *) - -type 'a field = { - name: string ; - field: Markdown.text ; - default: Markdown.text option ; - descr: Markdown.text ; - optional: bool ; - get: ('a -> json option) option ; - set: ('a -> json -> 'a) option ; -} - -module type S_field = -sig - include S - - val mk_field : - name:string -> - optional:bool -> - ?default:Markdown.text -> - descr:Markdown.text -> - ?get:('a -> t option) -> - ?set:('a -> t -> 'a) -> - unit -> 'a field - - val field : - name:string -> - descr:string -> - ('a -> t) -> - ('a -> t -> 'a) -> - 'a field - - val option : - name:string -> - ?default:string -> - descr:string -> - ('a -> t option) -> - ('a -> t -> 'a) -> - 'a field - - val getter : - name:string -> - descr:string -> - ('a -> t) -> - 'a field - - val getopt : - name:string -> - ?default:string -> - descr:string -> - ('a -> t option) -> - 'a field - - val setter : - name:string -> - descr:string -> - ('a -> t -> 'a) -> - 'a field - - val setopt : - name:string -> - ?default:string -> - descr:string -> - ('a -> t -> 'a) -> - 'a field - -end - -module Field(A : S) : S_field with type t = A.t = -struct - include A - - let opt f = function None -> None | Some x -> Some (f x) - - let mk_field ~name ~optional ?default ~descr ?get ?set () = - begin match get , set with - | None , None -> - raise (Invalid_argument "Server.Data.field: no setter and no getter") - | _ -> () - end ; - { - name ; optional ; default ; descr ; - field = A.descr ; - set = opt (fun f data js -> f data (A.of_json js)) set ; - get = opt (fun f data -> opt A.to_json (f data)) get ; - } - - let field ~name ~descr get set = - mk_field ~name - ~descr:(Markdown.rm descr) - ~optional:false - ~get:(fun d -> Some (get d)) ~set () - - let option ~name ?default ~descr get set = - mk_field ~name - ~descr:(Markdown.rm descr) - ?default:(opt Markdown.rm default) - ~optional:true - ~get ~set () - - let getter ~name ~descr get = - mk_field ~name - ~optional:false - ~descr:(Markdown.rm descr) - ~get:(fun d -> Some (get d)) () - - let setter ~name ~descr set = - mk_field ~name - ~optional:false - ~descr:(Markdown.rm descr) - ~set () - - let getopt ~name ?default ~descr get = - mk_field ~name - ~optional:true - ?default:(opt Markdown.rm default) - ~descr:(Markdown.rm descr) - ~get () - - let setopt ~name ?default ~descr set = - mk_field ~name - ~optional:true - ?default:(opt Markdown.rm default) - ~descr:(Markdown.rm descr) - ~set () - -end - (* -------------------------------------------------------------------------- *) (* --- Option --- *) (* -------------------------------------------------------------------------- *) -module Joption(A : S) : S_field with type t = A.t option = - Field - (struct - type t = A.t option +module Joption(A : S) : S with type t = A.t option = +struct + type t = A.t option - let nullable = try ignore (A.of_json `Null) ; true with _ -> false - let descr = d_option (if nullable then A.descr else d_tuple [A.descr]) + let nullable = try ignore (A.of_json `Null) ; true with _ -> false + let descr = d_option (if nullable then A.descr else d_tuple [A.descr]) - let to_json = function - | None -> `Null - | Some v -> if nullable then `List [A.to_json v] else A.to_json v + let to_json = function + | None -> `Null + | Some v -> if nullable then `List [A.to_json v] else A.to_json v - let of_json = function - | `Null -> None - | `List [js] when nullable -> Some (A.of_json js) - | js -> Some (A.of_json js) + let of_json = function + | `Null -> None + | `List [js] when nullable -> Some (A.of_json js) + | js -> Some (A.of_json js) - end) +end (* -------------------------------------------------------------------------- *) (* --- Tuples --- *) (* -------------------------------------------------------------------------- *) -module Jpair(A : S)(B : S) : S_field with type t = A.t * B.t = - Field - (struct - type t = A.t * B.t - let descr = d_tuple [A.descr;B.descr] - let to_json (x,y) = `List [ A.to_json x ; B.to_json y ] - let of_json = function - | `List [ ja ; jb ] -> A.of_json ja , B.of_json jb - | js -> raise (Jutil.Type_error( "Expected list with 2 elements" , js )) - end) +module Jpair(A : S)(B : S) : S with type t = A.t * B.t = +struct + type t = A.t * B.t + let descr = d_tuple [A.descr;B.descr] + let to_json (x,y) = `List [ A.to_json x ; B.to_json y ] + let of_json = function + | `List [ ja ; jb ] -> A.of_json ja , B.of_json jb + | js -> raise (Jutil.Type_error( "Expected list with 2 elements" , js )) +end -module Jtriple(A : S)(B : S)(C : S) : S_field with type t = A.t * B.t * C.t = - Field - (struct - type t = A.t * B.t * C.t - let descr = d_tuple [A.descr;B.descr;C.descr] - let to_json (x,y,z) = `List [ A.to_json x ; B.to_json y ; C.to_json z ] - let of_json = function - | `List [ ja ; jb ; jc ] -> A.of_json ja , B.of_json jb , C.of_json jc - | js -> raise (Jutil.Type_error( "Expected list with 3 elements" , js )) - end) +module Jtriple(A : S)(B : S)(C : S) : S with type t = A.t * B.t * C.t = +struct + type t = A.t * B.t * C.t + let descr = d_tuple [A.descr;B.descr;C.descr] + let to_json (x,y,z) = `List [ A.to_json x ; B.to_json y ; C.to_json z ] + let of_json = function + | `List [ ja ; jb ; jc ] -> A.of_json ja , B.of_json jb , C.of_json jc + | js -> raise (Jutil.Type_error( "Expected list with 3 elements" , js )) +end (* -------------------------------------------------------------------------- *) (* --- Lists --- *) (* -------------------------------------------------------------------------- *) -module Jlist(A : S) : S_field with type t = A.t list = - Field - (struct - type t = A.t list - let descr = d_array A.descr - let to_json xs = `List (List.map A.to_json xs) - let of_json js = List.map A.of_json (Jutil.to_list js) - end) +module Jlist(A : S) : S with type t = A.t list = +struct + type t = A.t list + let descr = d_array A.descr + let to_json xs = `List (List.map A.to_json xs) + let of_json js = List.map A.of_json (Jutil.to_list js) +end (* -------------------------------------------------------------------------- *) (* --- Arrays --- *) (* -------------------------------------------------------------------------- *) -module Jarray(A : S) : S_field with type t = A.t array = - Field - (struct - type t = A.t array - let descr = d_array A.descr - let to_json xs = `List (List.map A.to_json (Array.to_list xs)) - let of_json js = Array.of_list @@ List.map A.of_json (Jutil.to_list js) - end) +module Jarray(A : S) : S with type t = A.t array = +struct + type t = A.t array + let descr = d_array A.descr + let to_json xs = `List (List.map A.to_json (Array.to_list xs)) + let of_json js = Array.of_list @@ List.map A.of_json (Jutil.to_list js) +end (* -------------------------------------------------------------------------- *) (* --- Collections --- *) @@ -256,15 +129,15 @@ module Jarray(A : S) : S_field with type t = A.t array = module type S_collection = sig - include S_field - module Joption : S_field with type t = t option - module Jlist : S_field with type t = t list - module Jarray : S_field with type t = t array + include S + module Joption : S with type t = t option + module Jlist : S with type t = t list + module Jarray : S with type t = t array end module Collection(A : S) : S_collection with type t = A.t = struct - include Field(A) + include A module Joption = Joption(A) module Jlist = Jlist(A) module Jarray = Jarray(A) @@ -282,14 +155,13 @@ struct let to_json () = `Null end -module Jany : S_field with type t = json = - Field - (struct - type t = json - let descr = Markdown.it "any" - let of_json js = js - let to_json js = js - end) +module Jany : S with type t = json = +struct + type t = json + let descr = Markdown.it "any" + let of_json js = js + let to_json js = js +end module Jbool : S_collection with type t = bool = Collection @@ -339,111 +211,87 @@ end (* --- Records --- *) (* -------------------------------------------------------------------------- *) -module Record = +module Fmap = Map.Make(String) + +module Record( R : Info ) = struct - type 'a record = 'a field list + type t = json Fmap.t + let descr = Markdown.it R.name - let descr_table + type 'a field = { + member : t -> bool ; + getter : t -> 'a ; + setter : t -> 'a -> t ; + } + + (* Declared Fields in this Record *) + let fdocs = ref [] + let defaults = ref Fmap.empty + + let default () = !defaults + let has fd r = fd.member r + let get fd r = fd.getter r + let set fd v r = fd.setter r v + + let field (type a) name ~descr ?default (d : a data) : a field = + let module D = (val d) in + let def = match default with + | None -> None + | Some v -> + let jd = D.to_json v in + defaults := Fmap.add name jd !defaults ; Some jd + in fdocs := (name , D.descr , def , descr) :: !fdocs ; + let member r = Fmap.mem name r in + let getter r = D.of_json (Fmap.find name r) in + let setter r v = Fmap.add name (D.to_json v) r in + { member ; getter ; setter } + + let option (type a) name ~descr (d : a data) : a option field = + let module D = (val d) in + fdocs := (name , d_option D.descr , None , descr) :: !fdocs ; + let member r = Fmap.mem name r in + let getter r = + try Some (D.of_json (Fmap.find name r)) with Not_found -> None in + let setter r = function + | None -> Fmap.remove name r + | Some v -> Fmap.add name (D.to_json v) r in + { member ; getter ; setter } + + let details ?(field=`Center "Field") ?(format=`Center "Format") ?(default=`Center "Default") ?(descr=`Left "Description") - ?(filter=(fun _ -> true)) - record = - let defs = ref false in - let fields = List.filter - (fun fd -> - if filter fd then - ((if fd.default<>None then defs := true) ; true) - else false) - record in - if fields = [] then Markdown.empty else - let typ fd = if fd.optional then d_option fd.field else fd.field in - if !defs then - let def = function None -> Markdown.rm "" | Some text -> text in - Markdown.table - [ field ; format ; default ; descr ] - (List.map - (fun fd -> [ - Markdown.tt fd.name ; - typ fd ; - def fd.default ; - fd.descr ; - ]) - fields) - else - Markdown.table - [ field ; format ; descr ] - (List.map - (fun fd -> [ Markdown.tt fd.name ; typ fd ; fd.descr ]) - fields) - - let rec getters = function - | { name ; optional ; get = Some f } :: fds -> - (name,optional,f) :: getters fds - | _ :: fds -> getters fds - | [] -> [] - - let rec setters = function - | { name ; optional ; set = Some f } :: fds -> - (name,optional,f) :: setters fds - | _ :: fds -> setters fds - | [] -> [] - - let parser stage index default setters = - let values = Array.make (Array.length stage) None in - List.iter - (fun (fd,js) -> - try - let i = Hashtbl.find index fd in - if values.(i) = None then - failure (Printf.sprintf "Duplicate field %S" fd) js ; - values.(i) <- Some js ; - with Not_found -> - failure (Printf.sprintf "Unexpected field %S" fd) js - ) setters ; - let value = ref default in - Array.iteri - (fun i (name,required,set) -> - match values.(i) with - | None -> - if required then - failwith (Printf.sprintf "Missing field %S" name) - | Some js -> value := set !value js - ) stage ; - !value - - let of_json record = - let stage = Array.of_list (setters record) in - let index = Hashtbl.create (Array.length stage) in - Array.iteri - (fun i (name,_,_) -> - if Hashtbl.mem index name then - raise (Invalid_argument - "Server.Data.Record.compile: duplicate field") ; - Hashtbl.add index name i) - stage ; - fun default json -> - match json with - | `Null -> default - | `Assoc fields -> - begin - try parser stage index default fields - with Failure msg -> failure msg json - end - | js -> failure "Record expected" js - - let formatter data (name,_optional,f) fds = - match f data with - | None -> fds - | Some js -> (name,js) :: fds - - let to_json record = - let printer = getters record in - fun data -> - let fields = List.fold_right (formatter data) printer [] in - if fields = [] then `Null else `Assoc fields + () + = + if Fmap.is_empty !defaults then + Markdown.table [ field ; format ; descr ] + (List.map + (fun (fd,fmt,_def,descr) -> [ Markdown.tt fd ; fmt ; descr ]) + !fdocs) + else + let mk_format def fmt = if def <> None then d_option fmt else fmt in + let mk_default = function + | None -> Markdown.text [] + | Some js -> Markdown.tt (Json.to_string js) in + Markdown.table [ field ; format ; default ; descr ] + (List.map + (fun (fd,fmt,def,descr) -> [ + Markdown.tt fd ; + mk_format def fmt ; + mk_default def ; + descr ; + ]) + !fdocs) + + let of_json js = + List.fold_left + (fun r (fd,js) -> Fmap.add fd js r) + (default ()) (Jutil.to_assoc js) + + let to_json r : json = + `Assoc (Fmap.fold (fun fd js fds -> (fd,js) :: fds) r []) end @@ -461,12 +309,6 @@ sig val find : key -> 'a t -> 'a end -module type IndexInfo = -sig - val name : string - val descr : Markdown.text -end - module type Index = sig include S_collection @@ -475,7 +317,7 @@ sig val clear : unit -> unit end -module INDEXER(M : Map)(I : IndexInfo) : +module INDEXER(M : Map)(I : Info) : sig type index val create : unit -> index @@ -526,7 +368,7 @@ struct end -module Static(M : Map)(I : IndexInfo) : Index with type t = M.key = +module Static(M : Map)(I : Info) : Index with type t = M.key = struct module INDEX = INDEXER(M)(I) let index = INDEX.create () @@ -542,7 +384,7 @@ struct end) end -module Index(M : Map)(I : IndexInfo) : Index with type t = M.key = +module Index(M : Map)(I : Info) : Index with type t = M.key = struct module INDEX = INDEXER(M)(I) @@ -582,8 +424,7 @@ module type IdentifiedType = sig type t val id : t -> int - val name : string - val descr : Markdown.text + include Info end module Identified(A : IdentifiedType) : Index with type t = A.t = @@ -635,9 +476,8 @@ end module type Enum = sig type t - val name : string - val descr : Markdown.text val values : (t * string * Markdown.text) list + include Info end module Dictionary(E : Enum) : diff --git a/src/plugins/server/data.mli b/src/plugins/server/data.mli index 761b3aeee892697b57a11bbb3c9525cb94433a6b..5650d886ab60249e0842f15819c0abd47c9e079d 100644 --- a/src/plugins/server/data.mli +++ b/src/plugins/server/data.mli @@ -36,166 +36,89 @@ sig val to_json : t -> json end -(* -------------------------------------------------------------------------- *) -(** {2 Collections} *) -(* -------------------------------------------------------------------------- *) - -(** Record field specification. *) -type 'a field = { - - name: string ; (** Name of JSON field *) - field: Markdown.text ; (** Format of JSON field *) - default: Markdown.text option ; (** Description of the field default *) - descr: Markdown.text ; (** Description of the JSON field *) - - optional: bool ; (** Whether the field is optional or not *) - - get: ('a -> json option) option ; - (** Accessor for building the « field » value from type ['a], if - to be including in the output. *) - - set: ('a -> json -> 'a) option ; - (** Updater for some under-construction « record » value of type ['a] - with the field value. *) - -} - -module type S_field = +module type Info = sig - include S + val name : string + val descr : Markdown.text +end - (** Generic field build. - At least one of [~get] or [~set] shall be specified. *) - val mk_field : - name:string -> - optional:bool -> - ?default:Markdown.text -> - descr:Markdown.text -> - ?get:('a -> t option) -> - ?set:('a -> t -> 'a) -> - unit -> 'a field - - (** Helper for simple (required) field *) - val field : - name:string -> - descr:string -> - ('a -> t) -> - ('a -> t -> 'a) -> - 'a field - - (** Helper for simple (optional) field *) - val option : - name:string -> - ?default:string -> - descr:string -> - ('a -> t option) -> - ('a -> t -> 'a) -> - 'a field - - (** Helper for simple fields with only a getter. *) - val getter : - name:string -> - descr:string -> - ('a -> t) -> - 'a field - - (** Helper for simple fields with only an optional getter. *) - val getopt : - name:string -> - ?default:string -> - descr:string -> - ('a -> t option) -> - 'a field - - (** Helper for simple fields with only a (required) setter. *) - val setter : - name:string -> - descr:string -> - ('a -> t -> 'a) -> - 'a field - - (** Helper for simple fields with only a (required) setter. *) - val setopt : - name:string -> - ?default:string -> - descr:string -> - ('a -> t -> 'a) -> - 'a field +type 'a data = (module S with type t = 'a) -end +(* -------------------------------------------------------------------------- *) +(** {2 Collections} *) +(* -------------------------------------------------------------------------- *) module type S_collection = sig - include S_field - module Joption : S_field with type t = t option - module Jlist : S_field with type t = t list - module Jarray : S_field with type t = t array + include S + module Joption : S with type t = t option + module Jlist : S with type t = t list + module Jarray : S with type t = t array end -module Field(A : S) : S_field with type t = A.t module Collection(A : S) : S_collection with type t = A.t (* -------------------------------------------------------------------------- *) (** {2 Constructors} *) (* -------------------------------------------------------------------------- *) -module Joption(A : S) : S_field with type t = A.t option -module Jpair(A : S)(B : S) : S_field with type t = A.t * B.t -module Jtriple(A : S)(B : S)(C : S) : S_field with type t = A.t * B.t * C.t -module Jlist(A : S) : S_field with type t = A.t list -module Jarray(A : S) : S_field with type t = A.t array +module Joption(A : S) : S with type t = A.t option +module Jpair(A : S)(B : S) : S with type t = A.t * B.t +module Jtriple(A : S)(B : S)(C : S) : S with type t = A.t * B.t * C.t +module Jlist(A : S) : S with type t = A.t list +module Jarray(A : S) : S with type t = A.t array (* -------------------------------------------------------------------------- *) (** {2 Atomic Data} *) (* -------------------------------------------------------------------------- *) module Junit : S with type t = unit -module Jany : S_field with type t = json +module Jany : S with type t = json module Jbool : S_collection with type t = bool module Jint : S_collection with type t = int module Jfloat : S_collection with type t = float module Jstring : S_collection with type t = string -module Jtext : S_field with type t = json +module Jtext : S with type t = json (** Rich text encoding, see [Jbuffer] *) (* -------------------------------------------------------------------------- *) -(** {2 Record Helper} *) +(** {2 Records} *) (* -------------------------------------------------------------------------- *) -module Record : +module Record(R : Info) : sig + (** A new type [t] is created for each application of the functor. *) + include S - (** Ordered collection of fields to finally build values of type ['a] *) - type 'a record = 'a field list - - (** Create a parser of JSON records from the specification. - Each field setter is applied in its order of declaration. - Extra fields or missing required ones leads to errors. *) - val of_json : 'a record -> ('a -> json -> 'a) - - (** Create a formatter into JSON records from the specification. - Each field getter is applied when specified. *) - val to_json : 'a record -> ('a -> json) - - (** Output a description table for the field specification. - Options allow to configure the columns of the table, and the rows to - be printed. - - [~field] is the field name column title (defaults to [`Center "Field"]) - - [~format] is the field format column title (defaults to [`Center "Format"]) - - [~default] if an optional column title for defaults (defaults to [`Center "Default"]) - - [~descr] is the field description title (defaults to [`Left "Description"]) - - [~filter] is an optional filer over field specifications - - The [default] column is discarded if none of the filtered field has - a default description. The output is [Markdown.empty] if all fields are - filtered out. *) - val descr_table : + (** Parametric field. Can only be used with type [t]. *) + type 'a field + + (** Field constructor *) + val field : string -> descr:Markdown.text -> ?default:'a -> 'a data -> 'a field + + (** Optional field constructor *) + val option : string -> descr:Markdown.text -> 'a data -> 'a option field + + (** Field presence. If the field has a default value, it will be always + present. *) + val has : 'a field -> t -> bool + + (** Field accessor. + @raise Not_found if the field is optional and not present *) + val get : 'a field -> t -> 'a + + (** Field updator. *) + val set : 'a field -> 'a -> t -> t + + (** Contains only the default values. *) + val default : unit -> t + + val details : ?field:Markdown.column -> ?format:Markdown.column -> ?default:Markdown.column -> ?descr:Markdown.column -> - ?filter:('a field -> bool) -> - 'a record -> Markdown.block + unit -> Markdown.block end @@ -213,12 +136,6 @@ sig val find : key -> 'a t -> 'a end -module type IndexInfo = -sig - val name : string - val descr : Markdown.text (** Actually an integer JSON *) -end - module type Index = sig include S_collection @@ -229,10 +146,10 @@ sig end (** Builds an indexer that {i does not} depend on current project. *) -module Static(M : Map)(I : IndexInfo) : Index with type t = M.key +module Static(M : Map)(I : Info) : Index with type t = M.key (** Builds a {i projectified} index. *) -module Index(M : Map)(I : IndexInfo) : Index with type t = M.key +module Index(M : Map)(I : Info) : Index with type t = M.key (* -------------------------------------------------------------------------- *) (** {2 Identified Types} *) @@ -256,9 +173,9 @@ module Identified(A : IdentifiedType) : Index with type t = A.t module type Enum = sig type t + val values : (t * string * Markdown.text) list val name : string val descr : Markdown.text - val values : (t * string * Markdown.text) list end module Dictionary(E : Enum) : diff --git a/src/plugins/server/kernel_fc.ml b/src/plugins/server/kernel_fc.ml index 2ef1873d5588525a089a9cfe6c3adb1dbad2c510..51bac5783330bd87adc44fce39e773454cf1a39b 100644 --- a/src/plugins/server/kernel_fc.ml +++ b/src/plugins/server/kernel_fc.ml @@ -40,27 +40,22 @@ struct type t = unit let descr = Markdown.tt "{ … }" - let version = - Jstring.getter ~name:"version" ~descr:"Frama-C version" - (fun () -> Config.version) - - let datadir = - Jstring.getter ~name:"datadir" ~descr:"Shared directory (FRAMAC_SHARE)" - (fun () -> Config.datadir) - - let libdir = - Jstring.getter ~name:"libdir" ~descr:"Lib directory (FRAMAC_LIB)" - (fun () -> Config.datadir) - - let pluginpath = - Jstring.Jlist.getter ~name:"pluginpath" - ~descr:"Plugin directories (FRAMAC_PLUGIN)" - (fun () -> Config.plugin_dir) + let to_json () = `Assoc [ + "version" , Jstring.to_json Config.version ; + "datadir" , Jstring.to_json Config.datadir ; + "libdir" , Jstring.to_json Config.libdir ; + "pluginpath" , Jstring.Jlist.to_json Config.plugin_dir ; + ] - let record = [ version ; datadir ; libdir ; pluginpath ] + let details = + let open Markdown in + table [ `Left "field" ; `Left "format" ; `Left "Description" ] [ + [ tt "'version'" ; Jstring.descr ; rm "Frama-C version" ] ; + [ tt "'datadir'" ; Jstring.descr ; rm "Shared directory (FRAMAC_SHARE)" ] ; + [ tt "'libdir'" ; Jstring.descr ; rm "Lib directory (FRAMAC_LIB)" ] ; + [ tt "'pluginpath'" ; Jstring.Jlist.descr ; rm "Plugin directories (FRAMAC_PLUGIN)" ] ; + ] - let to_json = Data.Record.to_json record - let details = Data.Record.descr_table record end module GetConfig = @@ -143,44 +138,48 @@ end module RawEvent = struct - let kind = LogKind.field ~name:"kind" ~descr:"Message kind" - Log.(fun evt -> evt.evt_kind) - Log.(fun evt evt_kind -> { evt with evt_kind }) - - let plugin = Jstring.field ~name:"plugin" ~descr:"Emitter plugin" - Log.(fun evt -> evt.evt_plugin) - Log.(fun evt evt_plugin -> { evt with evt_plugin }) + module R = Record + (struct + let name = "log" + let descr = Markdown.href (Doc.href fc_page "log") + end) - let category = Jstring.option ~name:"category" - ~descr:"Message category (DEBUG or WARNING)" - Log.(fun evt -> evt.evt_category) - Log.(fun evt a -> { evt with evt_category = Some a }) + let descr = Markdown.rm - let source = LogSource.option ~name:"source" ~descr:"Source file position" - Log.(fun evt -> evt.evt_source) - Log.(fun evt s -> { evt with evt_source = Some s }) + let kind = R.field "kind" ~descr:(descr "Message kind") (module LogKind) + let plugin = R.field "plugin" ~descr:(descr "Emitter plugin") (module Jstring) + let message = R.field "message" ~descr:(descr "Message text") (module Jstring) - let message = Jstring.field ~name:"message" ~descr:"Message text" - Log.(fun evt -> evt.evt_message) - Log.(fun evt evt_message -> { evt with evt_message }) + let category = R.option "category" + ~descr:(descr "Message category (DEBUG or WARNING)") + (module Jstring) - let record = [ kind ; plugin ; category ; source ; message ] + let source = R.option "source" ~descr:(descr "Source file position") + (module LogSource) type t = Log.event - let default = Log.{ - evt_plugin = "" ; - evt_kind = Feedback ; - evt_category = None ; - evt_source = None ; - evt_message = "" ; + let to_json evt = + R.default () |> + R.set plugin evt.Log.evt_plugin |> + R.set kind evt.Log.evt_kind |> + R.set category evt.Log.evt_category |> + R.set source evt.Log.evt_source |> + R.set message evt.Log.evt_message |> + R.to_json + + let of_json js = + let r = R.of_json js in + { + Log.evt_plugin = R.get plugin r ; + Log.evt_kind = R.get kind r ; + Log.evt_category = R.get category r ; + Log.evt_source = R.get source r ; + Log.evt_message = R.get message r ; } - let to_json = Record.to_json record - let of_json = Record.of_json record default - let descr = Markdown.href (Doc.href fc_page "log") - let details = Record.descr_table record + let details = R.details () end