From d8354963e2cd306ab0c5ab7142619783921eec2d Mon Sep 17 00:00:00 2001
From: Michele Alberti <michele.alberti@cea.fr>
Date: Mon, 27 Apr 2020 11:19:19 +0200
Subject: [PATCH] [frama-c/server] Rework requests.

---
 ivette/src/frama-c/server.ts    |  49 +++++++++++--
 ivette/src/frama-c/states.ts    | 121 +++++++++++++++++++-------------
 ivette/src/renderer/ASTview.tsx |  27 ++++---
 3 files changed, 133 insertions(+), 64 deletions(-)

diff --git a/ivette/src/frama-c/server.ts b/ivette/src/frama-c/server.ts
index 84bf3f2d20d..65d770957fd 100644
--- a/ivette/src/frama-c/server.ts
+++ b/ivette/src/frama-c/server.ts
@@ -651,14 +651,49 @@ Dome.on(SHUTDOWN, () => {
  *   - `SET` Used to write data into the server
  *   - `EXEC` Used to make the server execute a task
  */
-export enum RqKind {
+enum RqKind {
   GET = 'GET',
   SET = 'SET',
   EXEC = 'EXEC'
 }
 
 /**
- *  @summary Send request to the server.
+ * @typedef ServerRequest
+ * @summary Server request.
+ * @param {string} endpoint - the request identifier
+ * @param {any} params - the request parameters
+ */
+export interface ServerRequest {
+  endpoint: string;
+  params: any;
+}
+
+/**
+ * @summary Get data from the server.
+ * @param sr - the server request description.
+ */
+async function GET(sr: ServerRequest) {
+  return _create(RqKind.GET, sr.endpoint, sr.params);
+}
+
+/**
+ * @summary Set data into the server.
+ * @param sr - the server request description.
+ */
+async function SET(sr: ServerRequest) {
+  return _create(RqKind.SET, sr.endpoint, sr.params);
+}
+
+/**
+ * @summary Make the server execute a task.
+ * @param sr - the server request description.
+ */
+async function EXEC(sr: ServerRequest) {
+  return _create(RqKind.EXEC, sr.endpoint, sr.params);
+}
+
+/**
+ *  @summary Create request to send to the server.
  *  @param {RqKind} kind - the request kind
  *  @param {string} rq - the request identifier
  *  @param {object} params - request parameters
@@ -667,9 +702,9 @@ export enum RqKind {
  *  You may _kill_ the request before its normal termination by
  *  invoking `kill()` on the returned promised.
  */
-function send(kind: RqKind, rq: string, params: any) {
-  if (!isRunning()) return Promise.reject('Server not running');
-  if (!rq) return Promise.reject('Undefined request');
+function _create(kind: RqKind, rq: string, params: any) {
+  if (!isRunning()) return Promise.reject(new Error('Server not running'));
+  if (!rq) return Promise.reject(new Error('Undefined request'));
   const rid = `RQ.${rqid}`;
   rqid += 1;
   const data = JSON.stringify(params);
@@ -830,7 +865,9 @@ export default {
   kill,
   restart,
   clear,
-  send,
+  GET,
+  SET,
+  EXEC,
   onReady,
   onShutdown,
   onActivity,
diff --git a/ivette/src/frama-c/states.ts b/ivette/src/frama-c/states.ts
index 9e63255e1b4..9c757e7fd7d 100644
--- a/ivette/src/frama-c/states.ts
+++ b/ivette/src/frama-c/states.ts
@@ -11,7 +11,7 @@
 import _ from 'lodash';
 import React from 'react';
 import Dome from 'dome';
-import Server, { RqKind } from './server';
+import Server, { ServerRequest } from './server';
 
 /**
  *  @event
@@ -40,12 +40,15 @@ let currentProject = '<None>';
 let states: any = {};
 const stateDefaults: any = {};
 
-Server.onReady(() => {
-  Server.send(RqKind.GET, 'kernel.project.getCurrent', null)
-    .then((current: { id: string }) => {
-      currentProject = current.id;
-      Dome.emit(PROJECT);
-    });
+Server.onReady(async () => {
+  const sr: ServerRequest = {
+    endpoint: 'kernel.project.getCurrent',
+    params: {},
+  };
+  const current: { id: string } = await Server.GET(sr);
+  currentProject = current.id;
+  Dome.emit(PROJECT);
+  return;
 });
 
 Server.onShutdown(() => {
@@ -74,11 +77,16 @@ export function useProject() {
  *  Make all states switching to their projectified value.
  *  Emits `PROJECT`.
  */
-export function setProject(project: string) {
+export async function setProject(project: string) {
   if (Server.isRunning()) {
-    Server.send(RqKind.SET, 'kernel.project.setCurrent', project);
+    const sr: ServerRequest = {
+      endpoint: 'kernel.project.setCurrent',
+      params: project,
+    };
+    await Server.SET(sr);
     currentProject = project;
     Dome.emit(PROJECT);
+    return;
   }
 }
 
@@ -153,22 +161,36 @@ export function useState(id: string) {
 export function useRequest(rq: string, params: any, options: any = {}) {
   const project = useProject();
   const [value, setValue] = React.useState(options.offline);
+
   React.useEffect(() => {
     if (project) {
       const pending = options.prending;
-      if (pending !== null) setValue(pending);
-      Server.send(RqKind.GET, rq, params)
-        .then(setValue)
-        .catch((err: string) => {
-          if (Dome.DEVEL) console.warn(`[Server] use request '${rq}':`, err);
+      if (pending !== null) {
+        setValue(pending);
+      }
+      (async () => {
+        try {
+          const sr: ServerRequest = { endpoint: rq, params };
+          const v = await Server.GET(sr);
+          setValue(v);
+        } catch (err) {
+          if (Dome.DEVEL) {
+            console.warn(`[Server] use request '${rq}':`, err);
+          }
           const { error } = options;
-          if (error !== null) setValue(error);
-        });
+          if (error !== null) {
+            setValue(error);
+          }
+        }
+      })();
     } else {
       const v = options.offline;
-      if (value !== v) setValue(v);
+      if (value !== v) {
+        setValue(v);
+      }
     }
   }, [project, rq, JSON.stringify(params)]);
+
   return value;
 }
 
@@ -257,19 +279,20 @@ class SyncState {
     return this.value;
   }
 
-  setValue(v: any) {
+  async setValue(v: any) {
     this.insync = true;
     this.value = v;
-    Server.send(RqKind.SET, this.setRq, v);
+    const sr: ServerRequest = { endpoint: this.setRq, params: v };
+    await Server.SET(sr);
     Dome.emit(this.UPDATE);
   }
 
-  update() {
+  async update() {
     this.insync = true;
-    Server.send(RqKind.GET, this.getRq, null).then((v: any) => {
-      this.value = v;
-      Dome.emit(this.UPDATE);
-    });
+    const sr: ServerRequest = { endpoint: this.getRq, params: {} };
+    const v = await Server.GET(sr);
+    this.value = v;
+    Dome.emit(this.UPDATE);
   }
 }
 
@@ -360,33 +383,35 @@ class SyncArray {
     return _.find(this.index, () => true) !== undefined;
   }
 
-  fetch() {
+  async fetch() {
     this.insync = true;
-    Server.send(RqKind.GET, this.fetchRq, 50)
-      .then(({ reload = false, removed = [], updated = [], pending = 0 }) => {
-        let reloaded = false;
-        if (reload) {
-          reloaded = this.isEmpty();
-          this.index = {};
-        }
-        removed.forEach((key) => {
-          delete this.index[key];
-        });
-        updated.forEach((item: any) => {
-          this.index[item.key] = item;
-        });
-        if (reloaded || removed.length || updated.length) {
-          this.index = { ...this.index };
-          Dome.emit(this.UPDATE);
-        }
-        if (pending > 0) {
-          this.fetch();
-        }
-      });
+    const sr: ServerRequest = { endpoint: this.fetchRq, params: 50 };
+    const data = await Server.GET(sr);
+    const { reload = false, removed = [], updated = [], pending = 0 } = data;
+    let reloaded = false;
+    if (reload) {
+      reloaded = this.isEmpty();
+      this.index = {};
+    }
+    removed.forEach((key: any) => {
+      delete this.index[key];
+    });
+    updated.forEach((item: any) => {
+      this.index[item.key] = item;
+    });
+    if (reloaded || removed.length || updated.length) {
+      this.index = { ...this.index };
+      Dome.emit(this.UPDATE);
+    }
+    if (pending > 0) {
+      this.fetch();
+    }
+    return;
   }
 
-  reload() {
-    Server.send(RqKind.SET, this.reloadRq, null);
+  async reload() {
+    const sr: ServerRequest = { endpoint: this.reloadRq, params: {} };
+    await Server.SET(sr);
     this.index = {};
     this.insync = false;
     Dome.emit(this.UPDATE);
diff --git a/ivette/src/renderer/ASTview.tsx b/ivette/src/renderer/ASTview.tsx
index 1fd9d1befb4..6cf68490ac3 100644
--- a/ivette/src/renderer/ASTview.tsx
+++ b/ivette/src/renderer/ASTview.tsx
@@ -3,7 +3,7 @@
 // --------------------------------------------------------------------------
 
 import React from 'react';
-import Server, { RqKind } from 'frama-c/server';
+import Server, { ServerRequest } from 'frama-c/server';
 import States from 'frama-c/states';
 
 import { Vfill } from 'dome/layout/boxes';
@@ -49,15 +49,22 @@ const ASTview = () => {
     buffer.clear();
     if (theFunction) {
       buffer.log('// Loading', theFunction, '…');
-      Server.send(RqKind.GET, 'kernel.ast.printFunction', theFunction)
-        .then((data: string) => {
-          buffer.clear();
-          if (!data) {
-            buffer.log('// No code for function ', theFunction);
-          }
-          print(buffer, data);
-          if (theMarker) buffer.scroll(theMarker, undefined);
-        });
+      (async () => {
+        const sr: ServerRequest = {
+          endpoint: 'kernel.ast.printFunction',
+          params: theFunction,
+        };
+        const data = await Server.GET(sr);
+        buffer.clear();
+        if (!data) {
+          buffer.log('// No code for function ', theFunction);
+        }
+        print(buffer, data);
+        if (theMarker) {
+          buffer.scroll(theMarker, undefined);
+        }
+        return;
+      })();
     }
   }, [theFunction]);
 
-- 
GitLab