From 67033aa70226d4c95efc2bc31fa295c4248b5c9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr>
Date: Wed, 17 Jun 2020 08:53:55 +0200
Subject: [PATCH] [server] functional API & enum lookup

---
 src/plugins/server/data.ml        | 74 +++++++++++++++++++++++++------
 src/plugins/server/data.mli       | 31 ++++++++++---
 src/plugins/server/kernel_main.ml | 16 ++++---
 3 files changed, 96 insertions(+), 25 deletions(-)

diff --git a/src/plugins/server/data.ml b/src/plugins/server/data.ml
index 8620eccd1a4..63744296bf4 100644
--- a/src/plugins/server/data.ml
+++ b/src/plugins/server/data.ml
@@ -39,8 +39,6 @@ sig
   val to_json : t -> json
 end
 
-type 'a data = (module S with type t = 'a)
-
 exception InputError of string
 
 let failure ?json msg =
@@ -228,6 +226,47 @@ struct
     datatype ~package ~name:"text" ~descr jdef
 end
 
+(* -------------------------------------------------------------------------- *)
+(* --- Functional API                                                     --- *)
+(* -------------------------------------------------------------------------- *)
+
+type 'a data = (module S with type t = 'a)
+
+let junit : unit data = (module Junit)
+let jany : json data = (module Jany)
+let jbool : bool data = (module Jbool)
+let jint : int data = (module Jint)
+let jfloat : float data = (module Jfloat)
+let jstring : string data = (module Jstring)
+
+let jkey ~kind =
+  let module JkeyKind =
+  struct
+    include Jstring
+    let jtype = Jkey kind
+  end in
+  (module JkeyKind : S with type t = string)
+
+let jindex ~kind =
+  let module JindexKind =
+  struct
+    include Jint
+    let jtype = Jindex kind
+  end in
+  (module JindexKind : S with type t = int)
+
+let joption (type a) (d : a data) : a option data =
+  let module A = Joption(val d) in
+  (module A : S with type t = a option)
+
+let jlist (type a) (d : a data) : a list data =
+  let module A = Jlist(val d) in
+  (module A : S with type t = a list)
+
+let jarray (type a) (d : a data) : a array data =
+  let module A = Jarray(val d) in
+  (module A : S with type t = a array)
+
 (* -------------------------------------------------------------------------- *)
 (* --- Records                                                            --- *)
 (* -------------------------------------------------------------------------- *)
@@ -377,6 +416,7 @@ struct
     mutable syntax : Markdown.text ;
     mutable published : (package * string) option ;
     mutable tags : tagInfo list ;
+    mutable lookup : ('a -> string) option ;
   }
 
   type 'a tag = string
@@ -393,6 +433,7 @@ struct
     vindex = Hashtbl.create 0 ;
     syntax = [] ;
     tags = [] ;
+    lookup = None ;
   }
 
   let tag ~name ?label ~descr ?value (d : 'a dictionary) : 'a tag =
@@ -411,8 +452,12 @@ struct
       | Some v -> Hashtbl.add d.vindex v name
     end ; name
 
-  let find_tag (d : 'a dictionary) name =
-    if Hashtbl.mem d.values name then name else raise Not_found
+  let find (d : 'a dictionary) name : 'a tag =
+    if Hashtbl.mem d.values name then name else
+      raise Not_found
+
+  let set_lookup (d : 'a dictionary) (tag : 'a -> 'a tag) =
+    d.lookup <- Some tag
 
   let instance_name = Printf.sprintf "%s:%s"
 
@@ -434,10 +479,16 @@ struct
         Package.update ~package ~name (D_enum (List.rev d.tags))
     ) ; name
 
-  let to_json name vindex v =
-    try `String (Hashtbl.find vindex v)
-    with Not_found ->
-      failure "[%s] Value not found" name
+  let to_json name lookup vindex v =
+    `String begin
+      try match lookup with
+        | None ->
+          Hashtbl.find vindex v
+        | Some f ->
+          try f v with Not_found -> Hashtbl.find vindex v
+      with Not_found ->
+        failure "[%s] Value not found" name
+    end
 
   let of_json name values js =
     let tag = Ju.to_string js in
@@ -450,7 +501,7 @@ struct
 
   let tags d = List.rev d.tags
 
-  let publish (type a) ~package ~name ~descr ?tag (d : a dictionary) =
+  let publish (type a) ~package ~name ~descr (d : a dictionary) =
     ( match d.published with
       | None -> ()
       | Some _ ->
@@ -463,10 +514,7 @@ struct
         let enums = D_enum (List.rev d.tags) in
         Jdata (Package.declare_id ~package ~name ~descr enums)
       let of_json = of_json name d.values
-      let to_json =
-        match tag with
-        | None -> to_json name d.vindex
-        | Some to_tag -> fun x -> `String (to_tag x)
+      let to_json = to_json name d.lookup d.vindex
     end in
     begin
       d.published <- Some (package,name) ;
diff --git a/src/plugins/server/data.mli b/src/plugins/server/data.mli
index 5bb3b13aff3..f63673c9a09 100644
--- a/src/plugins/server/data.mli
+++ b/src/plugins/server/data.mli
@@ -70,9 +70,6 @@ sig
   val to_json : t -> json
 end
 
-(** Polymorphic data value. *)
-type 'a data = (module S with type t = 'a)
-
 (** Of main kernel data. *)
 val package : package
 
@@ -113,6 +110,25 @@ 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 Functional API} *)
+(* -------------------------------------------------------------------------- *)
+
+(** Polymorphic data value. *)
+type 'a data = (module S with type t = 'a)
+
+val junit : unit data
+val jany : json data
+val jbool : bool data
+val jint : int data
+val jfloat : float data
+val jstring : string data
+val jindex : kind:string -> int data
+val jkey : kind:string -> string data
+val jlist : 'a data -> 'a list data
+val jarray : 'a data -> 'a array data
+val joption : 'a data -> 'a option data
+
 (* -------------------------------------------------------------------------- *)
 (** {2 Records} *)
 (* -------------------------------------------------------------------------- *)
@@ -229,7 +245,7 @@ sig
 
   (** Returns the tag from its name.
       @raise Not_found if no tag has been registered with this name. *)
-  val find_tag: 'a dictionary -> string -> 'a tag
+  val find: 'a dictionary -> string -> 'a tag
 
   (** Register a new prefix tag in the dictionary.
       The default label is the capitalized prefix.
@@ -258,12 +274,17 @@ sig
   (** Obtain all the tags registered in the dictionary so far. *)
   val tags : 'a dictionary -> Tag.t list
 
+  (** Set tagging function for values. If the lookup function
+      raises `Not_found`, the dictionary will use the tag associated
+      with the provided value, if any. *)
+  val set_lookup : 'a dictionary -> ('a -> 'a tag) -> unit
+
   (**
      Publish the dictionary. No more tag nor prefix can be added afterwards.
      If no [~tag] function is provided, the values registered with tags are used.
   *)
   val publish : package:package -> name:string -> descr:Markdown.text ->
-    ?tag:('a -> 'a tag) -> 'a dictionary -> (module S with type t = 'a)
+    'a dictionary -> (module S with type t = 'a)
 
 end
 
diff --git a/src/plugins/server/kernel_main.ml b/src/plugins/server/kernel_main.ml
index 3fe87efe0b4..ad3e9fd94b8 100644
--- a/src/plugins/server/kernel_main.ml
+++ b/src/plugins/server/kernel_main.ml
@@ -129,13 +129,15 @@ module LogKind = Collection
       let t_failure = t_kind Log.Failure "FAILURE" "Plugin Failure"
       let t_debug = t_kind Log.Debug "DEBUG" "Analyser Debug"
 
-      let tag = function
-        | Log.Error -> t_error
-        | Log.Warning -> t_warning
-        | Log.Feedback -> t_feedback
-        | Log.Result -> t_result
-        | Log.Failure -> t_failure
-        | Log.Debug -> t_debug
+      let () = Enum.set_lookup kinds
+          begin function
+            | Log.Error -> t_error
+            | Log.Warning -> t_warning
+            | Log.Feedback -> t_feedback
+            | Log.Result -> t_result
+            | Log.Failure -> t_failure
+            | Log.Debug -> t_debug
+          end
 
       let data = Request.dictionary ~package
           ~name:"logkind"
-- 
GitLab